Browse Source

test encoding/decoding neighbors. add MAC.

cl-refactor
subtly 10 years ago
parent
commit
bf05c50c78
  1. 21
      libp2p/NodeTable.cpp
  2. 24
      libp2p/NodeTable.h
  3. 33
      libp2p/UDP.cpp
  4. 101
      test/net.cpp

21
libp2p/NodeTable.cpp

@ -235,7 +235,7 @@ void NodeTable::noteNode(Public const& _pubk, bi::udp::endpoint const& _endpoint
{
Address id = right160(sha3(_pubk));
// Don't add ourself (would result in -1 bucket lookup)
// Don't add ourself
if (id == m_node.address())
return;
@ -303,18 +303,27 @@ NodeTable::NodeBucket& NodeTable::bucket(NodeEntry const* _n)
void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet)
{
if (_packet.size() < 69)
// h256 + Signature + RLP
if (_packet.size() < 100)
{
clog(NodeTableMessageSummary) << "Invalid Message received from " << _from.address().to_string() << ":" << _from.port();
clog(NodeTableMessageSummary) << "Invalid Message size received from " << _from.address().to_string() << ":" << _from.port();
return;
}
bytesConstRef signedBytes(_packet.cropped(h256::size, _packet.size() - h256::size));
h256 hashSigned(sha3(signedBytes));
if (!_packet.cropped(0, h256::size).contentsEqual(hashSigned.asBytes()))
{
clog(NodeTableMessageSummary) << "Invalid Message hash received from " << _from.address().to_string() << ":" << _from.port();
return;
}
// 3 items is PingNode, 2 items w/no lists is FindNode, 2 items w/first item as list is Neighbors, 1 item is Pong
bytesConstRef rlpBytes(_packet.cropped(65, _packet.size() - 65));
bytesConstRef rlpBytes(signedBytes.cropped(Signature::size, signedBytes.size() - Signature::size));
RLP rlp(rlpBytes);
unsigned itemCount = rlp.itemCount();
bytesConstRef sigBytes(_packet.cropped(0, 65));
bytesConstRef sigBytes(_packet.cropped(h256::size, Signature::size));
Public nodeid(dev::recover(*(Signature const*)sigBytes.data(), sha3(rlpBytes)));
if (!nodeid)
{
@ -339,8 +348,8 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
if (rlp[0].isList())
{
// todo: chunk neighbors packet
clog(NodeTableMessageSummary) << "Received Neighbors from " << _from.address().to_string() << ":" << _from.port();
Neighbors in = Neighbors::fromBytesConstRef(_from, rlpBytes);
clog(NodeTableMessageSummary) << "Received " << in.nodes.size() << " Neighbors from " << _from.address().to_string() << ":" << _from.port();
for (auto n: in.nodes)
noteNode(n.node, bi::udp::endpoint(bi::address::from_string(n.ipAddress), n.port));
}

24
libp2p/NodeTable.h

@ -104,6 +104,8 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this<NodeTable>
};
public:
NodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _port = 30300);
~NodeTable();
/// Constants for Kademlia, mostly derived from address space.
@ -116,7 +118,6 @@ public:
static constexpr unsigned s_bucketSize = 16; ///< Denoted by k in [Kademlia]. Number of nodes stored in each bucket.
static constexpr unsigned s_alpha = 3; ///< Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests.
static constexpr uint16_t s_defaultPort = 30300; ///< Default port to listen on.
/// Intervals
@ -125,9 +126,6 @@ public:
std::chrono::seconds const c_bucketRefresh = std::chrono::seconds(3600); ///< Refresh interval prevents bucket from becoming stale. [Kademlia]
static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; }
NodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _port = s_defaultPort);
~NodeTable();
void join();
@ -233,9 +231,9 @@ struct PingNode: RLPXDatagram<PingNode>
using RLPXDatagram::RLPXDatagram;
PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _expiration = std::chrono::seconds(60)): RLPXDatagram(_ep), ipAddress(_src), port(_srcPort), expiration(futureFromEpoch(_expiration)) {}
std::string ipAddress; // rlp encoded bytes min: 16
unsigned port; // rlp encoded bytes min: 1 max: 3
unsigned expiration; // rlp encoded bytes min: 1 max: 9
std::string ipAddress;
unsigned port;
unsigned expiration;
void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << expiration; }
void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = r[0].toString(); port = r[1].toInt<unsigned>(); expiration = r[2].toInt<unsigned>(); }
@ -248,6 +246,7 @@ struct PingNode: RLPXDatagram<PingNode>
* Minimum Encoded Size: 33 bytes
* Maximum Encoded Size: 33 bytes
*
* @todo expiration
* @todo value of replyTo
* @todo create from PingNode (reqs RLPXDatagram verify flag)
*/
@ -256,6 +255,7 @@ struct Pong: RLPXDatagram<Pong>
using RLPXDatagram::RLPXDatagram;
h256 replyTo; // hash of rlp of PingNode
unsigned expiration;
void streamRLP(RLPStream& _s) const { _s.appendList(1); _s << replyTo; }
void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); replyTo = (h256)r[0]; }
@ -292,7 +292,7 @@ struct FindNode: RLPXDatagram<FindNode>
* RLP Encoded Items: 2 (first item is list)
* Minimum Encoded Size: 10 bytes
*
* @todo nonce
* @todo nonce: Should be replaced with expiration.
*/
struct Neighbors: RLPXDatagram<Neighbors>
{
@ -312,7 +312,7 @@ struct Neighbors: RLPXDatagram<Neighbors>
};
using RLPXDatagram::RLPXDatagram;
Neighbors(bi::udp::endpoint _to, std::vector<std::shared_ptr<NodeTable::NodeEntry>> const& _nearest): RLPXDatagram(_to), nonce(h256())
Neighbors(bi::udp::endpoint _to, std::vector<std::shared_ptr<NodeTable::NodeEntry>> const& _nearest): RLPXDatagram(_to)
{
for (auto& n: _nearest)
{
@ -325,11 +325,11 @@ struct Neighbors: RLPXDatagram<Neighbors>
}
std::list<Node> nodes;
h256 nonce;
unsigned expiration = 1;
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << 1; }
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << expiration; }
void interpretRLP(bytesConstRef _bytes) {
RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n)); nonce = (h256)r[1];
RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n)); expiration = r[1].toInt<unsigned>();
}
};

