/* This file is part of cpp-ethereum. cpp-ethereum is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. cpp-ethereum is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ /** @file shhrpc.cpp * @author Vladislav Gluhovsky * @date July 2015 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace dev; using namespace dev::eth; using namespace dev::p2p; using namespace dev::shh; namespace js = json_spirit; WebThreeDirect* web3; unique_ptr jsonrpcServer; unique_ptr jsonrpcClient; static uint16_t const c_web3port = 30333; static string const c_version("shhrpc-web3"); static unsigned const c_ttl = 777000; struct Setup { Setup() { dev::p2p::NodeIPEndpoint::test_allowLocal = true; static bool setup = false; if (!setup && !test::Options::get().nonetwork) { setup = true; NetworkPreferences nprefs(std::string(), c_web3port, false); web3 = new WebThreeDirect(c_version, "", WithExisting::Trust, {"shh"}, nprefs); web3->setIdealPeerCount(1); auto server = new jsonrpc::HttpServer(8080); vector v; KeyManager keyMan; TrivialGasPricer gp; jsonrpcServer = unique_ptr(new WebThreeStubServer(*server, *web3, nullptr, v, keyMan, gp)); jsonrpcServer->setIdentities({}); jsonrpcServer->StartListening(); auto client = new jsonrpc::HttpClient("http://localhost:8080"); jsonrpcClient = unique_ptr(new WebThreeStubClient(*client)); } } ~Setup() { dev::p2p::NodeIPEndpoint::test_allowLocal = false; } }; Json::Value createMessage(string const& _from, string const& _to, string const& _topic = "", string _payload = "") { Json::Value msg; msg["from"] = _from; msg["to"] = _to; msg["ttl"] = toJS(c_ttl); if (_payload.empty()) _payload = string("0x") + h256::random().hex(); msg["payload"] = _payload; if (!_topic.empty()) { Json::Value t(Json::arrayValue); t.append(_topic); msg["topics"] = t; } return msg; } BOOST_FIXTURE_TEST_SUITE(shhrpc, Setup) BOOST_AUTO_TEST_CASE(basic) { if (test::Options::get().nonetwork) return; cnote << "Testing web3 basic functionality..."; web3->startNetwork(); unsigned const step = 10; for (unsigned i = 0; i < 3000 && !web3->haveNetwork(); i += step) this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE(web3->haveNetwork()); uint16_t const port2 = 30334; NetworkPreferences prefs2("127.0.0.1", port2, false); string const version2 = "shhrpc-host2"; Host host2(version2, prefs2); auto whost2 = host2.registerCapability(make_shared()); host2.start(); for (unsigned i = 0; i < 3000 && !host2.haveNetwork(); i += step) this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE(host2.haveNetwork()); web3->addNode(host2.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), port2, port2)); for (unsigned i = 0; i < 3000 && (!web3->peerCount() || !host2.peerCount()); i += step) this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE_EQUAL(host2.peerCount(), 1); BOOST_REQUIRE_EQUAL(web3->peerCount(), 1); vector vpeers = web3->peers(); BOOST_REQUIRE(!vpeers.empty()); PeerSessionInfo const& peer = vpeers.back(); BOOST_REQUIRE_EQUAL(peer.id, host2.id()); BOOST_REQUIRE_EQUAL(peer.port, port2); BOOST_REQUIRE_EQUAL(peer.clientVersion, version2); web3->stopNetwork(); for (unsigned i = 0; i < 3000 && (web3->haveNetwork() || host2.haveNetwork()); i += step) this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE(!web3->peerCount()); BOOST_REQUIRE(!host2.peerCount()); } BOOST_AUTO_TEST_CASE(send) { if (test::Options::get().nonetwork) return; cnote << "Testing web3 send..."; bool sent = false; bool ready = false; unsigned result = 0; unsigned const messageCount = 10; unsigned const step = 10; uint16_t port2 = 30337; Host host2("shhrpc-host2", NetworkPreferences("127.0.0.1", port2, false)); host2.setIdealPeerCount(1); auto whost2 = host2.registerCapability(make_shared()); host2.start(); web3->startNetwork(); std::thread listener([&]() { setThreadName("listener"); ready = true; auto w = whost2->installWatch(BuildTopicMask("odd")); set received; for (unsigned x = 0; x < 7000 && !sent; x += step) this_thread::sleep_for(chrono::milliseconds(step)); for (unsigned x = 0, last = 0; x < 100 && received.size() < messageCount; ++x) { this_thread::sleep_for(chrono::milliseconds(50)); for (auto i: whost2->checkWatch(w)) { Message msg = whost2->envelope(i).open(whost2->fullTopics(w)); last = RLP(msg.payload()).toInt(); if (received.insert(last).second) result += last; } } }); for (unsigned i = 0; i < 2000 && (!host2.haveNetwork() || !web3->haveNetwork()); i += step) this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE(host2.haveNetwork()); BOOST_REQUIRE(web3->haveNetwork()); web3->requirePeer(host2.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), port2, port2)); for (unsigned i = 0; i < 3000 && (!web3->peerCount() || !host2.peerCount()); i += step) this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE_EQUAL(host2.peerCount(), 1); BOOST_REQUIRE_EQUAL(web3->peerCount(), 1); KeyPair us = KeyPair::create(); for (unsigned i = 0; i < messageCount; ++i) { web3->whisper()->post(us.sec(), RLPStream().append(i * i).out(), BuildTopic(i)(i % 2 ? "odd" : "even"), 777000, 1); this_thread::sleep_for(chrono::milliseconds(50)); } sent = true; auto messages = web3->whisper()->all(); BOOST_REQUIRE_EQUAL(messages.size(), messageCount); listener.join(); BOOST_REQUIRE_EQUAL(result, 1 + 9 + 25 + 49 + 81); } BOOST_AUTO_TEST_CASE(receive) { if (test::Options::get().nonetwork) return; cnote << "Testing web3 receive..."; bool sent = false; bool ready = false; unsigned result = 0; unsigned const messageCount = 6; unsigned const step = 10; uint16_t port2 = 30338; Host host2("shhrpc-host2", NetworkPreferences("127.0.0.1", port2, false)); host2.setIdealPeerCount(1); auto whost2 = host2.registerCapability(make_shared()); host2.start(); web3->startNetwork(); std::thread listener([&]() { setThreadName("listener"); ready = true; auto w = web3->whisper()->installWatch(BuildTopicMask("odd")); set received; for (unsigned x = 0; x < 7000 && !sent; x += step) this_thread::sleep_for(chrono::milliseconds(step)); for (unsigned x = 0, last = 0; x < 100 && received.size() < messageCount; ++x) { this_thread::sleep_for(chrono::milliseconds(50)); for (auto i: web3->whisper()->checkWatch(w)) { Message msg = web3->whisper()->envelope(i).open(web3->whisper()->fullTopics(w)); last = RLP(msg.payload()).toInt(); if (received.insert(last).second) result += last; } } web3->whisper()->uninstallWatch(w); }); for (unsigned i = 0; i < 2000 && (!host2.haveNetwork() || !web3->haveNetwork()); i += step) this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE(host2.haveNetwork()); BOOST_REQUIRE(web3->haveNetwork()); host2.addNode(web3->id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), c_web3port, c_web3port)); for (unsigned i = 0; i < 3000 && (!web3->peerCount() || !host2.peerCount()); i += step) this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE_EQUAL(host2.peerCount(), 1); BOOST_REQUIRE_EQUAL(web3->peerCount(), 1); KeyPair us = KeyPair::create(); for (unsigned i = 0; i < messageCount; ++i) { web3->whisper()->post(us.sec(), RLPStream().append(i * i * i).out(), BuildTopic(i)(i % 2 ? "odd" : "even"), c_ttl, 1); this_thread::sleep_for(chrono::milliseconds(50)); } sent = true; listener.join(); BOOST_REQUIRE_EQUAL(result, 1 + 27 + 125); } BOOST_AUTO_TEST_CASE(serverBasic) { if (test::Options::get().nonetwork) return; cnote << "Testing basic jsonrpc server..."; string s = jsonrpcServer->web3_clientVersion(); BOOST_REQUIRE_EQUAL(s, c_version); s = jsonrpcServer->net_version(); BOOST_REQUIRE(s.empty()); s = jsonrpcServer->web3_sha3("some pseudo-random string here"); BOOST_REQUIRE_EQUAL(s.size(), h256::size * 2 + 2); BOOST_REQUIRE('0' == s[0] && 'x' == s[1]); s = jsonrpcServer->net_peerCount(); BOOST_REQUIRE_EQUAL(s, "0x0"); KeyPair src = KeyPair::create(); KeyPair dst = KeyPair::create(); Json::Value t1 = createMessage(toJS(src.address()), toJS(dst.address())); bool b = jsonrpcServer->shh_post(t1); BOOST_REQUIRE(b); string const id = jsonrpcServer->shh_newIdentity(); BOOST_REQUIRE_EQUAL(id.size(), 130); BOOST_REQUIRE('0' == id[0] && 'x' == id[1]); b = jsonrpcServer->shh_hasIdentity(id); BOOST_REQUIRE(b); Json::Value t2 = createMessage(id, id); b = jsonrpcServer->shh_post(t2); BOOST_REQUIRE(b); } BOOST_AUTO_TEST_CASE(server) { if (test::Options::get().nonetwork) return; cnote << "Testing server functionality..."; bool b; string s; Json::Value j; SessionPermissions permissions; permissions.privileges.insert(Privilege::Admin); string const text = string("0x") + h256::random().hex(); // message must be in raw form string sess1 = jsonrpcServer->newSession(permissions); string sess2("session number two"); jsonrpcServer->addSession(sess2, permissions); b = jsonrpcServer->admin_web3_setVerbosity(5, sess1); BOOST_REQUIRE(b); b = jsonrpcServer->admin_net_start(sess1); BOOST_REQUIRE(b); unsigned const step = 10; for (unsigned i = 0; i < 3000 && !jsonrpcServer->net_listening(); i += step) this_thread::sleep_for(chrono::milliseconds(step)); b = jsonrpcServer->net_listening(); BOOST_REQUIRE(b); b = jsonrpcServer->admin_net_stop(sess1); BOOST_REQUIRE(b); b = jsonrpcServer->net_listening(); BOOST_REQUIRE(!b); j = jsonrpcServer->admin_net_peers(sess1); BOOST_REQUIRE(j.empty()); j = jsonrpcServer->admin_net_nodeInfo(sess2); BOOST_REQUIRE_EQUAL(j["id"].asString(), web3->id().hex()); BOOST_REQUIRE_EQUAL(j["port"].asUInt(), c_web3port); uint16_t port2 = 30339; Host host2("shhrpc-host2", NetworkPreferences("127.0.0.1", port2, false)); host2.setIdealPeerCount(1); auto whost2 = host2.registerCapability(make_shared()); host2.start(); b = jsonrpcServer->admin_net_start(sess2); BOOST_REQUIRE(b); for (unsigned i = 0; i < 2000 && !host2.haveNetwork(); i += step) this_thread::sleep_for(chrono::milliseconds(step)); for (unsigned i = 0; i < 2000 && !jsonrpcServer->net_listening(); i += step) this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE(host2.haveNetwork()); BOOST_REQUIRE(jsonrpcServer->net_listening()); string node("enode://"); node += host2.id().hex(); node += "@"; node += "127.0.0.1:30339"; b = jsonrpcServer->admin_net_connect(node, sess2); for (unsigned i = 0; i < 3000 && !host2.peerCount(); i += step) this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE_EQUAL(host2.peerCount(), 1); this_thread::sleep_for(chrono::milliseconds(step)); j = jsonrpcServer->admin_net_peers(sess2); BOOST_REQUIRE_EQUAL(j.size(), 1); Json::Value peer = j[0]; s = peer["id"].asString(); BOOST_REQUIRE_EQUAL(s, host2.id().hex()); BOOST_REQUIRE_EQUAL(peer["port"].asUInt(), port2); s = jsonrpcServer->net_peerCount(); BOOST_REQUIRE_EQUAL(s, "0x1"); KeyPair src = KeyPair::create(); KeyPair dst = KeyPair::create(); Json::Value t1 = createMessage(toJS(src.address()), toJS(dst.address())); b = jsonrpcServer->shh_post(t1); BOOST_REQUIRE(b); string const id = jsonrpcServer->shh_newIdentity(); BOOST_REQUIRE_EQUAL(id.size(), 130); BOOST_REQUIRE(jsonrpcServer->shh_hasIdentity(id)); Json::Value t2 = createMessage(id, id); b = jsonrpcServer->shh_post(t2); BOOST_REQUIRE(b); string const nonexistent = "123456789"; b = jsonrpcServer->shh_uninstallFilter(nonexistent); BOOST_REQUIRE(b); j = jsonrpcServer->shh_getMessages(nonexistent); BOOST_REQUIRE(j.empty()); string const topic = "unicorns"; Json::Value t(Json::arrayValue); t.append(topic); Json::Value f; f["to"] = id; f["topics"] = t; string const filter = jsonrpcServer->shh_newFilter(f); j = jsonrpcServer->shh_getFilterChanges(filter); BOOST_REQUIRE(j.empty()); j = jsonrpcServer->shh_getMessages(filter); BOOST_REQUIRE(j.empty()); Json::Value msg = createMessage(id, id, topic, text); b = jsonrpcServer->shh_post(msg); BOOST_REQUIRE(b); this_thread::sleep_for(chrono::milliseconds(50)); j = jsonrpcServer->shh_getFilterChanges(filter); BOOST_REQUIRE(!j.empty()); Json::Value m1 = j[0]; BOOST_REQUIRE_EQUAL(m1["ttl"], toJS(c_ttl)); BOOST_REQUIRE_EQUAL(m1["from"], id); BOOST_REQUIRE_EQUAL(m1["to"], id); BOOST_REQUIRE_EQUAL(m1["payload"], text); j = jsonrpcServer->shh_getMessages(filter); BOOST_REQUIRE(!j.empty()); Json::Value m2 = j[0]; BOOST_REQUIRE_EQUAL(m2["ttl"], toJS(c_ttl)); BOOST_REQUIRE_EQUAL(m2["from"], id); BOOST_REQUIRE_EQUAL(m2["to"], id); BOOST_REQUIRE_EQUAL(m2["payload"], text); j = jsonrpcServer->shh_getFilterChanges(filter); BOOST_REQUIRE(j.empty()); j = jsonrpcServer->shh_getMessages(filter); BOOST_REQUIRE(!j.empty()); m1 = j[0]; BOOST_REQUIRE_EQUAL(m1["ttl"], toJS(c_ttl)); BOOST_REQUIRE_EQUAL(m1["from"], id); BOOST_REQUIRE_EQUAL(m1["to"], id); BOOST_REQUIRE_EQUAL(m1["payload"], text); msg = createMessage(id, id, topic); b = jsonrpcServer->shh_post(msg); BOOST_REQUIRE(b); this_thread::sleep_for(chrono::milliseconds(50)); j = jsonrpcServer->shh_getFilterChanges(filter); BOOST_REQUIRE_EQUAL(j.size(), 1); j = jsonrpcServer->shh_getMessages(filter); BOOST_REQUIRE_EQUAL(j.size(), 2); b = jsonrpcServer->shh_uninstallFilter(filter); BOOST_REQUIRE(b); j = jsonrpcServer->shh_getFilterChanges(filter); BOOST_REQUIRE(j.empty()); j = jsonrpcServer->shh_getMessages(filter); BOOST_REQUIRE(j.empty()); msg = createMessage(id, id, topic); b = jsonrpcServer->shh_post(msg); BOOST_REQUIRE(b); this_thread::sleep_for(chrono::milliseconds(50)); j = jsonrpcServer->shh_getFilterChanges(filter); BOOST_REQUIRE(j.empty()); j = jsonrpcServer->shh_getMessages(filter); BOOST_REQUIRE(j.empty()); b = jsonrpcServer->admin_net_stop(sess2); BOOST_REQUIRE(b); b = jsonrpcServer->net_listening(); BOOST_REQUIRE(!b); } BOOST_AUTO_TEST_SUITE_END()