From de099b2d8b6c59ad297ffa372ca684ca77a5adec Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 19 Nov 2014 06:14:56 +0100 Subject: [PATCH 01/25] remove network pause. fix client repeat-disconnect loop. removed resolver from getifaddr dance (not reqd) and updated getifaddr code (needs windowz/linux testing). don't accept new connections if listenport bind fails (possible due to OS firewall settings). only use upnp w/ipv4 addresses. re: disconnect loop. Looks like ethhost session isn't deallocating once stopped. Thinking UI might have shared_ptr. For now, letting dealloc/quit continue as long as isOpen returns false -- maybe okay since network will be halted. Also, it maybe possible the issue is due to network thread not running after disconnect so packets aren't tx/rx to kill peer -- either way, it's possible remote end doesn't ack disconnect and timeout/force-close-dealloc is required, so will need more attention later. --- libdevcore/Worker.cpp | 3 +- libdevcore/Worker.h | 18 +- libethereum/EthereumHost.h | 1 + libp2p/Host.cpp | 376 +++++++++++++++++++++---------------- libp2p/Host.h | 42 +++-- 5 files changed, 256 insertions(+), 184 deletions(-) diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp index b2660305a..d9246f9fd 100644 --- a/libdevcore/Worker.cpp +++ b/libdevcore/Worker.cpp @@ -41,7 +41,8 @@ void Worker::startWorking() startedWorking(); while (!m_stop) { - this_thread::sleep_for(chrono::milliseconds(30)); + if (m_idlewaitms) + this_thread::sleep_for(chrono::milliseconds(m_idlewaitms)); doWork(); } cdebug << "Finishing up worker thread"; diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h index a4a998dd7..f8d694681 100644 --- a/libdevcore/Worker.h +++ b/libdevcore/Worker.h @@ -31,7 +31,7 @@ namespace dev class Worker { protected: - Worker(std::string const& _name = "anon"): m_name(_name) {} + Worker(std::string const& _name = "anon", unsigned _idlewaitms = 30): m_name(_name), m_idlewaitms(_idlewaitms) {} /// Move-constructor. Worker(Worker&& _m) { std::swap(m_name, _m.m_name); } @@ -41,20 +41,34 @@ protected: virtual ~Worker() { stopWorking(); } + /// Allows changing worker name if work is stopped. void setName(std::string _n) { if (!isWorking()) m_name = _n; } + /// Starts worker thread; causes startedWorking() to be called. void startWorking(); + + /// Stop worker thread; causes call to stopWorking(). void stopWorking(); + + /// Returns if worker thread is present. bool isWorking() const { Guard l(x_work); return !!m_work; } + + /// Called after thread is started from startWorking(). virtual void startedWorking() {} + + /// Called continuously following sleep for m_idlewaitms. virtual void doWork() = 0; + + /// Called when is to be stopped, just prior to thread being joined. virtual void doneWorking() {} private: + std::string m_name; + unsigned m_idlewaitms; + mutable Mutex x_work; ///< Lock for the network existance. std::unique_ptr m_work; ///< The network thread. bool m_stop = false; - std::string m_name; }; } diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 18ba765aa..c732f2dc1 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -51,6 +51,7 @@ class BlockQueue; /** * @brief The EthereumHost class * @warning None of this is thread-safe. You have been warned. + * @doWork Syncs to peers and sends new blocks and transactions. */ class EthereumHost: public p2p::HostCapability, Worker { diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 996e219db..4c6973ae3 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -46,19 +46,17 @@ using namespace std; using namespace dev; using namespace dev::p2p; -// Addresses we will skip during network interface discovery -// Use a vector as the list is small -// Why this and not names? -// Under MacOSX loopback (127.0.0.1) can be named lo0 and br0 are bridges (0.0.0.0) +// Addresses skipped during network interface discovery +// @todo: filter out ivp6 link-local network mess on macos, ex: fe80::1%lo0 static const set c_rejectAddresses = { {bi::address_v4::from_string("127.0.0.1")}, - {bi::address_v6::from_string("::1")}, {bi::address_v4::from_string("0.0.0.0")}, + {bi::address_v6::from_string("::1")}, {bi::address_v6::from_string("::")} }; Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool _start): - Worker("p2p"), + Worker("p2p", 0), m_clientVersion(_clientVersion), m_netPrefs(_n), m_ioService(new ba::io_service), @@ -79,70 +77,17 @@ Host::~Host() void Host::start() { - // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (!m_ioService) - return; - - if (isWorking()) - stop(); - - for (unsigned i = 0; i < 2; ++i) - { - bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : m_netPrefs.listenPort); - try - { - m_acceptor->open(endpoint.protocol()); - m_acceptor->set_option(ba::socket_base::reuse_address(true)); - m_acceptor->bind(endpoint); - m_acceptor->listen(); - m_listenPort = i ? m_acceptor->local_endpoint().port() : m_netPrefs.listenPort; - break; - } - catch (...) - { - if (i) - { - cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); - return; - } - m_acceptor->close(); - continue; - } - } - - for (auto const& h: m_capabilities) - h.second->onStarting(); - startWorking(); } void Host::stop() { - // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (!m_ioService) - return; - - for (auto const& h: m_capabilities) - h.second->onStopping(); - + // flag transition to shutdown network + // once m_run is false the scheduler will shutdown network and stopWorking() + m_run = false; + while (m_timer) + this_thread::sleep_for(chrono::milliseconds(100)); stopWorking(); - - if (m_acceptor->is_open()) - { - if (m_accepting) - m_acceptor->cancel(); - m_acceptor->close(); - m_accepting = false; - } - if (m_socket->is_open()) - m_socket->close(); - disconnectPeers(); - - if (!!m_ioService) - { - m_ioService->stop(); - m_ioService->reset(); - } } void Host::quit() @@ -150,7 +95,8 @@ void Host::quit() // called to force io_service to kill any remaining tasks it might have - // such tasks may involve socket reads from Capabilities that maintain references // to resources we're about to free. - stop(); + if (isWorking()) + stop(); m_acceptor.reset(); m_socket.reset(); m_ioService.reset(); @@ -183,33 +129,6 @@ void Host::registerPeer(std::shared_ptr _s, CapDescs const& _caps) } } -void Host::disconnectPeers() -{ - // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (!m_ioService) - return; - - for (unsigned n = 0;; n = 0) - { - { - RecursiveGuard l(x_peers); - for (auto i: m_peers) - if (auto p = i.second.lock()) - { - p->disconnect(ClientQuit); - n++; - } - } - if (!n) - break; - m_ioService->poll(); - this_thread::sleep_for(chrono::milliseconds(100)); - } - - delete m_upnp; - m_upnp = nullptr; -} - void Host::seal(bytes& _b) { _b[0] = 0x22; @@ -225,10 +144,6 @@ void Host::seal(bytes& _b) void Host::determinePublic(string const& _publicAddress, bool _upnp) { - // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (!m_ioService) - return; - if (_upnp) try { @@ -236,11 +151,12 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) } catch (NoUPnPDevice) {} // let m_upnp continue as null - we handle it properly. - bi::tcp::resolver r(*m_ioService); if (m_upnp && m_upnp->isValid() && m_peerAddresses.size()) { clog(NetNote) << "External addr:" << m_upnp->externalIP(); int p; + + // iterate m_peerAddresses (populated by populateAddresses()) for (auto const& addr : m_peerAddresses) if ((p = m_upnp->addRedirect(addr.to_string().c_str(), m_listenPort))) break; @@ -258,7 +174,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p); else { - bi::address adr = adr = bi::address::from_string(eip); + bi::address adr = bi::address::from_string(eip); try { adr = bi::address::from_string(_publicAddress); @@ -271,7 +187,21 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) else { // No UPnP - fallback on given public address or, if empty, the assumed peer address. - bi::address adr = m_peerAddresses.size() ? m_peerAddresses[0] : bi::address(); + bi::address adr; // = m_peerAddresses.size() ? m_peerAddresses[0] : bi::address(); + if (m_peerAddresses.size()) + { + // prefer local ipv4 over local ipv6 + for (auto const& ip: m_peerAddresses) + if (ip.is_v4()) + { + adr = ip; + break; + } + + if (adr.is_unspecified()) + adr = m_peerAddresses[0]; + } + try { adr = bi::address::from_string(_publicAddress); @@ -328,57 +258,43 @@ void Host::populateAddresses() if (getifaddrs(&ifaddr) == -1) BOOST_THROW_EXCEPTION(NoNetworking()); - bi::tcp::resolver r(*m_ioService); - - for (ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) - { - if (!ifa->ifa_addr) + for (auto ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr || (strlen(ifa->ifa_name) > 2 && !strncmp(ifa->ifa_name, "lo0", 3))) { continue; + } + if (ifa->ifa_addr->sa_family == AF_INET) { - char host[NI_MAXHOST]; - if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) - continue; - try - { - auto it = r.resolve({host, "30303"}); - bi::tcp::endpoint ep = it->endpoint(); - bi::address ad = ep.address(); - m_addresses.push_back(ad.to_v4()); - bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); - if (!isLocal) - m_peerAddresses.push_back(ad.to_v4()); - clog(NetNote) << "Address: " << host << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); - } - catch (...) - { - clog(NetNote) << "Couldn't resolve: " << host; - } + in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr)); + if (std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), address) == c_rejectAddresses.end()) + m_peerAddresses.push_back(address); + + // Log IPv4 Address: + auto addr4 = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + char addressBuffer[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, addr4, addressBuffer, INET_ADDRSTRLEN); + printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); } else if (ifa->ifa_addr->sa_family == AF_INET6) { - char host[NI_MAXHOST]; - if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) - continue; - try - { - auto it = r.resolve({host, "30303"}); - bi::tcp::endpoint ep = it->endpoint(); - bi::address ad = ep.address(); - m_addresses.push_back(ad.to_v6()); - bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); - if (!isLocal) - m_peerAddresses.push_back(ad); - clog(NetNote) << "Address: " << host << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); - } - catch (...) - { - clog(NetNote) << "Couldn't resolve: " << host; - } + sockaddr_in6* sockaddr = ((struct sockaddr_in6 *)ifa->ifa_addr); + in6_addr addr = sockaddr->sin6_addr; + boost::asio::ip::address_v6::bytes_type bytes; + memcpy(&bytes[0], addr.s6_addr, 16); + boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id); + if (std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), address) == c_rejectAddresses.end()) + m_peerAddresses.push_back(address); + + // Log IPv6 Address: + auto addr6 = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + char addressBuffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, addr6, addressBuffer, INET6_ADDRSTRLEN); + printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); } } + if (ifaddr!=NULL) freeifaddrs(ifaddr); - freeifaddrs(ifaddr); #endif } @@ -461,8 +377,8 @@ Nodes Host::potentialPeers(RangeMask const& _known) void Host::ensureAccepting() { - // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (!m_ioService) + // return if there's no io-server (quit called) or we're not listening + if (!m_ioService || m_listenPort < 1) return; if (!m_accepting) @@ -654,12 +570,9 @@ void Host::growPeers() return; } else - { - ensureAccepting(); for (auto const& i: m_peers) if (auto p = i.second.lock()) p->ensureNodesRequested(); - } } } @@ -717,36 +630,150 @@ PeerInfos Host::peers(bool _updatePing) const ret.push_back(j->m_info); return ret; } - -void Host::startedWorking() -{ - determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); - ensureAccepting(); - - if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id())) - noteNode(id(), m_public, Origin::Perfect, false); - - clog(NetNote) << "Id:" << id().abridged(); -} - -void Host::doWork() + +void Host::run(boost::system::error_code const& error) { - // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (asserts(!!m_ioService)) + static unsigned s_lasttick = 0; + s_lasttick += c_timerInterval; + + if (error) + // tood: error handling. + { + m_timer.reset(); return; + } + + // no timer means this is first run and network must be started + if (!m_timer) + // run once when host worker thread calls startedWorking() + { + // reset io service and create deadline timer + m_ioService->reset(); + m_timer.reset(new boost::asio::deadline_timer(*m_ioService)); + m_run = true; + + // try to open acceptor (ipv4; todo: update for ipv6) + for (unsigned i = 0; i < 2; ++i) + { + // try to connect w/listenPort, else attempt net-allocated port + bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : m_netPrefs.listenPort); + try + { + m_acceptor->open(endpoint.protocol()); + m_acceptor->set_option(ba::socket_base::reuse_address(true)); + m_acceptor->bind(endpoint); + m_acceptor->listen(); + m_listenPort = i ? m_acceptor->local_endpoint().port() : m_netPrefs.listenPort; + break; + } + catch (...) + { + if (i) + { + // both attempts failed + cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); + m_listenPort = -1; + } + + // first attempt failed + m_acceptor->close(); + continue; + } + } + + // start capability threads + for (auto const& h: m_capabilities) + h.second->onStarting(); + + // determine public IP, but only if we're able to listen for connections + // todo: visualize when listen is unavailable in UI + // tood: only punch hole for ipv4 + if (m_listenPort) + { + determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); + ensureAccepting(); + } + + // 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); + + clog(NetNote) << "Id:" << id().abridged(); + } + + // io service went away, so stop here + if (!m_ioService) + { + m_timer.reset(); + return; + } - growPeers(); - prunePeers(); - + // network stopped; disconnect peers + if (!m_run) + { + // close acceptor + if (m_acceptor->is_open()) + { + if (m_accepting) + m_acceptor->cancel(); + m_acceptor->close(); + m_accepting = false; + } + + // stop capabilities (eth: stops syncing or block/tx broadcast) + for (auto const& h: m_capabilities) + h.second->onStopping(); + + // disconnect peers + for (unsigned n = 0;; n = 0) + { + { + RecursiveGuard l(x_peers); + for (auto i: m_peers) + if (auto p = i.second.lock()) + if (p->isOpen()) + { + p->disconnect(ClientQuit); + n++; + } + } + if (!n) + break; + this_thread::sleep_for(chrono::milliseconds(100)); + } + + if (m_socket->is_open()) + m_socket->close(); + + if (m_upnp != nullptr) + delete m_upnp; + + // m_run is false, so we're stopping; kill timer + s_lasttick = 0; + m_timer.reset(); + if (!!m_ioService) + m_ioService->stop(); + return; + } + + + if (s_lasttick == c_timerInterval * 100) + { + growPeers(); + prunePeers(); + s_lasttick = 0; + } + if (m_hadNewNodes) { for (auto p: m_peers) if (auto pp = p.second.lock()) pp->serviceNodesRequest(); - + m_hadNewNodes = false; } - + if (chrono::steady_clock::now() - m_lastPing > chrono::seconds(30)) // ping every 30s. { for (auto p: m_peers) @@ -756,7 +783,22 @@ void Host::doWork() pingAll(); } - m_ioService->poll(); + auto runcb = [this](boost::system::error_code const& error)->void{ run(error); }; + m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval)); + m_timer->async_wait(runcb); +} + +void Host::startedWorking() +{ + run(boost::system::error_code()); +} + +void Host::doWork() +{ + // no ioService means we've had quit() called - bomb out - we're not allowed in here. + if (asserts(!!m_ioService)) + return; + m_ioService->run(); } void Host::pingAll() diff --git a/libp2p/Host.h b/libp2p/Host.h index 7722905ab..7c4fbe1ce 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -110,7 +110,7 @@ struct NetworkPreferences bool upnp = true; bool localNetworking = false; }; - + /** * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. @@ -147,7 +147,7 @@ public: void connect(bi::tcp::endpoint const& _ep); void connect(std::shared_ptr const& _n); - /// @returns true iff we have the a peer of the given id. + /// @returns true if we have the a peer of the given id. bool havePeer(NodeId _id) const; /// Set ideal number of peers. @@ -175,10 +175,16 @@ public: void setNetworkPreferences(NetworkPreferences const& _p) { auto had = isStarted(); if (had) stop(); m_netPrefs = _p; if (had) start(); } + /// Start network. void start(); + + /// Stop network. void stop(); + + /// @returns if network is running bool isStarted() const { return isWorking(); } + /// Reset acceptor, socket, and IO service. Called by deallocator. Maybe called by implementation when ordered deallocation is required. void quit(); NodeId id() const { return m_key.pub(); } @@ -190,17 +196,23 @@ public: private: void seal(bytes& _b); void populateAddresses(); + + /// Try UPNP or listen to assumed address. Requires valid m_listenPort. void determinePublic(std::string const& _publicAddress, bool _upnp); + + void ensureAccepting(); void growPeers(); void prunePeers(); + /// Called by Worker. Not thread-safe; to be called only by worker. virtual void startedWorking(); + /// Called by startedWorking. Not thread-safe; to be called only be worker callback. + void run(boost::system::error_code const& error); ///< Run network. Called serially via ASIO deadline timer. Manages connection state transitions. + bool m_run = false; - /// Conduct I/O, polling, syncing, whatever. - /// Ideally all time-consuming I/O is done in a background thread or otherwise asynchronously, but you get this call every 100ms or so anyway. - /// This won't touch alter the blockchain. + /// Run network virtual void doWork(); std::shared_ptr noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId = NodeId()); @@ -208,18 +220,20 @@ private: std::string m_clientVersion; ///< Our version string. - NetworkPreferences m_netPrefs; ///< Network settings. + NetworkPreferences m_netPrefs; ///< Network settings. - static const int NetworkStopped = -1; ///< The value meaning we're not actually listening. - int m_listenPort = NetworkStopped; ///< What port are we listening on? + int m_listenPort = -1; ///< What port are we listening on. -1 means binding failed or acceptor hasn't been initialized. std::unique_ptr m_ioService; ///< IOService for network stuff. - std::unique_ptr m_acceptor; ///< Listening acceptor. - std::unique_ptr m_socket; ///< Listening socket. - - UPnP* m_upnp = nullptr; ///< UPnP helper. - bi::tcp::endpoint m_public; ///< Our public listening endpoint. - KeyPair m_key; ///< Our unique ID. + std::unique_ptr m_acceptor; ///< 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. + static const unsigned c_timerInterval = 100; ///< Interval which m_timer is run when network is connected. + + UPnP* m_upnp = nullptr; ///< UPnP helper. + bi::tcp::endpoint m_public; ///< Our public listening endpoint. + KeyPair m_key; ///< Our unique ID. bool m_hadNewNodes = false; From c479f23561d8b987e710cfcd09b6d3e1eb9f5a24 Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 19 Nov 2014 06:18:11 +0100 Subject: [PATCH 02/25] remove unused method. skip upnp for ipv6. --- libp2p/Host.cpp | 5 ++--- libp2p/Host.h | 4 ---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 4c6973ae3..e7c476441 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -158,7 +158,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) // iterate m_peerAddresses (populated by populateAddresses()) for (auto const& addr : m_peerAddresses) - if ((p = m_upnp->addRedirect(addr.to_string().c_str(), m_listenPort))) + if (addr.is_v4() && (p = m_upnp->addRedirect(addr.to_string().c_str(), m_listenPort))) break; if (p) clog(NetNote) << "Punched through NAT and mapped local port" << m_listenPort << "onto external port" << p << "."; @@ -187,7 +187,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) else { // No UPnP - fallback on given public address or, if empty, the assumed peer address. - bi::address adr; // = m_peerAddresses.size() ? m_peerAddresses[0] : bi::address(); + bi::address adr; if (m_peerAddresses.size()) { // prefer local ipv4 over local ipv6 @@ -687,7 +687,6 @@ void Host::run(boost::system::error_code const& error) // determine public IP, but only if we're able to listen for connections // todo: visualize when listen is unavailable in UI - // tood: only punch hole for ipv4 if (m_listenPort) { determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); diff --git a/libp2p/Host.h b/libp2p/Host.h index 7c4fbe1ce..b60949278 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -128,9 +128,6 @@ public: /// Will block on network process events. virtual ~Host(); - /// Closes all peers. - void disconnectPeers(); - /// Basic peer network protocol version. unsigned protocolVersion() const; @@ -200,7 +197,6 @@ private: /// Try UPNP or listen to assumed address. Requires valid m_listenPort. void determinePublic(std::string const& _publicAddress, bool _upnp); - void ensureAccepting(); void growPeers(); From 056e201e8e1beebfd494ea45a9b35cf65f08ed80 Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Wed, 19 Nov 2014 10:55:28 +0100 Subject: [PATCH 03/25] more precompiled contract tests Conflicts: libevm/VM.h --- libevm/VM.h | 2 +- test/stPreCompiledContractsFiller.json | 68 ++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/libevm/VM.h b/libevm/VM.h index 487c8cd1a..52149a9a1 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -71,7 +71,7 @@ public: template bytesConstRef go(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1); - void require(u256 _n) { if (m_stack.size() < _n) BOOST_THROW_EXCEPTION(StackTooSmall() << RequirementError((bigint)_n, (bigint)m_stack.size())); } + void require(u256 _n) { if (m_stack.size() < _n) BOOST_THROW_EXCEPTION(StackTooSmall() << RequirementError(int(_n), m_stack.size())); } void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } } u256 gas() const { return m_gas; } u256 curPC() const { return m_curPC; } diff --git a/test/stPreCompiledContractsFiller.json b/test/stPreCompiledContractsFiller.json index bb2b35756..9c65ad37b 100644 --- a/test/stPreCompiledContractsFiller.json +++ b/test/stPreCompiledContractsFiller.json @@ -33,6 +33,40 @@ } }, + "CallEcrecover0_completeReturnValue": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "20000000", + "nonce" : 0, + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MLOAD 128) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "365224", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "CallEcrecover0_gas500": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -305,6 +339,40 @@ } }, + "CallSha256_1_nonzeroValue": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "20000000", + "nonce" : 0, + "code" : "{ [[ 2 ]] (CALL 500 2 0x13 0 0 0 32) [[ 0 ]] (MLOAD 0)}", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "365224", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "CallSha256_2": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", From 7e8b65b6070acd8eab4453b907de7d8a4878b238 Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Mon, 17 Nov 2014 22:51:08 +0100 Subject: [PATCH 04/25] Changed name of some tests --- test/vmIOandFlowOperationsTestFiller.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/vmIOandFlowOperationsTestFiller.json b/test/vmIOandFlowOperationsTestFiller.json index e2ec1def1..dce594e1e 100644 --- a/test/vmIOandFlowOperationsTestFiller.json +++ b/test/vmIOandFlowOperationsTestFiller.json @@ -55,7 +55,7 @@ } }, - "dupAt51doesNotExistAnymore": { + "dupAt51becameMload": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -83,7 +83,7 @@ } }, - "swapAt52doesNotExistAnymore": { + "swapAt52becameMstore": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", From c7032ff55cc64239bf73356f97c27c11fbfcef00 Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Wed, 19 Nov 2014 10:03:26 +0100 Subject: [PATCH 05/25] new push32 test and renaming --- test/vmPushDupSwapTestFiller.json | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/test/vmPushDupSwapTestFiller.json b/test/vmPushDupSwapTestFiller.json index 3fc7e4a79..69d0254b7 100644 --- a/test/vmPushDupSwapTestFiller.json +++ b/test/vmPushDupSwapTestFiller.json @@ -924,7 +924,7 @@ } }, - "push32error": { + "push32AndSuicide": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -952,6 +952,35 @@ } }, + + "push32FillUpInputWithZerosAtTheEnd": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x7fff10112233445566778899aabbccddeeff00112233445566778899aabbccdd", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "dup1": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", From ff408ef6017783c9eddfe7d8b7609c8799511855 Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Wed, 19 Nov 2014 10:58:21 +0100 Subject: [PATCH 06/25] merge --- libevm/VM.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevm/VM.h b/libevm/VM.h index 52149a9a1..487c8cd1a 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -71,7 +71,7 @@ public: template bytesConstRef go(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1); - void require(u256 _n) { if (m_stack.size() < _n) BOOST_THROW_EXCEPTION(StackTooSmall() << RequirementError(int(_n), m_stack.size())); } + void require(u256 _n) { if (m_stack.size() < _n) BOOST_THROW_EXCEPTION(StackTooSmall() << RequirementError((bigint)_n, (bigint)m_stack.size())); } void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } } u256 gas() const { return m_gas; } u256 curPC() const { return m_curPC; } From 946f7d3d85d0bfda2d76d444452ce6eb429181f6 Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Wed, 19 Nov 2014 14:30:42 +0100 Subject: [PATCH 07/25] Add Log tests --- test/TestHelper.cpp | 12 ++++ test/TestHelper.h | 2 + test/vm.cpp | 49 ++++++++++++++++- test/vm.h | 2 + test/vmLogTestFiller.json | 113 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 test/vmLogTestFiller.json diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 1b13f9e82..a0e2d5723 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -287,6 +287,18 @@ void checkStorage(map _expectedStore, map _resultStore, } } +void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs) +{ + BOOST_REQUIRE_EQUAL(_resultLogs.size(), _expectedLogs.size()); + + for (size_t i = 0; i < _resultLogs.size(); ++i) + { + BOOST_CHECK(_resultLogs[i].address == _expectedLogs[i].address); + BOOST_CHECK(_resultLogs[i].topics == _expectedLogs[i].topics); + BOOST_CHECK(_resultLogs[i].data == _expectedLogs[i].data); + } +} + std::string getTestPath() { string testPath; diff --git a/test/TestHelper.h b/test/TestHelper.h index ef67d52fb..7a2b8da51 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -25,6 +25,7 @@ #include #include "JsonSpiritHeaders.h" #include +#include namespace dev { @@ -69,6 +70,7 @@ bytes importCode(json_spirit::mObject& _o); bytes importData(json_spirit::mObject& _o); void checkOutput(bytes const& _output, json_spirit::mObject& _o); void checkStorage(std::map _expectedStore, std::map _resultStore, Address _expectedAddr); +void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs); void executeTests(const std::string& _name, const std::string& _testPathAppendix, std::function doTests); std::string getTestPath(); void userDefinedTest(std::string testTypeFlag, std::function doTests); diff --git a/test/vm.cpp b/test/vm.cpp index cacbf94cc..2503f89d1 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -120,6 +120,43 @@ void FakeExtVM::importEnv(mObject& _o) currentBlock.coinbaseAddress = Address(_o["currentCoinbase"].get_str()); } +mObject FakeExtVM::exportLog() +{ + mObject ret; + for (LogEntry const& l: sub.logs) + { + mObject o; + o["address"] = toString(l.address); + mArray topics; + for (auto const& t: l.topics) + topics.push_back(toString(t)); + o["topics"] = topics; + o["data"] = "0x" + toHex(l.data); + ret[toString(l.bloom())] = o; + } + return ret; +} + +void FakeExtVM::importLog(mObject& _o) +{ + for (auto const& l: _o) + { + mObject o = l.second.get_obj(); + // cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest) + assert(o.count("address") > 0); + assert(o.count("topics") > 0); + assert(o.count("data") > 0); + LogEntry log; + log.address = Address(o["address"].get_str()); + for (auto const& t: o["topics"].get_array()) + { + log.topics.insert(h256(t.get_str())); + } + log.data = importData(o); + sub.logs.push_back(log); + } +} + mObject FakeExtVM::exportState() { mObject ret; @@ -302,7 +339,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) u256 gas; try { - output = vm.go(fev, fev.simpleTrace()).toVector(); + output = vm.go(fev, fev.simpleTrace()).toBytes(); gas = vm.gas(); } catch (VMException const& _e) @@ -346,6 +383,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) o["callcreates"] = fev.exportCallCreates(); o["out"] = "0x" + toHex(output); fev.push(o, "gas", gas); + o["logs"] = mValue(fev.exportLog()); } else { @@ -353,10 +391,12 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) BOOST_REQUIRE(o.count("callcreates") > 0); BOOST_REQUIRE(o.count("out") > 0); BOOST_REQUIRE(o.count("gas") > 0); + BOOST_REQUIRE(o.count("logs") > 0); dev::test::FakeExtVM test; test.importState(o["post"].get_obj()); test.importCallCreates(o["callcreates"].get_array()); + test.importLog(o["logs"].get_obj()); checkOutput(output, o); @@ -384,6 +424,8 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) checkAddresses, bytes> > >(test.addresses, fev.addresses); BOOST_CHECK(test.callcreates == fev.callcreates); + + checkLog(fev.sub.logs, test.sub.logs); } } } @@ -432,6 +474,11 @@ BOOST_AUTO_TEST_CASE(vmPushDupSwapTest) dev::test::executeTests("vmPushDupSwapTest", "/VMTests", dev::test::doVMTests); } +BOOST_AUTO_TEST_CASE(vmLogTest) +{ + dev::test::executeTests("vmLogTest", "/VMTests", dev::test::doVMTests); +} + BOOST_AUTO_TEST_CASE(vmRandom) { string testPath = getTestPath(); diff --git a/test/vm.h b/test/vm.h index a52a02e31..eb98aa0ab 100644 --- a/test/vm.h +++ b/test/vm.h @@ -66,6 +66,8 @@ public: u256 doPosts(); json_spirit::mObject exportEnv(); void importEnv(json_spirit::mObject& _o); + json_spirit::mObject exportLog(); + void importLog(json_spirit::mObject& _o); json_spirit::mObject exportState(); void importState(json_spirit::mObject& _object); json_spirit::mObject exportExec(); diff --git a/test/vmLogTestFiller.json b/test/vmLogTestFiller.json new file mode 100644 index 000000000..fbf206283 --- /dev/null +++ b/test/vmLogTestFiller.json @@ -0,0 +1,113 @@ +{ + "log0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG1 0 0 (CALLER)) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG2 0 0 (CALLER) (ADDRESS)) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log2_1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG2 0 0 (CALLER) 23) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + } +} From 1dff07b1b468a59bb95bf9d60102bd6681cf95a1 Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Wed, 19 Nov 2014 15:17:03 +0100 Subject: [PATCH 08/25] more log tests --- test/vmLogTestFiller.json | 1167 ++++++++++++++++++++++++++++++++++++- 1 file changed, 1160 insertions(+), 7 deletions(-) diff --git a/test/vmLogTestFiller.json b/test/vmLogTestFiller.json index fbf206283..f4fb48b52 100644 --- a/test/vmLogTestFiller.json +++ b/test/vmLogTestFiller.json @@ -1,5 +1,5 @@ { - "log0": { + "log0_emptyMem": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -27,7 +27,7 @@ } }, - "log1": { + "log0_nonEmptyMem": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -40,7 +40,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (LOG1 0 0 (CALLER)) }", + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG0 0 32) }", "storage": {} } }, @@ -55,7 +55,7 @@ } }, - "log2": { + "log0_nonEmptyMem_logMemSize1": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -68,7 +68,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (LOG2 0 0 (CALLER) (ADDRESS)) }", + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG0 0 1) }", "storage": {} } }, @@ -83,7 +83,1160 @@ } }, - "log2_1": { + + "log0_nonEmptyMem_logMemSize1_logMemStart31": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG0 31 1) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log0_logMemStartTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 1) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log0_logMemsizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG0 1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log0_logMemsizeZero": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG0 1 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log1_emptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG1 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log1_nonEmptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG1 0 32 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log1_nonEmptyMem_logMemSize1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 0 1 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "log1_nonEmptyMem_logMemSize1_logMemStart31": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 31 1 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log1_logMemStartTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 1 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log1_logMemsizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log1_logMemsizeZero": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 1 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log1_MaxTopic": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG1 0 32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log1_Caller": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE8 0 0xff) (LOG1 0 32 (CALLER)) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log2_emptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG2 0 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log2_nonEmptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG2 0 32 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log2_nonEmptyMem_logMemSize1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 0 1 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "log2_nonEmptyMem_logMemSize1_logMemStart31": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 31 1 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log2_logMemStartTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 1 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log2_logMemsizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log2_logMemsizeZero": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 1 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log2_MaxTopic": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG2 0 32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log2_Caller": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE8 0 0xff) (LOG2 0 32 0 (CALLER) ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log3_emptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG3 0 0 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log3_nonEmptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG3 0 32 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log3_nonEmptyMem_logMemSize1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 0 1 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "log3_nonEmptyMem_logMemSize1_logMemStart31": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 31 1 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log3_logMemStartTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 1 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log3_logMemsizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log3_logMemsizeZero": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 1 0 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log3_MaxTopic": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG3 0 32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log3_Caller": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE8 0 0xff) (LOG3 0 32 0 0 (CALLER) ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log3_PC": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE8 0 0xff) (LOG3 0 32 (PC) (PC) (PC) ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log4_emptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (LOG4 0 0 0 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log4_nonEmptyMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG4 0 32 0 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log4_nonEmptyMem_logMemSize1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 0 1 0 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + + "log4_nonEmptyMem_logMemSize1_logMemStart31": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 31 1 0 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log4_logMemStartTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 1 0 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log4_logMemsizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 1 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log4_logMemsizeZero": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 1 0 0 0 0 0) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log4_MaxTopic": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd) (LOG4 0 32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log4_Caller": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE8 0 0xff) (LOG3 0 32 0 0 0 (CALLER) ) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "log4_PC": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -96,7 +1249,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (LOG2 0 0 (CALLER) 23) }", + "code" : "{ (MSTORE8 0 0xff) (LOG3 0 32 (PC) (PC) (PC) (PC) ) }", "storage": {} } }, From b5210dd1c8d1c3229f5e28655f2ffec3306f2d16 Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Wed, 19 Nov 2014 18:39:53 +0100 Subject: [PATCH 09/25] add make money test --- test/stSpecialTestFiller.json | 41 +++++++++++++++++++++++++++++++++++ test/state.cpp | 5 +++++ 2 files changed, 46 insertions(+) create mode 100644 test/stSpecialTestFiller.json diff --git a/test/stSpecialTestFiller.json b/test/stSpecialTestFiller.json new file mode 100644 index 000000000..fcb1d74a6 --- /dev/null +++ b/test/stSpecialTestFiller.json @@ -0,0 +1,41 @@ +{ + "makeMoney" : { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) (CALL 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec 0xaaaaaaaaace5edbc8e2a8697c15331677e6ebf0b 23 0 0 0 0) }", + "storage": {} + }, + "aaaaaaaaace5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x600160015532600255", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "850", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + } +} diff --git a/test/state.cpp b/test/state.cpp index b5b238299..8ee7b2e96 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -122,6 +122,11 @@ BOOST_AUTO_TEST_CASE(stPreCompiledContracts) dev::test::executeTests("stPreCompiledContracts", "/StateTests", dev::test::doStateTests); } +BOOST_AUTO_TEST_CASE(stSpecialTest) +{ + dev::test::executeTests("stSpecialTest", "/StateTests", dev::test::doStateTests); +} + BOOST_AUTO_TEST_CASE(stCreateTest) { for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) From 1da0ca9aa6715dc6b3897978c619f06da7cddb2b Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Thu, 20 Nov 2014 20:41:35 +0100 Subject: [PATCH 10/25] Specify user defined test --- test/TestHelper.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 114399a49..4ce175892 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -310,12 +310,13 @@ void userDefinedTest(string testTypeFlag, std::function= boost::unit_test::framework::master_test_suite().argc) + if (i + 2 >= boost::unit_test::framework::master_test_suite().argc) { - cnote << "Missing filename\nUsage: testeth " << testTypeFlag << " \n"; + cnote << "Missing filename\nUsage: testeth " << testTypeFlag << " \n"; return; } string filename = boost::unit_test::framework::master_test_suite().argv[i + 1]; + string testname = boost::unit_test::framework::master_test_suite().argv[i + 2]; int currentVerbosity = g_logVerbosity; g_logVerbosity = 12; try @@ -325,7 +326,19 @@ void userDefinedTest(string testTypeFlag, std::function 0, "Contents of " + filename + " is empty. "); json_spirit::read_string(s, v); - doTests(v, false); + json_spirit::mObject oSingleTest; + + json_spirit::mObject::const_iterator pos = v.get_obj().find(testname); + if (pos == v.get_obj().end()) + { + cnote << "Could not find test: " << testname << " in " << filename << "\n"; + return; + } + else + oSingleTest[pos->first] = pos->second; + + json_spirit::mValue v_singleTest(oSingleTest); + doTests(v_singleTest, false); } catch (Exception const& _e) { From 648da92534e7983dbe0077b0fffe72e41f707245 Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Thu, 20 Nov 2014 22:21:08 +0100 Subject: [PATCH 11/25] style --- test/TestHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 4ce175892..d724eb395 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -310,7 +310,7 @@ void userDefinedTest(string testTypeFlag, std::function= boost::unit_test::framework::master_test_suite().argc) + if (boost::unit_test::framework::master_test_suite().argc <= i + 2) { cnote << "Missing filename\nUsage: testeth " << testTypeFlag << " \n"; return; From c74f5ae0e5c1e3d61bf8f44666e023f6eb47345a Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Fri, 21 Nov 2014 07:42:41 +0100 Subject: [PATCH 12/25] style --- test/TestHelper.cpp | 4 ++-- test/vm.cpp | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 66370ca3c..c41289329 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -293,8 +293,8 @@ void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs) for (size_t i = 0; i < _resultLogs.size(); ++i) { - BOOST_CHECK(_resultLogs[i].address == _expectedLogs[i].address); - BOOST_CHECK(_resultLogs[i].topics == _expectedLogs[i].topics); + BOOST_CHECK_EQUAL(_resultLogs[i].address, _expectedLogs[i].address); + BOOST_CHECK_EQUAL(_resultLogs[i].topics, _expectedLogs[i].topics); BOOST_CHECK(_resultLogs[i].data == _expectedLogs[i].data); } } diff --git a/test/vm.cpp b/test/vm.cpp index 5b3c010f5..edb983714 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -149,9 +149,7 @@ void FakeExtVM::importLog(mObject& _o) LogEntry log; log.address = Address(o["address"].get_str()); for (auto const& t: o["topics"].get_array()) - { log.topics.insert(h256(t.get_str())); - } log.data = importData(o); sub.logs.push_back(log); } From 381f2dfe64cadf954929073de0777dd16b248632 Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 22 Nov 2014 02:25:35 +0100 Subject: [PATCH 13/25] reduce timeout, coding standards --- libp2p/Host.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index e7c476441..41ac2e856 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -757,7 +757,7 @@ void Host::run(boost::system::error_code const& error) } - if (s_lasttick == c_timerInterval * 100) + if (s_lasttick == c_timerInterval * 10) { growPeers(); prunePeers(); @@ -782,7 +782,7 @@ void Host::run(boost::system::error_code const& error) pingAll(); } - auto runcb = [this](boost::system::error_code const& error)->void{ run(error); }; + auto runcb = [this](boost::system::error_code const& error) -> void{ run(error); }; m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval)); m_timer->async_wait(runcb); } From 24bbbefd5dee9c4d02093466ae6590053a4e8f94 Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 22 Nov 2014 08:48:02 +0100 Subject: [PATCH 14/25] pr fixes. refactor of network code. moved network-start methods into static methods, as they can only be called during certain stages or do not need Host class. Mutex start/stop. Added m_ifAddresses and removed m_addresses. Rewrite determinePublic. IP address preference is now: user-supplied public > user-supplied private (if localnetwork enabled) > first public4 interface > upnp4 > first private4 interface (if localnetwork enabled) > unspecified. IPv6 addresses are currently ignored (unless manually entered by user) until link-local addresses can be properly detected. --- libp2p/Host.cpp | 547 ++++++++++++++++++++++++--------------------- libp2p/Host.h | 48 ++-- libp2p/Session.cpp | 2 +- 3 files changed, 326 insertions(+), 271 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 41ac2e856..b51443e63 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -55,16 +55,167 @@ static const set c_rejectAddresses = { {bi::address_v6::from_string("::")} }; +std::vector Host::getInterfaceAddresses() +{ + std::vector addresses; + +#ifdef _WIN32 + WSAData wsaData; + if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) + BOOST_THROW_EXCEPTION(NoNetworking()); + + char ac[80]; + if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) + { + clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name."; + WSACleanup(); + BOOST_THROW_EXCEPTION(NoNetworking()); + } + + struct hostent* phe = gethostbyname(ac); + if (phe == 0) + { + clog(NetWarn) << "Bad host lookup."; + WSACleanup(); + BOOST_THROW_EXCEPTION(NoNetworking()); + } + + for (int i = 0; phe->h_addr_list[i] != 0; ++i) + { + struct in_addr addr; + memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); + char *addrStr = inet_ntoa(addr); + bi::address address(bi::address::from_string(addrStr)); + if (std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), address) == c_rejectAddresses.end()) + addresses.push_back(ad.to_v4()); + } + + WSACleanup(); +#else + ifaddrs* ifaddr; + if (getifaddrs(&ifaddr) == -1) + BOOST_THROW_EXCEPTION(NoNetworking()); + + for (auto ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) + { + if (!ifa->ifa_addr || string(ifa->ifa_name) == "lo0") + continue; + + if (ifa->ifa_addr->sa_family == AF_INET) + { + in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr)); + if (std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), address) == c_rejectAddresses.end()) + addresses.push_back(address); + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + sockaddr_in6* sockaddr = ((struct sockaddr_in6 *)ifa->ifa_addr); + in6_addr addr = sockaddr->sin6_addr; + boost::asio::ip::address_v6::bytes_type bytes; + memcpy(&bytes[0], addr.s6_addr, 16); + boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id); + if (std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), address) == c_rejectAddresses.end()) + addresses.push_back(address); + } + } + if (ifaddr!=NULL) freeifaddrs(ifaddr); + +#endif + + return std::move(addresses); +} + +int Host::listen4(bi::tcp::acceptor* _acceptor, unsigned short _listenPort) +{ + int retport = -1; + for (unsigned i = 0; i < 2; ++i) + { + // try to connect w/listenPort, else attempt net-allocated port + bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : _listenPort); + try + { + _acceptor->open(endpoint.protocol()); + _acceptor->set_option(ba::socket_base::reuse_address(true)); + _acceptor->bind(endpoint); + _acceptor->listen(); + retport = _acceptor->local_endpoint().port(); + break; + } + catch (...) + { + if (i) + { + // both attempts failed + cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); + _acceptor->close(); + } + + // first attempt failed + _acceptor->close(); + continue; + } + } + return retport; +} + +bi::tcp::endpoint Host::traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr) +{ + asserts(_listenPort); + + UPnP* upnp; + try + { + upnp = new UPnP; + } + // let m_upnp continue as null - we handle it properly. + catch (NoUPnPDevice) {} + + bi::tcp::endpoint upnpep; + if (upnp && upnp->isValid()) + { + bi::address paddr; + int p; + for (auto const& addr : _ifAddresses) + if (addr.is_v4() && isPrivateAddress(addr) && (p = upnp->addRedirect(addr.to_string().c_str(), _listenPort))) + { + paddr = addr; + break; + } + + auto eip = upnp->externalIP(); + bi::address eipaddr(bi::address::from_string(eip)); + if (p && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr)) + { + clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << p << "."; + clog(NetNote) << "External addr:" << eip; + o_upnpifaddr = paddr; + upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)p); + } + else + clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place)."; + + if(upnp) + delete upnp; + } + + return upnpep; +} + Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool _start): Worker("p2p", 0), m_clientVersion(_clientVersion), m_netPrefs(_n), + m_ifAddresses(getInterfaceAddresses()), m_ioService(new ba::io_service), m_acceptor(new bi::tcp::acceptor(*m_ioService)), m_socket(new bi::tcp::socket(*m_ioService)), m_key(KeyPair::create()) { - populateAddresses(); + for (auto address: m_ifAddresses) + if (address.is_v4()) + clog(NetNote) << "IP Address: " << address << " = " << (isPrivateAddress(address) ? "[LOCAL]" : "[PEER]"); + clog(NetNote) << "Id:" << id().abridged(); if (_start) start(); @@ -82,11 +233,16 @@ void Host::start() void Host::stop() { - // flag transition to shutdown network - // once m_run is false the scheduler will shutdown network and stopWorking() - m_run = false; + { + // prevent m_run from being set to false at same time as set to true by start() + lock_guard l(x_runtimer); + // once m_run is false the scheduler will shutdown network and stopWorking() + m_run = false; + } + + // we know shutdown is complete when m_timer is reset while (m_timer) - this_thread::sleep_for(chrono::milliseconds(100)); + this_thread::sleep_for(chrono::milliseconds(50)); stopWorking(); } @@ -142,162 +298,6 @@ void Host::seal(bytes& _b) _b[7] = len & 0xff; } -void Host::determinePublic(string const& _publicAddress, bool _upnp) -{ - if (_upnp) - try - { - m_upnp = new UPnP; - } - catch (NoUPnPDevice) {} // let m_upnp continue as null - we handle it properly. - - if (m_upnp && m_upnp->isValid() && m_peerAddresses.size()) - { - clog(NetNote) << "External addr:" << m_upnp->externalIP(); - int p; - - // iterate m_peerAddresses (populated by populateAddresses()) - for (auto const& addr : m_peerAddresses) - if (addr.is_v4() && (p = m_upnp->addRedirect(addr.to_string().c_str(), m_listenPort))) - break; - if (p) - clog(NetNote) << "Punched through NAT and mapped local port" << m_listenPort << "onto external port" << p << "."; - else - { - // couldn't map - clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place). Assuming" << m_listenPort << "is local & external port."; - p = m_listenPort; - } - - auto eip = m_upnp->externalIP(); - if (eip == string("0.0.0.0") && _publicAddress.empty()) - m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p); - else - { - bi::address adr = bi::address::from_string(eip); - try - { - adr = bi::address::from_string(_publicAddress); - } - catch (...) {} - m_public = bi::tcp::endpoint(adr, (unsigned short)p); - m_addresses.push_back(m_public.address()); - } - } - else - { - // No UPnP - fallback on given public address or, if empty, the assumed peer address. - bi::address adr; - if (m_peerAddresses.size()) - { - // prefer local ipv4 over local ipv6 - for (auto const& ip: m_peerAddresses) - if (ip.is_v4()) - { - adr = ip; - break; - } - - if (adr.is_unspecified()) - adr = m_peerAddresses[0]; - } - - try - { - adr = bi::address::from_string(_publicAddress); - } - catch (...) {} - m_public = bi::tcp::endpoint(adr, m_listenPort); - m_addresses.push_back(adr); - } -} - -void Host::populateAddresses() -{ - // if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here. - if (!m_ioService) - return; - -#ifdef _WIN32 - WSAData wsaData; - if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) - BOOST_THROW_EXCEPTION(NoNetworking()); - - char ac[80]; - if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) - { - clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name."; - WSACleanup(); - BOOST_THROW_EXCEPTION(NoNetworking()); - } - - struct hostent* phe = gethostbyname(ac); - if (phe == 0) - { - clog(NetWarn) << "Bad host lookup."; - WSACleanup(); - BOOST_THROW_EXCEPTION(NoNetworking()); - } - - for (int i = 0; phe->h_addr_list[i] != 0; ++i) - { - struct in_addr addr; - memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); - char *addrStr = inet_ntoa(addr); - bi::address ad(bi::address::from_string(addrStr)); - m_addresses.push_back(ad.to_v4()); - bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); - if (!isLocal) - m_peerAddresses.push_back(ad.to_v4()); - clog(NetNote) << "Address: " << ac << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); - } - - WSACleanup(); -#else - ifaddrs* ifaddr; - if (getifaddrs(&ifaddr) == -1) - BOOST_THROW_EXCEPTION(NoNetworking()); - - for (auto ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr || (strlen(ifa->ifa_name) > 2 && !strncmp(ifa->ifa_name, "lo0", 3))) { - continue; - } - - if (ifa->ifa_addr->sa_family == AF_INET) - { - in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; - boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr)); - if (std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), address) == c_rejectAddresses.end()) - m_peerAddresses.push_back(address); - - // Log IPv4 Address: - auto addr4 = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; - char addressBuffer[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, addr4, addressBuffer, INET_ADDRSTRLEN); - printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); - } - else if (ifa->ifa_addr->sa_family == AF_INET6) - { - sockaddr_in6* sockaddr = ((struct sockaddr_in6 *)ifa->ifa_addr); - in6_addr addr = sockaddr->sin6_addr; - boost::asio::ip::address_v6::bytes_type bytes; - memcpy(&bytes[0], addr.s6_addr, 16); - boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id); - if (std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), address) == c_rejectAddresses.end()) - m_peerAddresses.push_back(address); - - // Log IPv6 Address: - auto addr6 = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; - char addressBuffer[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, addr6, addressBuffer, INET6_ADDRSTRLEN); - printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); - } - } - if (ifaddr!=NULL) freeifaddrs(ifaddr); - -#endif -} - shared_ptr Host::noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId) { RecursiveGuard l(x_peers); @@ -375,6 +375,69 @@ Nodes Host::potentialPeers(RangeMask const& _known) return ret; } +void Host::determinePublic(string const& _publicAddress, bool _upnp) +{ + m_peerAddresses.clear(); + + // no point continuing if there are no interface addresses or valid listen port + if (!m_ifAddresses.size() || m_listenPort < 1) + return; + + // populate interfaces we'll listen on (eth listens on all interfaces); ignores local + for (auto addr: m_ifAddresses) + if ((m_netPrefs.localNetworking || !isPrivateAddress(addr)) && find(c_rejectAddresses.begin(), c_rejectAddresses.end(), addr) == c_rejectAddresses.end()) + m_peerAddresses.insert(addr); + + // 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 ? false : find(c_rejectAddresses.begin(), c_rejectAddresses.end(), reqpublicaddr) == c_rejectAddresses.end(); + if (!reqpublicaddr.is_unspecified() && (ispublic || (isprivate && m_netPrefs.localNetworking))) + { + if (!m_peerAddresses.count(reqpublicaddr)) + m_peerAddresses.insert(reqpublicaddr); + m_public = reqpublic; + return; + } + + // if address wasn't provided, then use first public ipv4 address found + for (auto addr: m_peerAddresses) + if (addr.is_v4() && !isPrivateAddress(addr)) + { + m_public = bi::tcp::endpoint(*m_peerAddresses.begin(), m_listenPort); + return; + } + + // or find address via upnp + if (_upnp) + { + bi::address upnpifaddr; + bi::tcp::endpoint upnpep = traverseNAT(m_ifAddresses, m_listenPort, upnpifaddr); + if (!upnpep.address().is_unspecified() && !upnpifaddr.is_unspecified()) + { + if (!m_peerAddresses.count(upnpep.address())) + m_peerAddresses.insert(upnpep.address()); + m_public = upnpep; + return; + } + } + + // or if no address provided, use private ipv4 address if local networking is enabled + if (reqpublicaddr.is_unspecified()) + if (m_netPrefs.localNetworking) + for (auto addr: m_peerAddresses) + if (addr.is_v4() && isPrivateAddress(addr)) + { + m_public = bi::tcp::endpoint(addr, m_listenPort); + return; + } + + // otherwise address is unspecified + m_public = bi::tcp::endpoint(bi::address(), m_listenPort); +} + void Host::ensureAccepting() { // return if there's no io-server (quit called) or we're not listening @@ -636,79 +699,49 @@ void Host::run(boost::system::error_code const& error) static unsigned s_lasttick = 0; s_lasttick += c_timerInterval; - if (error) - // tood: error handling. + if (error || !m_ioService) { + // timer died or io service went away, so stop here m_timer.reset(); return; } - - // no timer means this is first run and network must be started - if (!m_timer) - // run once when host worker thread calls startedWorking() + + // network running + if (m_run) { - // reset io service and create deadline timer - m_ioService->reset(); - m_timer.reset(new boost::asio::deadline_timer(*m_ioService)); - m_run = true; - - // try to open acceptor (ipv4; todo: update for ipv6) - for (unsigned i = 0; i < 2; ++i) + if (s_lasttick == c_timerInterval * 50) { - // try to connect w/listenPort, else attempt net-allocated port - bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : m_netPrefs.listenPort); - try - { - m_acceptor->open(endpoint.protocol()); - m_acceptor->set_option(ba::socket_base::reuse_address(true)); - m_acceptor->bind(endpoint); - m_acceptor->listen(); - m_listenPort = i ? m_acceptor->local_endpoint().port() : m_netPrefs.listenPort; - break; - } - catch (...) - { - if (i) - { - // both attempts failed - cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); - m_listenPort = -1; - } - - // first attempt failed - m_acceptor->close(); - continue; - } + growPeers(); + prunePeers(); + s_lasttick = 0; } - // start capability threads - for (auto const& h: m_capabilities) - h.second->onStarting(); + if (m_hadNewNodes) + { + for (auto p: m_peers) + if (auto pp = p.second.lock()) + pp->serviceNodesRequest(); + + m_hadNewNodes = false; + } - // determine public IP, but only if we're able to listen for connections - // todo: visualize when listen is unavailable in UI - if (m_listenPort) + if (chrono::steady_clock::now() - m_lastPing > chrono::seconds(30)) // ping every 30s. { - determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); - ensureAccepting(); + for (auto p: m_peers) + if (auto pp = p.second.lock()) + if (chrono::steady_clock::now() - pp->m_lastReceived > chrono::seconds(60)) + pp->disconnect(PingTimeout); + pingAll(); } - // 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); + auto runcb = [this](boost::system::error_code const& error) -> void{ run(error); }; + m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval)); + m_timer->async_wait(runcb); - clog(NetNote) << "Id:" << id().abridged(); - } - - // io service went away, so stop here - if (!m_ioService) - { - m_timer.reset(); return; } - - // network stopped; disconnect peers + + // network stopping if (!m_run) { // close acceptor @@ -745,50 +778,60 @@ void Host::run(boost::system::error_code const& error) if (m_socket->is_open()) m_socket->close(); - if (m_upnp != nullptr) - delete m_upnp; - // m_run is false, so we're stopping; kill timer s_lasttick = 0; + + // causes parent thread's stop() to continue which calls stopWorking() m_timer.reset(); + + // stop ioservice (stops blocking worker thread, allowing thread to join) if (!!m_ioService) m_ioService->stop(); return; } - - - if (s_lasttick == c_timerInterval * 10) - { - growPeers(); - prunePeers(); - s_lasttick = 0; - } - - if (m_hadNewNodes) - { - for (auto p: m_peers) - if (auto pp = p.second.lock()) - pp->serviceNodesRequest(); - - m_hadNewNodes = false; - } - - if (chrono::steady_clock::now() - m_lastPing > chrono::seconds(30)) // ping every 30s. - { - for (auto p: m_peers) - if (auto pp = p.second.lock()) - if (chrono::steady_clock::now() - pp->m_lastReceived > chrono::seconds(60)) - pp->disconnect(PingTimeout); - pingAll(); - } - - auto runcb = [this](boost::system::error_code const& error) -> void{ run(error); }; - m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval)); - m_timer->async_wait(runcb); } void Host::startedWorking() { + if (asserts(!m_timer)) + { + // no timer means this is first run and network must be started + // (run once when host worker thread calls startedWorking()) + + { + // prevent m_run from being set to true at same time as set to false by stop() + // don't release mutex until m_timer is set so in case stop() is called at same + // time, stop will wait on m_timer and graceful network shutdown. + lock_guard l(x_runtimer); + // reset io service and create deadline timer + m_timer.reset(new boost::asio::deadline_timer(*m_ioService)); + m_run = true; + } + m_ioService->reset(); + + // try to open acceptor (todo: ipv6) + m_listenPort = listen4(m_acceptor.get(), m_netPrefs.listenPort); + + // start capability threads + for (auto const& h: m_capabilities) + h.second->onStarting(); + + // determine public IP, but only if we're able to listen for connections + // todo: GUI when listen is unavailable in UI + if (m_listenPort) + { + determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); + ensureAccepting(); + } + + // 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); + + clog(NetNote) << "Id:" << id().abridged(); + } + run(boost::system::error_code()); } diff --git a/libp2p/Host.h b/libp2p/Host.h index b60949278..c619b6867 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -121,13 +121,24 @@ class Host: public Worker friend class HostCapabilityFace; friend struct Node; + /// Static network interface methods +public: + /// @returns public and private interface addresses + 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); + + /// 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); + public: /// Start server, listening for connections on the given port. Host(std::string const& _clientVersion, NetworkPreferences const& _n = NetworkPreferences(), bool _start = false); /// Will block on network process events. virtual ~Host(); - + /// Basic peer network protocol version. unsigned protocolVersion() const; @@ -144,7 +155,7 @@ public: void connect(bi::tcp::endpoint const& _ep); void connect(std::shared_ptr const& _n); - /// @returns true if we have the a peer of the given id. + /// @returns true iff we have a peer of the given id. bool havePeer(NodeId _id) const; /// Set ideal number of peers. @@ -172,14 +183,14 @@ public: void setNetworkPreferences(NetworkPreferences const& _p) { auto had = isStarted(); if (had) stop(); m_netPrefs = _p; if (had) start(); } - /// Start network. + /// Start network. @threadsafe void start(); - /// Stop network. + /// Stop network. @threadsafe void stop(); - /// @returns if network is running - bool isStarted() const { return isWorking(); } + /// @returns if network is running. + bool isStarted() const { return m_run; } /// Reset acceptor, socket, and IO service. Called by deallocator. Maybe called by implementation when ordered deallocation is required. void quit(); @@ -191,13 +202,12 @@ public: std::shared_ptr node(NodeId _id) const { if (m_nodes.count(_id)) return m_nodes.at(_id); return std::shared_ptr(); } private: - void seal(bytes& _b); - void populateAddresses(); - - /// Try UPNP or listen to assumed address. Requires valid m_listenPort. + /// Populate m_peerAddresses with available public addresses. void determinePublic(std::string const& _publicAddress, bool _upnp); void ensureAccepting(); + + void seal(bytes& _b); void growPeers(); void prunePeers(); @@ -206,7 +216,6 @@ private: virtual void startedWorking(); /// Called by startedWorking. Not thread-safe; to be called only be worker callback. void run(boost::system::error_code const& error); ///< Run network. Called serially via ASIO deadline timer. Manages connection state transitions. - bool m_run = false; /// Run network virtual void doWork(); @@ -214,9 +223,15 @@ private: std::shared_ptr noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId = NodeId()); Nodes potentialPeers(RangeMask const& _known); + bool m_run = false; ///< Whether network is running. + std::mutex x_runtimer; ///< Start/stop mutex. + std::string m_clientVersion; ///< Our version string. NetworkPreferences m_netPrefs; ///< Network settings. + + /// Interface addresses (private, public) + std::vector m_ifAddresses; ///< Interface addresses. int m_listenPort = -1; ///< What port are we listening on. -1 means binding failed or acceptor hasn't been initialized. @@ -227,7 +242,6 @@ private: std::unique_ptr m_timer; ///< Timer which, when network is running, calls scheduler() every c_timerInterval ms. static const unsigned c_timerInterval = 100; ///< Interval which m_timer is run when network is connected. - UPnP* m_upnp = nullptr; ///< UPnP helper. bi::tcp::endpoint m_public; ///< Our public listening endpoint. KeyPair m_key; ///< Our unique ID. @@ -247,13 +261,11 @@ private: std::vector m_nodesList; RangeMask m_ready; ///< Indices into m_nodesList over to which nodes we are not currently connected, connecting or otherwise ignoring. - RangeMask m_private; ///< Indices into m_nodesList over to which nodes are private. - - unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to. + RangeMask m_private; ///< Indices into m_nodesList over to which nodes are private. - // Our addresses. - std::vector m_addresses; ///< Addresses for us. - std::vector m_peerAddresses; ///< Addresses that peers (can) know us by. + unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to. + + std::set m_peerAddresses; ///< Public addresses that peers (can) know us by. // Our capabilities. std::map> m_capabilities; ///< Each of the capabilities we support. diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index c4385df5f..958473870 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -344,7 +344,7 @@ bool Session::interpret(RLP const& _r) // goto CONTINUE; // Wierd port. // Avoid our random other addresses that they might end up giving us. - for (auto i: m_server->m_addresses) + for (auto i: m_server->m_peerAddresses) if (ep.address() == i && ep.port() == m_server->listenPort()) goto CONTINUE; From fb7f8e782ea61781cf64e5595b6b79c6babebf59 Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 22 Nov 2014 08:56:16 +0100 Subject: [PATCH 15/25] fix --- libp2p/Host.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index b51443e63..471d1dc56 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -793,7 +793,7 @@ void Host::run(boost::system::error_code const& error) void Host::startedWorking() { - if (asserts(!m_timer)) + if (!m_timer) { // no timer means this is first run and network must be started // (run once when host worker thread calls startedWorking()) From 1bdf76922eb9a484d2bc695811de6025cca63bef Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 23 Nov 2014 06:51:18 +0100 Subject: [PATCH 16/25] remove spontaneous whitespace --- libp2p/Host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Host.h b/libp2p/Host.h index c619b6867..c82ecf84c 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -110,7 +110,7 @@ struct NetworkPreferences bool upnp = true; bool localNetworking = false; }; - + /** * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. From ad1a1f3ee2cb2aad3f1d594dce999ccdd7006aff Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 19 Nov 2014 18:56:10 +0100 Subject: [PATCH 17/25] We only care about member types. --- libsolidity/AST.cpp | 2 +- libsolidity/Types.cpp | 8 ++++---- libsolidity/Types.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index e8bdecf31..2972cb5c2 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -469,7 +469,7 @@ void MemberAccess::checkTypeRequirements() unsigned memberIndex = type.memberNameToIndex(*m_memberName); if (memberIndex >= type.getMemberCount()) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString())); - m_type = type.getMemberByIndex(memberIndex).getType(); + m_type = type.getMemberByIndex(memberIndex); m_isLvalue = true; } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index e24435976..79d4a1c17 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -234,7 +234,7 @@ u256 StructType::getStorageSize() const bool StructType::canLiveOutsideStorage() const { for (unsigned i = 0; i < getMemberCount(); ++i) - if (!getMemberByIndex(i).getType()->canLiveOutsideStorage()) + if (!getMemberByIndex(i)->canLiveOutsideStorage()) return false; return true; } @@ -258,9 +258,9 @@ unsigned StructType::memberNameToIndex(string const& _name) const return unsigned(-1); } -VariableDeclaration const& StructType::getMemberByIndex(unsigned _index) const +shared_ptr const& StructType::getMemberByIndex(unsigned _index) const { - return *m_struct.getMembers()[_index]; + return m_struct.getMembers()[_index].getType(); } u256 StructType::getStorageOffsetOfMember(unsigned _index) const @@ -269,7 +269,7 @@ u256 StructType::getStorageOffsetOfMember(unsigned _index) const u256 offset; // vector> const& members = m_struct.getMembers(); for (unsigned index = 0; index < _index; ++index) - offset += getMemberByIndex(index).getType()->getStorageSize(); + offset += getMemberByIndex(index)->getStorageSize(); return offset; } diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 726470172..b0d95d291 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -190,7 +190,7 @@ public: unsigned getMemberCount() const; /// Returns the index of the member with name @a _name or unsigned(-1) if it does not exist. unsigned memberNameToIndex(std::string const& _name) const; - VariableDeclaration const& getMemberByIndex(unsigned _index) const; + std::shared_ptr const& getMemberByIndex(unsigned _index) const; u256 getStorageOffsetOfMember(unsigned _index) const; private: From 6ee07c2cb8cff638cf80fad2974e97c0f7d8ddb0 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 20 Nov 2014 10:19:43 +0100 Subject: [PATCH 18/25] Convenience class for type members. --- libsolidity/AST.cpp | 10 ++---- libsolidity/ExpressionCompiler.cpp | 5 +-- libsolidity/Types.cpp | 49 +++++++++++++++--------------- libsolidity/Types.h | 47 +++++++++++++++++++++++++--- 4 files changed, 70 insertions(+), 41 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 2972cb5c2..9aecbdedf 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -462,14 +462,10 @@ void MemberAccess::checkTypeRequirements() { m_expression->checkTypeRequirements(); m_expression->requireLValue(); - if (m_expression->getType()->getCategory() != Type::Category::STRUCT) - BOOST_THROW_EXCEPTION(createTypeError("Member access to a non-struct (is " + - m_expression->getType()->toString() + ")")); - StructType const& type = dynamic_cast(*m_expression->getType()); - unsigned memberIndex = type.memberNameToIndex(*m_memberName); - if (memberIndex >= type.getMemberCount()) + Type const& type = *m_expression->getType(); + m_type = type.getMemberType(*m_memberName); + if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString())); - m_type = type.getMemberByIndex(memberIndex); m_isLvalue = true; } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index f37ce39ce..89eeb31f2 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -208,10 +208,7 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) if (asserts(m_currentLValue.isInStorage())) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to a non-storage value.")); StructType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - unsigned memberIndex = type.memberNameToIndex(_memberAccess.getMemberName()); - if (asserts(memberIndex <= type.getMemberCount())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member not found in struct during compilation.")); - m_context << type.getStorageOffsetOfMember(memberIndex) << eth::Instruction::ADD; + m_context << type.getStorageOffsetOfMember(_memberAccess.getMemberName()) << eth::Instruction::ADD; m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 79d4a1c17..897ca221c 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -86,6 +86,8 @@ shared_ptr Type::forLiteral(Literal const& _literal) } } +const MemberList Type::EmptyMemberList = MemberList(); + shared_ptr IntegerType::smallestTypeForLiteral(string const& _literal) { bigint value(_literal); @@ -226,15 +228,15 @@ bool StructType::operator==(Type const& _other) const u256 StructType::getStorageSize() const { u256 size = 0; - for (ASTPointer const& variable: m_struct.getMembers()) - size += variable->getType()->getStorageSize(); + for (pair> const& member: getMembers()) + size += member.second->getStorageSize(); return max(1, size); } bool StructType::canLiveOutsideStorage() const { - for (unsigned i = 0; i < getMemberCount(); ++i) - if (!getMemberByIndex(i)->canLiveOutsideStorage()) + for (pair> const& member: getMembers()) + if (!member.second->canLiveOutsideStorage()) return false; return true; } @@ -244,33 +246,30 @@ string StructType::toString() const return string("struct ") + m_struct.getName(); } -unsigned StructType::getMemberCount() const -{ - return m_struct.getMembers().size(); -} - -unsigned StructType::memberNameToIndex(string const& _name) const -{ - vector> const& members = m_struct.getMembers(); - for (unsigned index = 0; index < members.size(); ++index) - if (members[index]->getName() == _name) - return index; - return unsigned(-1); -} - -shared_ptr const& StructType::getMemberByIndex(unsigned _index) const +MemberList const& StructType::getMembers() const { - return m_struct.getMembers()[_index].getType(); + // We need to lazy-initialize it because of recursive references. + if (!m_members) + { + map> members; + for (ASTPointer const& variable: m_struct.getMembers()) + members[variable->getName()] = variable->getType(); + m_members.reset(new MemberList(members)); + } + return *m_members; } -u256 StructType::getStorageOffsetOfMember(unsigned _index) const +u256 StructType::getStorageOffsetOfMember(string const& _name) const { //@todo cache member offset? u256 offset; -// vector> const& members = m_struct.getMembers(); - for (unsigned index = 0; index < _index; ++index) - offset += getMemberByIndex(index)->getStorageSize(); - return offset; + for (ASTPointer variable: m_struct.getMembers()) + { + offset += variable->getType()->getStorageSize(); + if (variable->getName() == _name) + return offset; + } + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); } bool FunctionType::operator==(Type const& _other) const diff --git a/libsolidity/Types.h b/libsolidity/Types.h index b0d95d291..2208a880e 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -37,6 +38,33 @@ namespace solidity // @todo realMxN, string +class Type; // forward + +/** + * List of members of a type. + */ +class MemberList +{ +public: + using TypePointer = std::shared_ptr; + using MemberMap = std::map; + + MemberList() {} + explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {} + TypePointer getMemberType(std::string const& _name) const + { + auto it = m_memberTypes.find(_name); + return it != m_memberTypes.end() ? it->second : std::shared_ptr(); + } + + MemberMap::const_iterator begin() const { return m_memberTypes.begin(); } + MemberMap::const_iterator end() const { return m_memberTypes.end(); } + +private: + MemberMap m_memberTypes; +}; + + /** * Abstract base class that forms the root of the type hierarchy. */ @@ -81,12 +109,21 @@ public: /// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping. virtual bool canLiveOutsideStorage() const { return true; } + /// Returns the list of all members of this type. Default implementation: no members. + virtual MemberList const& getMembers() const { return EmptyMemberList; } + /// Convenience method, returns the type of the given named member or an empty pointer if no such member exists. + std::shared_ptr getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); } + virtual std::string toString() const = 0; virtual u256 literalValue(Literal const&) const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested " "for type without literals.")); } + +protected: + /// Convenience object used when returning an empty member list. + static const MemberList EmptyMemberList; }; /** @@ -187,14 +224,14 @@ public: virtual bool canLiveOutsideStorage() const; virtual std::string toString() const override; - unsigned getMemberCount() const; - /// Returns the index of the member with name @a _name or unsigned(-1) if it does not exist. - unsigned memberNameToIndex(std::string const& _name) const; - std::shared_ptr const& getMemberByIndex(unsigned _index) const; - u256 getStorageOffsetOfMember(unsigned _index) const; + virtual MemberList const& getMembers() const override; + + u256 getStorageOffsetOfMember(std::string const& _name) const; private: StructDefinition const& m_struct; + /// List of member types, will be lazy-initialized because of recursive references. + mutable std::unique_ptr m_members; }; /** From 638960f22f5c5d394dce0bb8223b860e4869b8c7 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 20 Nov 2014 18:33:23 +0100 Subject: [PATCH 19/25] Contracts as types and framework for special global variables. --- libsolidity/AST.cpp | 36 ++++++++++++- libsolidity/AST.h | 20 +++++--- libsolidity/CompilerStack.cpp | 5 +- libsolidity/CompilerStack.h | 9 ++-- libsolidity/ExpressionCompiler.cpp | 14 ++++-- libsolidity/GlobalContext.cpp | 70 ++++++++++++++++++++++++++ libsolidity/GlobalContext.h | 61 ++++++++++++++++++++++ libsolidity/NameAndTypeResolver.cpp | 43 +++++----------- libsolidity/NameAndTypeResolver.h | 3 +- libsolidity/Scanner.cpp | 1 - libsolidity/Token.h | 1 - libsolidity/Types.cpp | 30 +++++++++-- libsolidity/Types.h | 32 +++++++----- test/solidityCompiler.cpp | 2 +- test/solidityEndToEndTest.cpp | 29 +++++++---- test/solidityExpressionCompiler.cpp | 2 +- test/solidityNameAndTypeResolution.cpp | 2 +- test/solidityParser.cpp | 20 ++++++++ 18 files changed, 302 insertions(+), 78 deletions(-) create mode 100644 libsolidity/GlobalContext.cpp create mode 100644 libsolidity/GlobalContext.h diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 9aecbdedf..22a658d40 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -51,6 +51,40 @@ void StructDefinition::accept(ASTVisitor& _visitor) _visitor.endVisit(*this); } +void StructDefinition::checkValidityOfMembers() +{ + checkMemberTypes(); + checkRecursion(); +} + +void StructDefinition::checkMemberTypes() +{ + for (ASTPointer const& member: getMembers()) + if (!member->getType()->canBeStored()) + BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct.")); +} + +void StructDefinition::checkRecursion() +{ + set definitionsSeen; + vector queue = {this}; + while (!queue.empty()) + { + StructDefinition const* def = queue.back(); + queue.pop_back(); + if (definitionsSeen.count(def)) + BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation()) + << errinfo_comment("Recursive struct definition.")); + definitionsSeen.insert(def); + for (ASTPointer const& member: def->getMembers()) + if (member->getType()->getCategory() == Type::Category::STRUCT) + { + UserDefinedTypeName const& typeName = dynamic_cast(*member->getTypeName()); + queue.push_back(&dynamic_cast(*typeName.getReferencedDeclaration())); + } + } +} + void ParameterList::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) @@ -258,7 +292,7 @@ void Literal::accept(ASTVisitor& _visitor) _visitor.endVisit(*this); } -TypeError ASTNode::createTypeError(string const& _description) +TypeError ASTNode::createTypeError(string const& _description) const { return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 80c7dd198..01e1b54f0 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -66,7 +66,7 @@ public: /// Creates a @ref TypeError exception and decorates it with the location of the node and /// the given description - TypeError createTypeError(std::string const& _description); + TypeError createTypeError(std::string const& _description) const; ///@{ ///@name equality operators @@ -139,7 +139,14 @@ public: std::vector> const& getMembers() const { return m_members; } + /// Checks that the members do not include any recursive structs and have valid types + /// (e.g. no functions). + void checkValidityOfMembers(); + private: + void checkMemberTypes(); + void checkRecursion(); + std::vector> m_members; }; @@ -210,7 +217,6 @@ public: Declaration(_location, _name), m_typeName(_type) {} virtual void accept(ASTVisitor& _visitor) override; - bool isTypeGivenExplicitly() const { return bool(m_typeName); } TypeName* getTypeName() const { return m_typeName.get(); } /// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly @@ -238,6 +244,7 @@ public: /// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared /// pointer until the types have been resolved using the @ref NameAndTypeResolver. + /// If it returns an empty shared pointer after that, this indicates that the type was not found. virtual std::shared_ptr toType() const = 0; }; @@ -263,8 +270,7 @@ private: }; /** - * Name referring to a user-defined type (i.e. a struct). - * @todo some changes are necessary if this is also used to refer to contract types later + * Name referring to a user-defined type (i.e. a struct, contract, etc.). */ class UserDefinedTypeName: public TypeName { @@ -275,13 +281,13 @@ public: virtual std::shared_ptr toType() const override { return Type::fromUserDefinedTypeName(*this); } ASTString const& getName() const { return *m_name; } - void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; } - StructDefinition const* getReferencedStruct() const { return m_referencedStruct; } + void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } + Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } private: ASTPointer m_name; - StructDefinition* m_referencedStruct; + Declaration* m_referencedDeclaration; }; /** diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index d87c27916..f051d58fd 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,9 @@ void CompilerStack::parse() if (!m_scanner) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available.")); m_contractASTNode = Parser().parse(m_scanner); - NameAndTypeResolver().resolveNamesAndTypes(*m_contractASTNode); + m_globalContext = make_shared(); + m_globalContext->setCurrentContract(*m_contractASTNode); + NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*m_contractASTNode); m_parseSuccessful = true; } diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 2fb505897..6cae8660f 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -30,9 +30,11 @@ namespace dev { namespace solidity { -class Scanner; // forward -class ContractDefinition; // forward -class Compiler; // forward +// forward declarations +class Scanner; +class ContractDefinition; +class Compiler; +class GlobalContext; /** * Easy to use and self-contained Solidity compiler with as few header dependencies as possible. @@ -71,6 +73,7 @@ public: private: std::shared_ptr m_scanner; + std::shared_ptr m_globalContext; std::shared_ptr m_contractASTNode; bool m_parseSuccessful; std::string m_interface; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 89eeb31f2..4d175527a 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -167,7 +167,15 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) BOOST_THROW_EXCEPTION(InternalCompilerError()); Expression& firstArgument = *_functionCall.getArguments().front(); firstArgument.accept(*this); - appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); + if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT && + _functionCall.getType()->getCategory() == Type::Category::INTEGER) + { + // explicit type conversion contract -> address, nothing to do. + } + else + { + appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); + } } else { @@ -466,7 +474,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool } } -void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(const Expression& _expression) +void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression) { if (!_expression.lvalueRequested()) { @@ -475,7 +483,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(const Express } } -void ExpressionCompiler::LValue::fromDeclaration( Expression const& _expression, Declaration const& _declaration) +void ExpressionCompiler::LValue::fromDeclaration(Expression const& _expression, Declaration const& _declaration) { if (m_context->isLocalVariable(&_declaration)) { diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp new file mode 100644 index 000000000..6179722b2 --- /dev/null +++ b/libsolidity/GlobalContext.cpp @@ -0,0 +1,70 @@ +/* + 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 . +*/ +/** + * @author Christian + * @date 2014 + * Container of the (implicit and explicit) global objects. + */ + +#include +#include +#include +#include + +using namespace std; + +namespace dev +{ +namespace solidity +{ + +GlobalContext::GlobalContext() +{ + // CurrentContract this; // @todo type depends on context -> switch prior to entering contract + // Message msg; + // Transaction tx; + // Block block; + + //@todo type will be a custom complex type, maybe the same type class for msg tx and block. + //addVariable("msg", ); +} + +void GlobalContext::setCurrentContract(ContractDefinition const& _contract) +{ + m_this = createVariable("this", make_shared(_contract)); +} + +vector GlobalContext::getDeclarations() const +{ + vector declarations; + declarations.reserve(m_objects.size() + 1); + for (ASTPointer const& declaration: m_objects) + declarations.push_back(declaration.get()); + declarations.push_back(m_this.get()); + return declarations; +} + +ASTPointer GlobalContext::createVariable(const string& _name, shared_ptr const& _type) +{ + ASTPointer variable = make_shared(Location(), ASTPointer(), + make_shared(_name)); + variable->setType(_type); + return variable; +} + +} +} diff --git a/libsolidity/GlobalContext.h b/libsolidity/GlobalContext.h new file mode 100644 index 000000000..b6dea7d56 --- /dev/null +++ b/libsolidity/GlobalContext.h @@ -0,0 +1,61 @@ +/* + 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 . +*/ +/** + * @author Christian + * @date 2014 + * Container of the (implicit and explicit) global objects. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace dev +{ +namespace solidity +{ + +class Type; // forward + +/** + * Container for all global objects which look like AST nodes, but are not part of the AST + * that is currently being compiled. + * @note must not be destroyed or moved during compilation as its objects can be referenced from + * other objects. + */ +class GlobalContext: private boost::noncopyable +{ +public: + GlobalContext(); + void setCurrentContract(ContractDefinition const& _contract); + + std::vector getDeclarations() const; + +private: + /// Creates a virtual variable declaration with the given name and type. + static ASTPointer createVariable(std::string const& _name, std::shared_ptr const& _type); + + std::vector> m_objects; + ASTPointer m_this; +}; + +} +} diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index aa7726582..225f2a78a 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -32,15 +32,20 @@ namespace solidity { +NameAndTypeResolver::NameAndTypeResolver(std::vector const& _globals) +{ + for (Declaration* declaration: _globals) + m_scopes[nullptr].registerDeclaration(*declaration); +} + void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) { - reset(); DeclarationRegistrationHelper registrar(m_scopes, _contract); m_currentScope = &m_scopes[&_contract]; for (ASTPointer const& structDef: _contract.getDefinedStructs()) ReferencesResolver resolver(*structDef, *this, nullptr); for (ASTPointer const& structDef: _contract.getDefinedStructs()) - checkForRecursion(*structDef); + structDef->checkValidityOfMembers(); for (ASTPointer const& variable: _contract.getStateVariables()) ReferencesResolver resolver(*variable, *this, nullptr); for (ASTPointer const& function: _contract.getDefinedFunctions()) @@ -73,30 +78,6 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name return m_currentScope->resolveName(_name, _recursive); } -void NameAndTypeResolver::checkForRecursion(StructDefinition const& _struct) -{ - set definitionsSeen; - vector queue = {&_struct}; - while (!queue.empty()) - { - StructDefinition const* def = queue.back(); - queue.pop_back(); - if (definitionsSeen.count(def)) - BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation()) - << errinfo_comment("Recursive struct definition.")); - definitionsSeen.insert(def); - for (ASTPointer const& member: def->getMembers()) - if (member->getType()->getCategory() == Type::Category::STRUCT) - queue.push_back(dynamic_cast(*member->getTypeName()).getReferencedStruct()); - } -} - -void NameAndTypeResolver::reset() -{ - m_scopes.clear(); - m_currentScope = nullptr; -} - DeclarationRegistrationHelper::DeclarationRegistrationHelper(map& _scopes, ASTNode& _astRoot): m_scopes(_scopes), m_currentScope(&m_scopes[nullptr]) @@ -195,7 +176,11 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable) // endVisit because the internal type needs resolving if it is a user defined type // or mapping if (_variable.getTypeName()) + { _variable.setType(_variable.getTypeName()->toType()); + if (!_variable.getType()) + BOOST_THROW_EXCEPTION(_variable.getTypeName()->createTypeError("Invalid type name")); + } else if (!m_allowLazyTypes) BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed.")); // otherwise we have a "var"-declaration whose type is resolved by the first assignment @@ -221,11 +206,7 @@ bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) if (!declaration) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) << errinfo_comment("Undeclared identifier.")); - StructDefinition* referencedStruct = dynamic_cast(declaration); - //@todo later, contracts are also valid types - if (!referencedStruct) - BOOST_THROW_EXCEPTION(_typeName.createTypeError("Identifier does not name a type name.")); - _typeName.setReferencedStruct(*referencedStruct); + _typeName.setReferencedDeclaration(*declaration); return false; } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index d335807e5..64f3c89db 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -41,8 +41,7 @@ namespace solidity class NameAndTypeResolver: private boost::noncopyable { public: - NameAndTypeResolver() {} - + explicit NameAndTypeResolver(std::vector const& _globals); void resolveNamesAndTypes(ContractDefinition& _contract); /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 895b8ae7a..dd18a320f 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -683,7 +683,6 @@ Token::Value Scanner::scanNumber(char _charSeen) KEYWORD("switch", Token::SWITCH) \ KEYWORD_GROUP('t') \ KEYWORD("text", Token::TEXT) \ - KEYWORD("this", Token::THIS) \ KEYWORD("true", Token::TRUE_LITERAL) \ KEYWORD_GROUP('u') \ KEYWORD("uint", Token::UINT) \ diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 524487521..8974ca1ad 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -160,7 +160,6 @@ namespace solidity K(RETURNS, "returns", 0) \ K(STRUCT, "struct", 0) \ K(SWITCH, "switch", 0) \ - K(THIS, "this", 0) \ K(VAR, "var", 0) \ K(WHILE, "while", 0) \ \ diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 897ca221c..f9d3d90fa 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -60,13 +60,24 @@ shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) shared_ptr Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) { - return make_shared(*_typeName.getReferencedStruct()); + Declaration const* declaration = _typeName.getReferencedDeclaration(); + if (StructDefinition const* structDef = dynamic_cast(declaration)) + return make_shared(*structDef); + else if (FunctionDefinition const* function = dynamic_cast(declaration)) + return make_shared(*function); + else if (ContractDefinition const* contract = dynamic_cast(declaration)) + return make_shared(*contract); + return shared_ptr(); } shared_ptr Type::fromMapping(Mapping const& _typeName) { shared_ptr keyType = _typeName.getKeyType().toType(); + if (!keyType) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name.")); shared_ptr valueType = _typeName.getValueType().toType(); + if (!valueType) + BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); return make_shared(keyType, valueType); } @@ -201,6 +212,15 @@ u256 BoolType::literalValue(Literal const& _literal) const BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal.")); } +bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (isImplicitlyConvertibleTo(_convertTo)) + return true; + if (_convertTo.getCategory() == Category::INTEGER) + return dynamic_cast(_convertTo).isAddress(); + return false; +} + bool ContractType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -217,6 +237,11 @@ u256 ContractType::getStorageSize() const return max(1, size); } +string ContractType::toString() const +{ + return "contract " + m_contract.getName(); +} + bool StructType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -282,8 +307,7 @@ bool FunctionType::operator==(Type const& _other) const string FunctionType::toString() const { - //@todo nice string for function types - return "function(...)returns(...)"; + return "function " + m_function.getName(); } bool MappingType::operator==(Type const& _other) const diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 2208a880e..297284ba0 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -106,6 +106,8 @@ public: /// @returns number of bytes required to hold this value in storage. /// For dynamically "allocated" types, it returns the size of the statically allocated head, virtual u256 getStorageSize() const { return 1; } + /// Returns true if the type can be stored in storage. + virtual bool canBeStored() const { return true; } /// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping. virtual bool canLiveOutsideStorage() const { return true; } @@ -151,7 +153,7 @@ public: virtual bool operator==(Type const& _other) const override; - virtual unsigned getCalldataEncodedSize() const { return m_bits / 8; } + virtual unsigned getCalldataEncodedSize() const override { return m_bits / 8; } virtual std::string toString() const override; virtual u256 literalValue(Literal const& _literal) const override; @@ -197,10 +199,11 @@ class ContractType: public Type public: virtual Category getCategory() const override { return Category::CONTRACT; } ContractType(ContractDefinition const& _contract): m_contract(_contract) {} - + /// Contracts can be converted to themselves and to addresses. + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool operator==(Type const& _other) const override; - virtual u256 getStorageSize() const; - virtual std::string toString() const override { return "contract{...}"; } + virtual u256 getStorageSize() const override; + virtual std::string toString() const override; private: ContractDefinition const& m_contract; @@ -220,8 +223,8 @@ public: } virtual bool operator==(Type const& _other) const override; - virtual u256 getStorageSize() const; - virtual bool canLiveOutsideStorage() const; + virtual u256 getStorageSize() const override; + virtual bool canLiveOutsideStorage() const override; virtual std::string toString() const override; virtual MemberList const& getMembers() const override; @@ -247,8 +250,9 @@ public: virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; - virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); } - virtual bool canLiveOutsideStorage() const { return false; } + virtual bool canBeStored() const override { return false; } + virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); } + virtual bool canLiveOutsideStorage() const override { return false; } private: FunctionDefinition const& m_function; @@ -266,7 +270,7 @@ public: virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; - virtual bool canLiveOutsideStorage() const { return false; } + virtual bool canLiveOutsideStorage() const override { return false; } std::shared_ptr getKeyType() const { return m_keyType; } std::shared_ptr getValueType() const { return m_valueType; } @@ -287,8 +291,9 @@ public: VoidType() {} virtual std::string toString() const override { return "void"; } - virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); } - virtual bool canLiveOutsideStorage() const { return false; } + virtual bool canBeStored() const override { return false; } + 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; } }; /** @@ -304,8 +309,9 @@ public: std::shared_ptr const& getActualType() const { return m_actualType; } virtual bool operator==(Type const& _other) const override; - virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } - virtual bool canLiveOutsideStorage() const { return false; } + virtual bool canBeStored() const override { return false; } + virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } + virtual bool canLiveOutsideStorage() const override { return false; } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } private: diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index e0635b6ae..1767a0f36 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -48,7 +48,7 @@ bytes compileContract(const string& _sourceCode) Parser parser; ASTPointer contract; BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared(CharStream(_sourceCode)))); - NameAndTypeResolver resolver; + NameAndTypeResolver resolver({}); BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); Compiler compiler; diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 4e68103ac..a7c6357bf 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -45,17 +45,17 @@ class ExecutionFramework public: ExecutionFramework() { g_logVerbosity = 0; } - bytes const& compileAndRun(string const& _sourceCode) + bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0) { bytes code = dev::solidity::CompilerStack::staticCompile(_sourceCode); - sendMessage(code, true); + sendMessage(code, true, _value); BOOST_REQUIRE(!m_output.empty()); return m_output; } - bytes const& callContractFunction(byte _index, bytes const& _data = bytes()) + bytes const& callContractFunction(byte _index, bytes const& _data = bytes(), u256 const& _value = 0) { - sendMessage(bytes(1, _index) + _data, false); + sendMessage(bytes(1, _index) + _data, false, _value); return m_output; } @@ -111,11 +111,11 @@ private: return toBigEndian(_cppFunction(_arguments...)); } - void sendMessage(bytes const& _data, bool _isCreation) + void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0) { eth::Executive executive(m_state); - eth::Transaction t = _isCreation ? eth::Transaction(0, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) - : eth::Transaction(0, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); + eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) + : eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); bytes transactionRLP = t.rlp(); try { @@ -125,7 +125,7 @@ private: catch (...) {} if (_isCreation) { - BOOST_REQUIRE(!executive.create(Address(), 0, m_gasPrice, m_gas, &_data, Address())); + BOOST_REQUIRE(!executive.create(Address(), _value, m_gasPrice, m_gas, &_data, Address())); m_contractAddress = executive.newAddress(); BOOST_REQUIRE(m_contractAddress); BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); @@ -133,7 +133,7 @@ private: else { BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); - BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), 0, m_gasPrice, &_data, m_gas, Address())); + BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), _value, m_gasPrice, &_data, m_gas, Address())); } BOOST_REQUIRE(executive.go()); executive.finalize(); @@ -722,6 +722,17 @@ BOOST_AUTO_TEST_CASE(constructor) testSolidityAgainstCpp(0, get, u256(7)); } +BOOST_AUTO_TEST_CASE(balance) +{ + char const* sourceCode = "contract test {\n" + " function getBalance() returns (u256 balance) {\n" + " return address(this).balance;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode, 23); + BOOST_CHECK(callContractFunction(0) == toBigEndian(u256(23))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/solidityExpressionCompiler.cpp b/test/solidityExpressionCompiler.cpp index 59a9e9336..6ea66badb 100644 --- a/test/solidityExpressionCompiler.cpp +++ b/test/solidityExpressionCompiler.cpp @@ -88,7 +88,7 @@ bytes compileFirstExpression(const string& _sourceCode, vector> _ Parser parser; ASTPointer contract; BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared(CharStream(_sourceCode)))); - NameAndTypeResolver resolver; + NameAndTypeResolver resolver({}); BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); FirstExpressionExtractor extractor(*contract); BOOST_REQUIRE(extractor.getExpression() != nullptr); diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 930bba0e3..61aabb346 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -43,7 +43,7 @@ void parseTextAndResolveNames(std::string const& _source) Parser parser; ASTPointer contract = parser.parse( std::make_shared(CharStream(_source))); - NameAndTypeResolver resolver; + NameAndTypeResolver resolver({}); resolver.resolveNamesAndTypes(*contract); } } diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 9319a02c5..0ee88b261 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -221,6 +221,26 @@ BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(blockchain_access) +{ + char const* text = "contract test {\n" + " function fun() {\n" + " u256 x = address(0).balance;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(blockchain_access_invalid) +{ + char const* text = "contract test {\n" + " function fun() {\n" + " address(0).balance = 7;\n" + " }\n" + "}\n"; + BOOST_CHECK_THROW(parseText(text), TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } From 27d20cd849d7404e69ee92588b35120265214eb6 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 21 Nov 2014 19:14:56 +0100 Subject: [PATCH 20/25] Magic variables. --- libsolidity/AST.cpp | 17 +++--- libsolidity/AST.h | 22 +++++++ libsolidity/ASTForward.h | 1 + libsolidity/Compiler.cpp | 15 ++--- libsolidity/Compiler.h | 5 +- libsolidity/CompilerContext.cpp | 5 ++ libsolidity/CompilerContext.h | 4 ++ libsolidity/CompilerStack.cpp | 2 +- libsolidity/ExpressionCompiler.cpp | 85 +++++++++++++++++--------- libsolidity/ExpressionCompiler.h | 13 ++-- libsolidity/GlobalContext.cpp | 32 +++++++--- libsolidity/GlobalContext.h | 11 ++-- libsolidity/Types.cpp | 2 + libsolidity/Types.h | 10 ++- test/solidityCompiler.cpp | 2 +- test/solidityEndToEndTest.cpp | 2 +- test/solidityNameAndTypeResolution.cpp | 20 ++++++ test/solidityParser.cpp | 20 ------ 18 files changed, 172 insertions(+), 96 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 22a658d40..168a095c8 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -372,8 +372,6 @@ void VariableDefinition::checkTypeRequirements() m_variable->setType(m_value->getType()); } } - if (m_variable->getType() && !m_variable->getType()->canLiveOutsideStorage()) - BOOST_THROW_EXCEPTION(m_variable->createTypeError("Type is required to live outside storage.")); } void Assignment::checkTypeRequirements() @@ -466,8 +464,6 @@ void FunctionCall::checkTypeRequirements() } else { - m_expression->requireLValue(); - //@todo would be nice to create a struct type from the arguments // and then ask if that is implicitly convertible to the struct represented by the // function parameters @@ -495,25 +491,23 @@ bool FunctionCall::isTypeConversion() const void MemberAccess::checkTypeRequirements() { m_expression->checkTypeRequirements(); - m_expression->requireLValue(); Type const& type = *m_expression->getType(); m_type = type.getMemberType(*m_memberName); if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString())); - m_isLvalue = true; + m_isLvalue = (type.getCategory() == Type::Category::STRUCT && m_type->getCategory() != Type::Category::MAPPING); } void IndexAccess::checkTypeRequirements() { m_base->checkTypeRequirements(); - m_base->requireLValue(); if (m_base->getType()->getCategory() != Type::Category::MAPPING) BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " + m_base->getType()->toString() + ")")); MappingType const& type = dynamic_cast(*m_base->getType()); m_index->expectType(*type.getKeyType()); m_type = type.getValueType(); - m_isLvalue = true; + m_isLvalue = m_type->getCategory() != Type::Category::MAPPING; } void Identifier::checkTypeRequirements() @@ -545,7 +539,6 @@ void Identifier::checkTypeRequirements() // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type // conversion. m_type = make_shared(*functionDef); - m_isLvalue = true; return; } ContractDefinition* contractDef = dynamic_cast(m_referencedDeclaration); @@ -554,6 +547,12 @@ void Identifier::checkTypeRequirements() m_type = make_shared(make_shared(*contractDef)); return; } + MagicVariableDeclaration* magicVariable = dynamic_cast(m_referencedDeclaration); + if (magicVariable) + { + m_type = magicVariable->getType(); + return; + } BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type.")); } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 01e1b54f0..f8ff52749 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -230,6 +230,28 @@ private: std::shared_ptr m_type; ///< derived type, initially empty }; +/** + * Pseudo AST node that is used as declaration for "this", "msg", "tx" and "block" when the + * identifier is encountered. Will never have a valid location in the source code. + */ +class MagicVariableDeclaration: public Declaration +{ +public: + enum class VariableKind { THIS, MSG, TX, BLOCK }; + MagicVariableDeclaration(VariableKind _kind, ASTString const& _name, + std::shared_ptr const& _type): + Declaration(Location(), std::make_shared(_name)), m_kind(_kind), m_type(_type) {} + virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError() + << errinfo_comment("MagicVariableDeclaration used inside real AST.")); } + + std::shared_ptr const& getType() const { return m_type; } + VariableKind getKind() const { return m_kind; } + +private: + VariableKind m_kind; + std::shared_ptr m_type; +}; + /// Types /// @{ diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index 2b0bd8869..a369c8a79 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -40,6 +40,7 @@ class StructDefinition; class ParameterList; class FunctionDefinition; class VariableDeclaration; +class MagicVariableDeclaration; class TypeName; class ElementaryTypeName; class UserDefinedTypeName; diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index a3865bc30..a82ecd95a 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -32,17 +32,13 @@ using namespace std; namespace dev { namespace solidity { -bytes Compiler::compile(ContractDefinition& _contract, bool _optimize) -{ - Compiler compiler; - compiler.compileContract(_contract); - return compiler.m_context.getAssembledBytecode(_optimize); -} - -void Compiler::compileContract(ContractDefinition& _contract) +void Compiler::compileContract(ContractDefinition& _contract, vector const& _magicGlobals) { m_context = CompilerContext(); // clear it just in case + for (MagicVariableDeclaration const* variable: _magicGlobals) + m_context.addMagicGlobal(*variable); + for (ASTPointer const& function: _contract.getDefinedFunctions()) if (function->getName() != _contract.getName()) // don't add the constructor here m_context.addFunction(*function); @@ -328,7 +324,8 @@ bool Compiler::visit(ExpressionStatement& _expressionStatement) { Expression& expression = _expressionStatement.getExpression(); ExpressionCompiler::compileExpression(m_context, expression); - if (expression.getType()->getCategory() != Type::Category::VOID) + Type::Category category = expression.getType()->getCategory(); + if (category != Type::Category::VOID && category != Type::Category::MAGIC) m_context << eth::Instruction::POP; return false; } diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 3887d3b5b..70e6c44f2 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -32,13 +32,10 @@ class Compiler: private ASTVisitor public: Compiler(): m_returnTag(m_context.newTag()) {} - void compileContract(ContractDefinition& _contract); + void compileContract(ContractDefinition& _contract, std::vector const& _magicGlobals); bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); } void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); } - /// Compile the given contract and return the EVM bytecode. - static bytes compile(ContractDefinition& _contract, bool _optimize); - private: /// Creates a new compiler context / assembly, packs the current code into the data part and /// adds the constructor code. diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index 3c1acdfa7..b89a8e5b5 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/CompilerContext.cpp @@ -30,6 +30,11 @@ using namespace std; namespace dev { namespace solidity { +void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration) +{ + m_magicGlobals.insert(&_declaration); +} + void CompilerContext::addStateVariable(VariableDeclaration const& _declaration) { m_stateVariables[&_declaration] = m_stateVariablesSize; diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index e624222dd..6a48e1485 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -40,6 +40,7 @@ class CompilerContext public: CompilerContext(): m_stateVariablesSize(0) {} + void addMagicGlobal(MagicVariableDeclaration const& _declaration); void addStateVariable(VariableDeclaration const& _declaration); void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); } void initializeLocalVariables(unsigned _numVariables); @@ -48,6 +49,7 @@ public: void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } + bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration); } bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); } bool isLocalVariable(Declaration const* _declaration) const; bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration); } @@ -90,6 +92,8 @@ public: private: eth::Assembly m_asm; + /// Magic global variables like msg, tx or this, distinguished by type. + std::set m_magicGlobals; /// Size of the state variables, offset of next variable to be added. u256 m_stateVariablesSize; /// Storage offsets of state variables diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index f051d58fd..6535e00d4 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -64,7 +64,7 @@ bytes const& CompilerStack::compile(bool _optimize) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); m_bytecode.clear(); m_compiler = make_shared(); - m_compiler->compileContract(*m_contractASTNode); + m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables()); return m_bytecode = m_compiler->getAssembledBytecode(_optimize); } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 4d175527a..b1a49457c 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -162,7 +162,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) { if (_functionCall.isTypeConversion()) { - //@todo we only have integers and bools for now which cannot be explicitly converted + //@todo struct construction if (asserts(_functionCall.getArguments().size() == 1)) BOOST_THROW_EXCEPTION(InternalCompilerError()); Expression& firstArgument = *_functionCall.getArguments().front(); @@ -179,6 +179,8 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) } else { + //@todo: check for "external call" (to be stored in type) + // Calling convention: Caller pushes return address and arguments // Callee removes them and pushes return values FunctionDefinition const& function = dynamic_cast(*_functionCall.getExpression().getType()).getFunction(); @@ -193,9 +195,6 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType()); } _functionCall.getExpression().accept(*this); - if (asserts(m_currentLValue.isInCode())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Code reference expected.")); - m_currentLValue.reset(); m_context.appendJump(); m_context << returnLabel; @@ -213,19 +212,36 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) { - if (asserts(m_currentLValue.isInStorage())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to a non-storage value.")); - StructType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - m_context << type.getStorageOffsetOfMember(_memberAccess.getMemberName()) << eth::Instruction::ADD; - m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); + switch (_memberAccess.getExpression().getType()->getCategory()) + { + case Type::Category::INTEGER: + if (asserts(_memberAccess.getMemberName() == "balance")) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); + m_context << eth::Instruction::BALANCE; + break; + case Type::Category::CONTRACT: + // call function + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented.")); + break; + case Type::Category::MAGIC: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Magic variables not yet implemented.")); + break; + case Type::Category::STRUCT: + { + StructType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); + m_context << type.getStorageOffsetOfMember(_memberAccess.getMemberName()) << eth::Instruction::ADD; + m_currentLValue = LValue(m_context, LValue::STORAGE); + m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); + break; + } + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type.")); + } } bool ExpressionCompiler::visit(IndexAccess& _indexAccess) { - m_currentLValue.reset(); _indexAccess.getBaseExpression().accept(*this); - if (asserts(m_currentLValue.isInStorage())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index access to a non-storage value.")); _indexAccess.getIndexExpression().accept(*this); appendTypeConversion(*_indexAccess.getIndexExpression().getType(), *dynamic_cast(*_indexAccess.getBaseExpression().getType()).getKeyType(), @@ -242,8 +258,25 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess) void ExpressionCompiler::endVisit(Identifier& _identifier) { - m_currentLValue.fromDeclaration(_identifier, *_identifier.getReferencedDeclaration()); - m_currentLValue.retrieveValueIfLValueNotRequested(_identifier); + Declaration* declaration = _identifier.getReferencedDeclaration(); + if (MagicVariableDeclaration* magicVar = dynamic_cast(declaration)) + { + if (magicVar->getKind() == MagicVariableDeclaration::VariableKind::THIS) + m_context << eth::Instruction::ADDRESS; + return; + } + if (FunctionDefinition* functionDef = dynamic_cast(declaration)) + { + m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag(); + return; + } + if (VariableDeclaration* varDef = dynamic_cast(declaration)) + { + m_currentLValue.fromIdentifier(_identifier, *_identifier.getReferencedDeclaration()); + m_currentLValue.retrieveValueIfLValueNotRequested(_identifier); + return; + } + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); } void ExpressionCompiler::endVisit(Literal& _literal) @@ -410,9 +443,6 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo { switch (m_type) { - case CODE: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Tried to retrieve value of a function.")); - break; case STACK: { unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); @@ -423,11 +453,15 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo break; } case STORAGE: + if (!_expression.getType()->isValueType()) + break; // no distinction between value and reference for non-value types if (!_remove) *m_context << eth::Instruction::DUP1; *m_context << eth::Instruction::SLOAD; break; case MEMORY: + if (!_expression.getType()->isValueType()) + break; // no distinction between value and reference for non-value types BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) << errinfo_comment("Location type not yet implemented.")); break; @@ -455,15 +489,15 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool break; } case LValue::STORAGE: + if (!_expression.getType()->isValueType()) + break; // no distinction between value and reference for non-value types if (!_move) *m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1; *m_context << eth::Instruction::SSTORE; break; - case LValue::CODE: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) - << errinfo_comment("Location type does not support assignment.")); - break; case LValue::MEMORY: + if (!_expression.getType()->isValueType()) + break; // no distinction between value and reference for non-value types BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) << errinfo_comment("Location type not yet implemented.")); break; @@ -483,7 +517,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co } } -void ExpressionCompiler::LValue::fromDeclaration(Expression const& _expression, Declaration const& _declaration) +void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration) { if (m_context->isLocalVariable(&_declaration)) { @@ -495,13 +529,8 @@ void ExpressionCompiler::LValue::fromDeclaration(Expression const& _expression, m_type = STORAGE; *m_context << m_context->getStorageLocationOfVariable(_declaration); } - else if (m_context->isFunctionDefinition(&_declaration)) - { - m_type = CODE; - *m_context << m_context->getFunctionEntryLabel(dynamic_cast(_declaration)).pushTag(); - } else - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation()) << errinfo_comment("Identifier type not supported or identifier not found.")); } diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index f52da29ec..3ed7848b5 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -83,31 +83,30 @@ private: /** * Helper class to store and retrieve lvalues to and from various locations. - * All types except STACK store a reference in a slot on the stack, STACK just stores the - * base stack offset of the variable in @a m_baseStackOffset. + * All types except STACK store a reference in a slot on the stack, STACK just + * stores the base stack offset of the variable in @a m_baseStackOffset. */ class LValue { public: - enum LValueType { NONE, CODE, STACK, MEMORY, STORAGE }; + enum LValueType { NONE, STACK, MEMORY, STORAGE }; explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); } LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0): m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {} /// Set type according to the declaration and retrieve the reference. - /// @a _expression is the current expression, used for error reporting. - void fromDeclaration(Expression const& _expression, Declaration const& _declaration); + /// @a _expression is the current expression + void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration); void reset() { m_type = NONE; m_baseStackOffset = 0; } bool isValid() const { return m_type != NONE; } - bool isInCode() const { return m_type == CODE; } bool isInOnStack() const { return m_type == STACK; } bool isInMemory() const { return m_type == MEMORY; } bool isInStorage() const { return m_type == STORAGE; } /// @returns true if this lvalue reference type occupies a slot on the stack. - bool storesReferenceOnStack() const { return m_type == STORAGE || m_type == MEMORY || m_type == CODE; } + bool storesReferenceOnStack() const { return m_type == STORAGE || m_type == MEMORY; } /// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true, /// also removes the reference from the stack (note that is does not reset the type to @a NONE). diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index 6179722b2..f2e6d9ce9 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -45,25 +45,37 @@ GlobalContext::GlobalContext() void GlobalContext::setCurrentContract(ContractDefinition const& _contract) { - m_this = createVariable("this", make_shared(_contract)); + m_currentContract = &_contract; } vector GlobalContext::getDeclarations() const { vector declarations; - declarations.reserve(m_objects.size() + 1); - for (ASTPointer const& declaration: m_objects) - declarations.push_back(declaration.get()); - declarations.push_back(m_this.get()); + declarations.reserve(m_magicVariables.size() + 1); + for (ASTPointer const& variable: m_magicVariables) + declarations.push_back(variable.get()); + declarations.push_back(getCurrentThis()); return declarations; } -ASTPointer GlobalContext::createVariable(const string& _name, shared_ptr const& _type) +MagicVariableDeclaration*GlobalContext::getCurrentThis() const { - ASTPointer variable = make_shared(Location(), ASTPointer(), - make_shared(_name)); - variable->setType(_type); - return variable; + if (!m_thisPointer[m_currentContract]) + m_thisPointer[m_currentContract] = make_shared( + MagicVariableDeclaration::VariableKind::THIS, + "this", make_shared(*m_currentContract)); + return m_thisPointer[m_currentContract].get(); + +} + +vector GlobalContext::getMagicVariables() const +{ + vector declarations; + declarations.reserve(m_magicVariables.size() + 1); + for (ASTPointer const& variable: m_magicVariables) + declarations.push_back(variable.get()); + declarations.push_back(getCurrentThis()); + return declarations; } } diff --git a/libsolidity/GlobalContext.h b/libsolidity/GlobalContext.h index b6dea7d56..0166734ca 100644 --- a/libsolidity/GlobalContext.h +++ b/libsolidity/GlobalContext.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -47,14 +48,14 @@ public: GlobalContext(); void setCurrentContract(ContractDefinition const& _contract); + std::vector getMagicVariables() const; std::vector getDeclarations() const; private: - /// Creates a virtual variable declaration with the given name and type. - static ASTPointer createVariable(std::string const& _name, std::shared_ptr const& _type); - - std::vector> m_objects; - ASTPointer m_this; + MagicVariableDeclaration* getCurrentThis() const; + std::vector> m_magicVariables; + ContractDefinition const* m_currentContract; + std::map> mutable m_thisPointer; }; } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index f9d3d90fa..8ded428f8 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -189,6 +189,8 @@ u256 IntegerType::literalValue(Literal const& _literal) const return u256(value); } +const MemberList IntegerType::AddressMemberList = MemberList({{"balance", std::make_shared(256)}}); + bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const { // conversion to integer is fine, but not to address diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 297284ba0..a8caf7154 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -73,7 +73,7 @@ class Type: private boost::noncopyable public: enum class Category { - INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE + INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MAGIC }; ///@{ @@ -110,6 +110,9 @@ public: virtual bool canBeStored() const { return true; } /// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping. virtual bool canLiveOutsideStorage() const { return true; } + /// Returns true if the type can be stored as a value (as opposed to a reference) on the stack, + /// i.e. it behaves differently in lvalue context and in value context. + virtual bool isValueType() const { return false; } /// Returns the list of all members of this type. Default implementation: no members. virtual MemberList const& getMembers() const { return EmptyMemberList; } @@ -154,6 +157,9 @@ public: virtual bool operator==(Type const& _other) const override; virtual unsigned getCalldataEncodedSize() const override { return m_bits / 8; } + virtual bool isValueType() const override { return true; } + + virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; } virtual std::string toString() const override; virtual u256 literalValue(Literal const& _literal) const override; @@ -166,6 +172,7 @@ public: private: int m_bits; Modifier m_modifier; + static const MemberList AddressMemberList; }; /** @@ -186,6 +193,7 @@ public: } virtual unsigned getCalldataEncodedSize() const { return 1; } + virtual bool isValueType() const override { return true; } virtual std::string toString() const override { return "bool"; } virtual u256 literalValue(Literal const& _literal) const override; diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index 1767a0f36..3e86919a6 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -52,7 +52,7 @@ bytes compileContract(const string& _sourceCode) BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); Compiler compiler; - compiler.compileContract(*contract); + compiler.compileContract(*contract, {}); // debug //compiler.streamAssembly(cout); return compiler.getAssembledBytecode(); diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index a7c6357bf..46081a63c 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -725,7 +725,7 @@ BOOST_AUTO_TEST_CASE(constructor) BOOST_AUTO_TEST_CASE(balance) { char const* sourceCode = "contract test {\n" - " function getBalance() returns (u256 balance) {\n" + " function getBalance() returns (uint256 balance) {\n" " return address(this).balance;\n" " }\n" "}\n"; diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 61aabb346..a19e7450c 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -224,6 +224,26 @@ BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } +BOOST_AUTO_TEST_CASE(balance) +{ + char const* text = "contract test {\n" + " function fun() {\n" + " uint256 x = address(0).balance;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(balance_invalid) +{ + char const* text = "contract test {\n" + " function fun() {\n" + " address(0).balance = 7;\n" + " }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 0ee88b261..9319a02c5 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -221,26 +221,6 @@ BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion) BOOST_CHECK_NO_THROW(parseText(text)); } -BOOST_AUTO_TEST_CASE(blockchain_access) -{ - char const* text = "contract test {\n" - " function fun() {\n" - " u256 x = address(0).balance;\n" - " }\n" - "}\n"; - BOOST_CHECK_NO_THROW(parseText(text)); -} - -BOOST_AUTO_TEST_CASE(blockchain_access_invalid) -{ - char const* text = "contract test {\n" - " function fun() {\n" - " address(0).balance = 7;\n" - " }\n" - "}\n"; - BOOST_CHECK_THROW(parseText(text), TypeError); -} - BOOST_AUTO_TEST_SUITE_END() } From 60a62708c4df4d79ed47c9510a7c40fc643b181a Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 23 Nov 2014 21:17:51 +0100 Subject: [PATCH 21/25] Another test for struct references. --- test/solidityEndToEndTest.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 46081a63c..11535d4f6 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -700,6 +700,34 @@ BOOST_AUTO_TEST_CASE(structs) BOOST_CHECK(callContractFunction(0) == bytes({0x01})); } +BOOST_AUTO_TEST_CASE(struct_reference) +{ + char const* sourceCode = "contract test {\n" + " struct s2 {\n" + " uint32 z;\n" + " mapping(uint8 => s2) recursive;\n" + " }\n" + " s2 data;\n" + " function check() returns (bool ok) {\n" + " return data.z == 2 && \n" + " data.recursive[0].z == 3 && \n" + " data.recursive[0].recursive[1].z == 0 && \n" + " data.recursive[0].recursive[0].z == 1;\n" + " }\n" + " function set() {\n" + " data.z = 2;\n" + " var map = data.recursive;\n" + " s2 inner = map[0];\n" + " inner.z = 3;\n" + " inner.recursive[0].z = inner.recursive[1].z + 1;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction(0) == bytes({0x00})); + BOOST_CHECK(callContractFunction(1) == bytes()); + BOOST_CHECK(callContractFunction(0) == bytes({0x01})); +} + BOOST_AUTO_TEST_CASE(constructor) { char const* sourceCode = "contract test {\n" From c1ae9fc1262ef18541c2af74c3195c4f89de206b Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 23 Nov 2014 22:57:47 +0100 Subject: [PATCH 22/25] pr fixes --- libp2p/Common.cpp | 14 ++++++++++++++ libp2p/Common.h | 1 + libp2p/Host.cpp | 42 +++++++++++++++++------------------------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp index 8a3b27ef0..e0f3b5629 100644 --- a/libp2p/Common.cpp +++ b/libp2p/Common.cpp @@ -53,6 +53,20 @@ bool p2p::isPrivateAddress(bi::address const& _addressToCheck) return false; } +// Helper function to determine if an address is localhost +bool p2p::isLocalHostAddress(bi::address const& _addressToCheck) +{ + // @todo: ivp6 link-local adresses (macos), ex: fe80::1%lo0 + static const set c_rejectAddresses = { + {bi::address_v4::from_string("127.0.0.1")}, + {bi::address_v4::from_string("0.0.0.0")}, + {bi::address_v6::from_string("::1")}, + {bi::address_v6::from_string("::")} + }; + + return find(c_rejectAddresses.begin(), c_rejectAddresses.end(), _addressToCheck) != c_rejectAddresses.end(); +} + std::string p2p::reasonOf(DisconnectReason _r) { switch (_r) diff --git a/libp2p/Common.h b/libp2p/Common.h index 0df7f14fe..34f47b236 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -47,6 +47,7 @@ namespace p2p using NodeId = h512; bool isPrivateAddress(bi::address const& _addressToCheck); +bool isLocalHostAddress(bi::address const& _addressToCheck); class UPnP; class Capability; diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 471d1dc56..6e151d34d 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -46,15 +46,6 @@ using namespace std; using namespace dev; using namespace dev::p2p; -// Addresses skipped during network interface discovery -// @todo: filter out ivp6 link-local network mess on macos, ex: fe80::1%lo0 -static const set c_rejectAddresses = { - {bi::address_v4::from_string("127.0.0.1")}, - {bi::address_v4::from_string("0.0.0.0")}, - {bi::address_v6::from_string("::1")}, - {bi::address_v6::from_string("::")} -}; - std::vector Host::getInterfaceAddresses() { std::vector addresses; @@ -86,7 +77,7 @@ std::vector Host::getInterfaceAddresses() memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); char *addrStr = inet_ntoa(addr); bi::address address(bi::address::from_string(addrStr)); - if (std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), address) == c_rejectAddresses.end()) + if (!isLocalHostAddress(address)) addresses.push_back(ad.to_v4()); } @@ -105,7 +96,7 @@ std::vector Host::getInterfaceAddresses() { in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr)); - if (std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), address) == c_rejectAddresses.end()) + if (!isLocalHostAddress(address)) addresses.push_back(address); } else if (ifa->ifa_addr->sa_family == AF_INET6) @@ -115,11 +106,13 @@ std::vector Host::getInterfaceAddresses() boost::asio::ip::address_v6::bytes_type bytes; memcpy(&bytes[0], addr.s6_addr, 16); boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id); - if (std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), address) == c_rejectAddresses.end()) + if (!isLocalHostAddress(address)) addresses.push_back(address); } } - if (ifaddr!=NULL) freeifaddrs(ifaddr); + + if (ifaddr!=NULL) + freeifaddrs(ifaddr); #endif @@ -148,7 +141,6 @@ int Host::listen4(bi::tcp::acceptor* _acceptor, unsigned short _listenPort) { // both attempts failed cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); - _acceptor->close(); } // first attempt failed @@ -175,9 +167,9 @@ bi::tcp::endpoint Host::traverseNAT(std::vector const& _ifAddresses if (upnp && upnp->isValid()) { bi::address paddr; - int p; - for (auto const& addr : _ifAddresses) - if (addr.is_v4() && isPrivateAddress(addr) && (p = upnp->addRedirect(addr.to_string().c_str(), _listenPort))) + int extPort = 0; + for (auto const& addr: _ifAddresses) + if (addr.is_v4() && isPrivateAddress(addr) && (extPort = upnp->addRedirect(addr.to_string().c_str(), _listenPort))) { paddr = addr; break; @@ -185,17 +177,17 @@ bi::tcp::endpoint Host::traverseNAT(std::vector const& _ifAddresses auto eip = upnp->externalIP(); bi::address eipaddr(bi::address::from_string(eip)); - if (p && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr)) + if (extPort && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr)) { - clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << p << "."; + clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << extPort << "."; clog(NetNote) << "External addr:" << eip; o_upnpifaddr = paddr; - upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)p); + upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)extPort); } else clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place)."; - if(upnp) + if (upnp) delete upnp; } @@ -385,7 +377,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) // populate interfaces we'll listen on (eth listens on all interfaces); ignores local for (auto addr: m_ifAddresses) - if ((m_netPrefs.localNetworking || !isPrivateAddress(addr)) && find(c_rejectAddresses.begin(), c_rejectAddresses.end(), addr) == c_rejectAddresses.end()) + if ((m_netPrefs.localNetworking || !isPrivateAddress(addr)) && !isLocalHostAddress(addr)) m_peerAddresses.insert(addr); // if user supplied address is a public address then we use it @@ -393,7 +385,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) 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 ? false : find(c_rejectAddresses.begin(), c_rejectAddresses.end(), reqpublicaddr) == c_rejectAddresses.end(); + bool ispublic = !isprivate && !isLocalHostAddress(reqpublicaddr); if (!reqpublicaddr.is_unspecified() && (ispublic || (isprivate && m_netPrefs.localNetworking))) { if (!m_peerAddresses.count(reqpublicaddr)) @@ -709,7 +701,7 @@ void Host::run(boost::system::error_code const& error) // network running if (m_run) { - if (s_lasttick == c_timerInterval * 50) + if (s_lasttick >= c_timerInterval * 50) { growPeers(); prunePeers(); @@ -734,7 +726,7 @@ void Host::run(boost::system::error_code const& error) pingAll(); } - auto runcb = [this](boost::system::error_code const& error) -> void{ run(error); }; + auto runcb = [this](boost::system::error_code const& error) -> void { run(error); }; m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval)); m_timer->async_wait(runcb); From 0669eb8d4ca663cfe5415f186fd82cefaa09b877 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 24 Nov 2014 00:00:46 +0100 Subject: [PATCH 23/25] Whitespace change. --- libsolidity/AST.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index f8ff52749..89f268b33 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -122,6 +122,7 @@ public: /// Returns the functions that make up the calling interface in the intended order. std::vector getInterfaceFunctions() const; + private: std::vector> m_definedStructs; std::vector> m_stateVariables; @@ -195,6 +196,7 @@ public: /// Checks that all parameters have allowed types and calls checkTypeRequirements on the body. void checkTypeRequirements(); + private: bool m_isPublic; ASTPointer m_parameters; @@ -238,8 +240,7 @@ class MagicVariableDeclaration: public Declaration { public: enum class VariableKind { THIS, MSG, TX, BLOCK }; - MagicVariableDeclaration(VariableKind _kind, ASTString const& _name, - std::shared_ptr const& _type): + MagicVariableDeclaration(VariableKind _kind, ASTString const& _name, std::shared_ptr const& _type): Declaration(Location(), std::make_shared(_name)), m_kind(_kind), m_type(_type) {} virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST.")); } From b185a748a5fefa9d1d6485df116a13773b18de18 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 24 Nov 2014 13:23:58 +0100 Subject: [PATCH 24/25] Access to blockchain data. --- libsolidity/ExpressionCompiler.cpp | 31 ++++++++++++++++-- libsolidity/GlobalContext.cpp | 18 ++++++----- libsolidity/Types.cpp | 50 ++++++++++++++++++++++++++++++ libsolidity/Types.h | 24 ++++++++++++++ test/solidityEndToEndTest.cpp | 13 ++++++++ 5 files changed, 125 insertions(+), 11 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index b1a49457c..de8bc1d28 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -212,10 +212,11 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) { + ASTString const& member = _memberAccess.getMemberName(); switch (_memberAccess.getExpression().getType()->getCategory()) { case Type::Category::INTEGER: - if (asserts(_memberAccess.getMemberName() == "balance")) + if (asserts(member == "balance")) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); m_context << eth::Instruction::BALANCE; break; @@ -224,12 +225,36 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented.")); break; case Type::Category::MAGIC: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Magic variables not yet implemented.")); + // we can ignore the kind of magic and only look at the name of the member + if (member == "coinbase") + m_context << eth::Instruction::COINBASE; + else if (member == "timestamp") + m_context << eth::Instruction::TIMESTAMP; + else if (member == "prevhash") + m_context << eth::Instruction::PREVHASH; + else if (member == "difficulty") + m_context << eth::Instruction::DIFFICULTY; + else if (member == "number") + m_context << eth::Instruction::NUMBER; + else if (member == "gaslimit") + m_context << eth::Instruction::GASLIMIT; + else if (member == "sender") + m_context << eth::Instruction::CALLER; + else if (member == "value") + m_context << eth::Instruction::CALLVALUE; + else if (member == "origin") + m_context << eth::Instruction::ORIGIN; + else if (member == "gas") + m_context << eth::Instruction::GAS; + else if (member == "gasprice") + m_context << eth::Instruction::GASPRICE; + else + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member.")); break; case Type::Category::STRUCT: { StructType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - m_context << type.getStorageOffsetOfMember(_memberAccess.getMemberName()) << eth::Instruction::ADD; + m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD; m_currentLValue = LValue(m_context, LValue::STORAGE); m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); break; diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index f2e6d9ce9..4120dae5e 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -32,15 +32,17 @@ namespace dev namespace solidity { -GlobalContext::GlobalContext() +GlobalContext::GlobalContext(): + m_magicVariables{make_shared(MagicVariableDeclaration::VariableKind::BLOCK, + "block", + make_shared(MagicType::Kind::BLOCK)), + make_shared(MagicVariableDeclaration::VariableKind::MSG, + "msg", + make_shared(MagicType::Kind::MSG)), + make_shared(MagicVariableDeclaration::VariableKind::TX, + "tx", + make_shared(MagicType::Kind::TX))} { - // CurrentContract this; // @todo type depends on context -> switch prior to entering contract - // Message msg; - // Transaction tx; - // Block block; - - //@todo type will be a custom complex type, maybe the same type class for msg tx and block. - //addVariable("msg", ); } void GlobalContext::setCurrentContract(ContractDefinition const& _contract) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 8ded428f8..616c91831 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -333,5 +333,55 @@ bool TypeType::operator==(Type const& _other) const return *getActualType() == *other.getActualType(); } +MagicType::MagicType(MagicType::Kind _kind): + m_kind(_kind) +{ + switch (m_kind) + { + case Kind::BLOCK: + m_members = MemberList({{"coinbase", make_shared(0, IntegerType::Modifier::ADDRESS)}, + {"timestamp", make_shared(256)}, + {"prevhash", make_shared(256, IntegerType::Modifier::HASH)}, + {"difficulty", make_shared(256)}, + {"number", make_shared(256)}, + {"gaslimit", make_shared(256)}}); + break; + case Kind::MSG: + m_members = MemberList({{"sender", make_shared(0, IntegerType::Modifier::ADDRESS)}, + {"value", make_shared(256)}}); + break; + case Kind::TX: + m_members = MemberList({{"origin", make_shared(0, IntegerType::Modifier::ADDRESS)}, + {"gas", make_shared(256)}, + {"gasprice", make_shared(256)}}); + break; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); + } +} + +bool MagicType::operator==(Type const& _other) const +{ + if (_other.getCategory() != getCategory()) + return false; + MagicType const& other = dynamic_cast(_other); + return other.m_kind == m_kind; +} + +string MagicType::toString() const +{ + switch (m_kind) + { + case Kind::BLOCK: + return "block"; + case Kind::MSG: + return "msg"; + case Kind::TX: + return "tx"; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); + } +} + } } diff --git a/libsolidity/Types.h b/libsolidity/Types.h index a8caf7154..2dd4af64f 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -327,5 +327,29 @@ private: }; +/** + * Special type for magic variables (block, msg, tx), similar to a struct but without any reference + * (it always references a global singleton by name). + */ +class MagicType: public Type +{ +public: + enum class Kind { BLOCK, MSG, TX }; //@todo should be unified with MagicVariableDeclaration::VariableKind; + virtual Category getCategory() const override { return Category::MAGIC; } + + MagicType(Kind _kind); + virtual bool operator==(Type const& _other) const; + virtual bool canBeStored() const override { return false; } + virtual bool canLiveOutsideStorage() const override { return true; } + virtual MemberList const& getMembers() const override { return m_members; } + + virtual std::string toString() const override; + +private: + Kind m_kind; + + MemberList m_members; +}; + } } diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 11535d4f6..fc542f141 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -761,6 +761,19 @@ BOOST_AUTO_TEST_CASE(balance) BOOST_CHECK(callContractFunction(0) == toBigEndian(u256(23))); } +BOOST_AUTO_TEST_CASE(blockchain) +{ + char const* sourceCode = "contract test {\n" + " function someInfo() returns (uint256 value, address coinbase, uint256 blockNumber) {\n" + " value = msg.value;\n" + " coinbase = block.coinbase;\n" + " blockNumber = block.number;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode, 27); + BOOST_CHECK(callContractFunction(0, bytes{0}, u256(28)) == toBigEndian(u256(28)) + bytes(20, 0) + toBigEndian(u256(1))); +} + BOOST_AUTO_TEST_SUITE_END() } From 965e5bd58cac4414305a815701d42ac4ed07e8fc Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 24 Nov 2014 17:24:42 +0100 Subject: [PATCH 25/25] coding standards --- libdevcore/Worker.cpp | 4 ++-- libdevcore/Worker.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp index d9246f9fd..014780a66 100644 --- a/libdevcore/Worker.cpp +++ b/libdevcore/Worker.cpp @@ -41,8 +41,8 @@ void Worker::startWorking() startedWorking(); while (!m_stop) { - if (m_idlewaitms) - this_thread::sleep_for(chrono::milliseconds(m_idlewaitms)); + if (m_idleWaitMs) + this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs)); doWork(); } cdebug << "Finishing up worker thread"; diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h index f8d694681..f73a0f4aa 100644 --- a/libdevcore/Worker.h +++ b/libdevcore/Worker.h @@ -31,7 +31,7 @@ namespace dev class Worker { protected: - Worker(std::string const& _name = "anon", unsigned _idlewaitms = 30): m_name(_name), m_idlewaitms(_idlewaitms) {} + Worker(std::string const& _name = "anon", unsigned _idleWaitMs = 30): m_name(_name), m_idleWaitMs(_idleWaitMs) {} /// Move-constructor. Worker(Worker&& _m) { std::swap(m_name, _m.m_name); } @@ -56,7 +56,7 @@ protected: /// Called after thread is started from startWorking(). virtual void startedWorking() {} - /// Called continuously following sleep for m_idlewaitms. + /// Called continuously following sleep for m_idleWaitMs. virtual void doWork() = 0; /// Called when is to be stopped, just prior to thread being joined. @@ -64,7 +64,7 @@ protected: private: std::string m_name; - unsigned m_idlewaitms; + unsigned m_idleWaitMs; mutable Mutex x_work; ///< Lock for the network existance. std::unique_ptr m_work; ///< The network thread.