33
libp2p/UDP.cpp

@ -23,23 +23,32 @@
using namespace dev;
using namespace dev::p2p;
//template <class T>
h256 RLPXDatagramFace::sign(Secret const& _k)
{
RLPStream packet;
streamRLP(packet);
bytes b(packet.out());
h256 h(dev::sha3(b));
Signature sig = dev::sign(_k, h);
data.resize(b.size() + Signature::size);
sig.ref().copyTo(&data);
memcpy(data.data() + sizeof(Signature), b.data(), b.size());
return std::move(h);
RLPStream rlpstream;
streamRLP(rlpstream);
bytes rlpBytes(rlpstream.out());
bytesConstRef rlp(&rlpBytes);
h256 hash(dev::sha3(rlp));
Signature sig = dev::sign(_k, hash);
data.resize(h256::size + Signature::size + rlp.size());
bytesConstRef packetHash(&data[0], h256::size);
bytesConstRef signedPayload(&data[h256::size], Signature::size + rlp.size());
bytesConstRef payloadSig(&data[h256::size], Signature::size);
bytesConstRef payload(&data[h256::size+Signature::size], rlp.size());
sig.ref().copyTo(payloadSig);
rlp.copyTo(payload);
dev::sha3(signedPayload).ref().copyTo(packetHash);
return std::move(hash);
};
//template <class T>
Public RLPXDatagramFace::authenticate(bytesConstRef _sig, bytesConstRef _rlp)
{
Signature const& sig = *(Signature const*)_sig.data();
return std::move(dev::recover(sig, sha3(_rlp)));
};
};

101
test/net.cpp

@ -53,7 +53,23 @@ struct TestNodeTable: public NodeTable
/// Constructor
using NodeTable::NodeTable;
void pingAll(std::vector<std::pair<KeyPair,unsigned>> const& _testNodes)
static std::vector<std::pair<KeyPair,unsigned>> createTestNodes(int _count = 16)
{
std::vector<std::pair<KeyPair,unsigned>> ret;
asserts(_count < 1000);
static uint16_t s_basePort = 30500;
ret.clear();
for (auto i = 0; i < _count; i++)
{
KeyPair k = KeyPair::create();
ret.push_back(make_pair(k,s_basePort+i));
}
return std::move(ret);
}
void pingTestNodes(std::vector<std::pair<KeyPair,unsigned>> const& _testNodes)
{
bi::address ourIp = bi::address::from_string("127.0.0.1");
for (auto& n: _testNodes)
@ -63,7 +79,7 @@ struct TestNodeTable: public NodeTable
}
}
void populate(std::vector<std::pair<KeyPair,unsigned>> const& _testNodes, size_t _count = 0)
void populateTestNodes(std::vector<std::pair<KeyPair,unsigned>> const& _testNodes, size_t _count = 0)
{
if (!_count)
_count = _testNodes.size();
@ -88,44 +104,19 @@ struct TestNodeTable: public NodeTable
*/
struct TestNodeTableHost: public TestHost
{
TestNodeTableHost(): m_alias(KeyPair::create()), nodeTable(new TestNodeTable(m_io, m_alias)) {};
TestNodeTableHost(): m_alias(KeyPair::create()), nodeTable(new TestNodeTable(m_io, m_alias)), testNodes(TestNodeTable::createTestNodes()) {};
~TestNodeTableHost() { m_io.stop(); stopWorking(); }
void generateTestNodes(int _count = 16)
{
asserts(_count < 1000);
static uint16_t s_basePort = 30500;
m_testNodes.clear();
for (auto i = 0; i < _count; i++)
{
KeyPair k = KeyPair::create();
m_testNodes.push_back(make_pair(k,s_basePort+i));
testNodes.push_back(make_shared<TestNodeTable>(m_io,k,s_basePort+i));
}
}
std::vector<std::pair<KeyPair,unsigned>> m_testNodes; // keypair and port
void setup()
{
generateTestNodes();
}
void setup() { for (auto n: testNodes) nodeTables.push_back(make_shared<TestNodeTable>(m_io,n.first,n.second)); }
void pingAll()
{
nodeTable->pingAll(m_testNodes);
// for (auto& n: testNodes)
// n->pingAll(m_testNodes);
}
void pingAll() { for (auto& t: nodeTables) t->pingTestNodes(testNodes); }
void populate(size_t _count = 0)
{
nodeTable->populate(m_testNodes, _count);
}
void populate(size_t _count = 0) { nodeTable->populateTestNodes(testNodes, _count); }
KeyPair m_alias;
shared_ptr<TestNodeTable> nodeTable;
std::vector<shared_ptr<TestNodeTable>> testNodes;
std::vector<std::pair<KeyPair,unsigned>> testNodes; // keypair and port
std::vector<shared_ptr<TestNodeTable>> nodeTables;
};
class TestUDPSocket: UDPSocketEvents, public TestHost
@ -141,6 +132,36 @@ public:
bool success = false;
};
BOOST_AUTO_TEST_CASE(test_neighbors_packet)
{
KeyPair k = KeyPair::create();
std::vector<std::pair<KeyPair,unsigned>> testNodes(TestNodeTable::createTestNodes());
bi::udp::endpoint to(boost::asio::ip::address::from_string("127.0.0.1"), 30000);
Neighbors out(to);
for (auto n: testNodes)
{
Neighbors::Node node;
node.ipAddress = boost::asio::ip::address::from_string("127.0.0.1").to_string();
node.port = n.second;
node.node = n.first.pub();
out.nodes.push_back(node);
}
out.sign(k.sec());
bytesConstRef packet(out.data.data(), out.data.size());
bytesConstRef rlpBytes(packet.cropped(97, packet.size() - 97));
Neighbors in = Neighbors::fromBytesConstRef(to, rlpBytes);
int count = 0;
for (auto n: in.nodes)
{
BOOST_REQUIRE_EQUAL(testNodes[count].second, n.port);
BOOST_REQUIRE_EQUAL(testNodes[count].first.pub(), n.node);
BOOST_REQUIRE_EQUAL(sha3(testNodes[count].first.pub()), sha3(n.node));
count++;
}
}
BOOST_AUTO_TEST_CASE(test_findnode_neighbors)
{
// Executing findNode should result in a list which is serialized
@ -154,21 +175,23 @@ BOOST_AUTO_TEST_CASE(kademlia)
node.start();
node.nodeTable->join(); // ideally, joining with empty node table logs warning we can check for
node.setup();
node.populate();
clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
node.pingAll();
this_thread::sleep_for(chrono::milliseconds(4000));
clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
this_thread::sleep_for(chrono::milliseconds(10000));
node.nodeTable->reset();
clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
node.populate(2);
clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
this_thread::sleep_for(chrono::milliseconds(500));
clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
// node.nodeTable->join();
// this_thread::sleep_for(chrono::milliseconds(2000));
//
// clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
node.nodeTable->join();
this_thread::sleep_for(chrono::milliseconds(2000));
clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
}
BOOST_AUTO_TEST_CASE(test_udp_once)

Loading…
Cancel
Save