mirror of https://github.com/libp2p/cpp-libp2p.git
Akvinikym
5 years ago
committed by
GitHub
130 changed files with 1523 additions and 859 deletions
@ -0,0 +1,19 @@ |
|||
# |
|||
# Copyright Soramitsu Co., Ltd. All Rights Reserved. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
# |
|||
|
|||
add_executable(libp2p_echo_server |
|||
libp2p_echo_server.cpp |
|||
) |
|||
|
|||
target_link_libraries(libp2p_echo_server |
|||
p2p_basic_host |
|||
p2p_default_network |
|||
p2p_peer_repository |
|||
p2p_inmem_address_repository |
|||
p2p_inmem_key_repository |
|||
p2p_inmem_protocol_repository |
|||
p2p_protocol_echo |
|||
p2p_literals |
|||
) |
@ -0,0 +1,10 @@ |
|||
# Example Libp2p Echo Server |
|||
## To be moved into a separate repo and turned into a test |
|||
|
|||
In order to build the C++ Libp2p Echo Server and test its compatibility with the Go Libp2p Echo Client, do the following: |
|||
|
|||
1. Build a C++ target |
|||
2. Build a Go example via `go build libp2p_client.go` |
|||
3. Start a C++ server. It will run for some number of seconds |
|||
4. Execute `./libp2p_client -insecure -l 40011 -d /ip4/127.0.0.1/tcp/40010/ipfs/12D3KooWLs7RC93EGXZzn9YdKyZYYx3f9UjTLYNX1reThpCkFb83` |
|||
5. Watch how "Hello, world!" is exchanged between server and client |
@ -0,0 +1,183 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"bufio" |
|||
"context" |
|||
"crypto/rand" |
|||
"flag" |
|||
"fmt" |
|||
"io" |
|||
"io/ioutil" |
|||
"log" |
|||
mrand "math/rand" |
|||
|
|||
"github.com/libp2p/go-libp2p" |
|||
"github.com/libp2p/go-libp2p-core/crypto" |
|||
"github.com/libp2p/go-libp2p-core/host" |
|||
"github.com/libp2p/go-libp2p-core/network" |
|||
"github.com/libp2p/go-libp2p-core/peer" |
|||
"github.com/libp2p/go-libp2p-core/peerstore" |
|||
|
|||
golog "github.com/ipfs/go-log" |
|||
ma "github.com/multiformats/go-multiaddr" |
|||
) |
|||
|
|||
// makeBasicHost creates a LibP2P host with a random peer ID listening on the
|
|||
// given multiaddress. It won't encrypt the connection if insecure is true.
|
|||
func makeBasicHost(listenPort int, insecure bool, randseed int64) (host.Host, error) { |
|||
|
|||
// If the seed is zero, use real cryptographic randomness. Otherwise, use a
|
|||
// deterministic randomness source to make generated keys stay the same
|
|||
// across multiple runs
|
|||
var r io.Reader |
|||
if randseed == 0 { |
|||
r = rand.Reader |
|||
} else { |
|||
r = mrand.New(mrand.NewSource(randseed)) |
|||
} |
|||
|
|||
// Generate a key pair for this host. We will use it at least
|
|||
// to obtain a valid host ID.
|
|||
priv, p, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, r) |
|||
log.Println("============ %s", p) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
opts := []libp2p.Option{ |
|||
libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort)), |
|||
libp2p.Identity(priv), |
|||
libp2p.DisableRelay(), |
|||
} |
|||
|
|||
if insecure { |
|||
opts = append(opts, libp2p.NoSecurity) |
|||
} |
|||
|
|||
basicHost, err := libp2p.New(context.Background(), opts...) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
// Build host multiaddress
|
|||
hostAddr, _ := ma.NewMultiaddr(fmt.Sprintf("/ipfs/%s", basicHost.ID().Pretty())) |
|||
|
|||
// Now we can build a full multiaddress to reach this host
|
|||
// by encapsulating both addresses:
|
|||
addr := basicHost.Addrs()[0] |
|||
fullAddr := addr.Encapsulate(hostAddr) |
|||
log.Printf("I am %s\n", fullAddr) |
|||
if insecure { |
|||
log.Printf("Now run \"./echo -l %d -d %s -insecure\" on a different terminal\n", listenPort+1, fullAddr) |
|||
} else { |
|||
log.Printf("Now run \"./echo -l %d -d %s\" on a different terminal\n", listenPort+1, fullAddr) |
|||
} |
|||
|
|||
return basicHost, nil |
|||
} |
|||
|
|||
func main() { |
|||
// LibP2P code uses golog to log messages. They log with different
|
|||
// string IDs (i.e. "swarm"). We can control the verbosity level for
|
|||
// all loggers with:
|
|||
// golog.SetAllLoggers(golog.LevelInfo) // Change to DEBUG for extra info
|
|||
golog.SetDebugLogging() |
|||
|
|||
// Parse options from the command line
|
|||
listenF := flag.Int("l", 0, "wait for incoming connections") |
|||
target := flag.String("d", "", "target peer to dial") |
|||
insecure := flag.Bool("insecure", false, "use an unencrypted connection") |
|||
seed := flag.Int64("seed", 0, "set random seed for id generation") |
|||
flag.Parse() |
|||
|
|||
if *listenF == 0 { |
|||
log.Fatal("Please provide a port to bind on with -l") |
|||
} |
|||
|
|||
// Make a host that listens on the given multiaddress
|
|||
ha, err := makeBasicHost(*listenF, *insecure, *seed) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
|
|||
// Set a stream handler on host A. /echo/1.0.0 is
|
|||
// a user-defined protocol name.
|
|||
ha.SetStreamHandler("/echo/1.0.0", func(s network.Stream) { |
|||
log.Println("Got a new stream!") |
|||
if err := doEcho(s); err != nil { |
|||
log.Println(err) |
|||
s.Reset() |
|||
} else { |
|||
s.Close() |
|||
} |
|||
}) |
|||
|
|||
if *target == "" { |
|||
log.Println("listening for connections") |
|||
select {} // hang forever
|
|||
} |
|||
/**** This is where the listener code ends ****/ |
|||
|
|||
// The following code extracts target's the peer ID from the
|
|||
// given multiaddress
|
|||
ipfsaddr, err := ma.NewMultiaddr(*target) |
|||
if err != nil { |
|||
log.Fatalln(err) |
|||
} |
|||
|
|||
pid, err := ipfsaddr.ValueForProtocol(ma.P_IPFS) |
|||
if err != nil { |
|||
log.Fatalln(err) |
|||
} |
|||
|
|||
peerid, err := peer.IDB58Decode(pid) |
|||
if err != nil { |
|||
log.Fatalln(err) |
|||
} |
|||
|
|||
// Decapsulate the /ipfs/<peerID> part from the target
|
|||
// /ip4/<a.b.c.d>/ipfs/<peer> becomes /ip4/<a.b.c.d>
|
|||
targetPeerAddr, _ := ma.NewMultiaddr( |
|||
fmt.Sprintf("/ipfs/%s", peer.IDB58Encode(peerid))) |
|||
targetAddr := ipfsaddr.Decapsulate(targetPeerAddr) |
|||
|
|||
// We have a peer ID and a targetAddr so we add it to the peerstore
|
|||
// so LibP2P knows how to contact it
|
|||
ha.Peerstore().AddAddr(peerid, targetAddr, peerstore.PermanentAddrTTL) |
|||
|
|||
log.Println("opening stream") |
|||
// make a new stream from host B to host A
|
|||
// it should be handled on host A by the handler we set above because
|
|||
// we use the same /echo/1.0.0 protocol
|
|||
s, err := ha.NewStream(context.Background(), peerid, "/echo/1.0.0") |
|||
if err != nil { |
|||
log.Fatalln(err) |
|||
} |
|||
log.Println("opened stream") |
|||
|
|||
_, err = s.Write([]byte("Hello, world!\n")) |
|||
if err != nil { |
|||
log.Fatalln(err) |
|||
} |
|||
log.Println("written hello world") |
|||
|
|||
out, err := ioutil.ReadAll(s) |
|||
if err != nil { |
|||
log.Fatalln(err) |
|||
} |
|||
|
|||
log.Printf("read reply: %q\n", out) |
|||
} |
|||
|
|||
// doEcho reads a line of data a stream and writes it back
|
|||
func doEcho(s network.Stream) error { |
|||
buf := bufio.NewReader(s) |
|||
str, err := buf.ReadString('\n') |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
log.Printf("read: %s\n", str) |
|||
_, err = s.Write([]byte(str)) |
|||
return err |
|||
} |
@ -0,0 +1,64 @@ |
|||
/**
|
|||
* Copyright Soramitsu Co., Ltd. All Rights Reserved. |
|||
* SPDX-License-Identifier: Apache-2.0 |
|||
*/ |
|||
|
|||
#include <chrono> |
|||
#include <iostream> |
|||
#include <memory> |
|||
|
|||
#include <libp2p/common/literals.hpp> |
|||
#include <libp2p/host/basic_host.hpp> |
|||
#include <libp2p/injector/host_injector.hpp> |
|||
#include <libp2p/protocol/echo.hpp> |
|||
|
|||
int main() { |
|||
using libp2p::crypto::Key; |
|||
using libp2p::crypto::KeyPair; |
|||
using libp2p::crypto::PrivateKey; |
|||
using libp2p::crypto::PublicKey; |
|||
using libp2p::common::operator""_unhex; |
|||
|
|||
// this keypair generates a PeerId
|
|||
// "12D3KooWLs7RC93EGXZzn9YdKyZYYx3f9UjTLYNX1reThpCkFb83"
|
|||
KeyPair keypair{PublicKey{{Key::Type::Ed25519, |
|||
"a4249ea6d62bdd8bccf62257ac4899ff284796" |
|||
"3228b388fda288db5d64e517e0"_unhex}}, |
|||
PrivateKey{{Key::Type::Ed25519, |
|||
"4a9361c525840f7086b893d584ebbe475b4ec" |
|||
"7069951d2e897e8bceb0a3f35ce"_unhex}}}; |
|||
|
|||
// create a default Host via an injector, overriding a random-generated
|
|||
// keypair with ours
|
|||
auto injector = |
|||
libp2p::injector::makeHostInjector(libp2p::injector::useKeyPair(keypair)); |
|||
auto host = injector.create<std::shared_ptr<libp2p::Host>>(); |
|||
|
|||
// set a handler for Echo protocol
|
|||
libp2p::protocol::Echo echo{libp2p::protocol::EchoConfig{1}}; |
|||
host->setProtocolHandler( |
|||
echo.getProtocolId(), |
|||
[&echo](std::shared_ptr<libp2p::connection::Stream> received_stream) { |
|||
echo.handle(std::move(received_stream)); |
|||
}); |
|||
|
|||
// launch a Listener part of the Host
|
|||
auto context = injector.create<std::shared_ptr<boost::asio::io_context>>(); |
|||
context->post([host{std::move(host)}] { |
|||
auto ma = |
|||
libp2p::multi::Multiaddress::create("/ip4/127.0.0.1/tcp/40010").value(); |
|||
auto listen_res = host->listen(ma); |
|||
if (!listen_res) { |
|||
std::cerr << "host cannot listen the given multiaddress: " |
|||
<< listen_res.error().message() << "\n"; |
|||
std::exit(EXIT_FAILURE); |
|||
} |
|||
|
|||
host->start(); |
|||
std::cout << "Server started\nListening on: " << ma.getStringAddress() |
|||
<< "\nPeer id: " << host->getPeerInfo().id.toBase58() << "\n"; |
|||
}); |
|||
|
|||
// run the IO context
|
|||
context->run_for(std::chrono::seconds(5)); |
|||
} |
@ -0,0 +1,6 @@ |
|||
# |
|||
# Copyright Soramitsu Co., Ltd. All Rights Reserved. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
# |
|||
|
|||
add_subdirectory(01-echo) |
@ -0,0 +1,52 @@ |
|||
/**
|
|||
* Copyright Soramitsu Co., Ltd. All Rights Reserved. |
|||
* SPDX-License-Identifier: Apache-2.0 |
|||
*/ |
|||
|
|||
#ifndef LIBP2P_BYTEUTIL_HPP |
|||
#define LIBP2P_BYTEUTIL_HPP |
|||
|
|||
#include <cstdint> |
|||
#include <vector> |
|||
|
|||
namespace libp2p::common { |
|||
using ByteArray = std::vector<uint8_t>; |
|||
|
|||
/**
|
|||
* Put an 8-bit number into the byte array |
|||
*/ |
|||
ByteArray &putUint8(ByteArray &bytes, uint8_t n); |
|||
|
|||
/**
|
|||
* Put a 16-bit number into the byte array in Big Endian encoding |
|||
*/ |
|||
ByteArray &putUint16BE(ByteArray &bytes, uint16_t n); |
|||
|
|||
/**
|
|||
* Put an 32-bit number into the byte array in Big Endian encoding |
|||
*/ |
|||
ByteArray &putUint32BE(ByteArray &bytes, uint32_t n); |
|||
|
|||
/**
|
|||
* Put an 64-bit number into the byte array in Big Endian encoding |
|||
*/ |
|||
ByteArray &putUint64BE(ByteArray &bytes, uint64_t n); |
|||
|
|||
/**
|
|||
* Convert value, to which the pointer (\param v) references, to the value of |
|||
* (\tparam T) |
|||
* |
|||
* @example |
|||
* std::vector<uint8_t> bytes_of_uint16{0x2A, 0x08}; |
|||
* uint16_t converted_value = convert<uint16_t>(bytes_of_uint16.data()); |
|||
* printf(converted_value); // 10760 (2A08 in hex)
|
|||
*/ |
|||
template <typename T> |
|||
T convert(const void *v) { |
|||
T t; |
|||
memcpy(&t, v, sizeof(T)); |
|||
return t; |
|||
} |
|||
} // namespace libp2p::common
|
|||
|
|||
#endif // LIBP2P_BYTEUTIL_HPP
|
@ -0,0 +1,41 @@ |
|||
/**
|
|||
* Copyright Soramitsu Co., Ltd. All Rights Reserved. |
|||
* SPDX-License-Identifier: Apache-2.0 |
|||
*/ |
|||
|
|||
#ifndef LIBP2P_LITERALS_HPP |
|||
#define LIBP2P_LITERALS_HPP |
|||
|
|||
#include <cstdint> |
|||
#include <vector> |
|||
|
|||
#include <libp2p/common/types.hpp> |
|||
|
|||
namespace libp2p { |
|||
|
|||
namespace multi { |
|||
class Multiaddress; |
|||
class Multihash; |
|||
} // namespace multi
|
|||
|
|||
namespace peer { |
|||
class PeerId; |
|||
} |
|||
|
|||
namespace common { |
|||
Hash256 operator""_hash256(const char *c, size_t s); |
|||
|
|||
std::vector<uint8_t> operator""_v(const char *c, size_t s); |
|||
|
|||
std::vector<uint8_t> operator""_unhex(const char *c, size_t s); |
|||
|
|||
multi::Multiaddress operator""_multiaddr(const char *c, size_t s); |
|||
|
|||
multi::Multihash operator""_multihash(const char *c, size_t s); |
|||
|
|||
peer::PeerId operator""_peerid(const char *c, size_t s); |
|||
} // namespace common
|
|||
|
|||
} // namespace libp2p
|
|||
|
|||
#endif // LIBP2P_LITERALS_HPP
|
@ -0,0 +1,29 @@ |
|||
/**
|
|||
* Copyright Soramitsu Co., Ltd. All Rights Reserved. |
|||
* SPDX-License-Identifier: Apache-2.0 |
|||
*/ |
|||
|
|||
#ifndef KAGOME_PROTOBUF_KEY_HPP |
|||
#define KAGOME_PROTOBUF_KEY_HPP |
|||
|
|||
#include <cstdint> |
|||
#include <vector> |
|||
|
|||
#include <boost/operators.hpp> |
|||
|
|||
namespace libp2p::crypto { |
|||
/**
|
|||
* Strict type for key, which is encoded into Protobuf format |
|||
*/ |
|||
struct ProtobufKey : public boost::equality_comparable<ProtobufKey> { |
|||
explicit ProtobufKey(std::vector<uint8_t> key) : key{std::move(key)} {} |
|||
|
|||
std::vector<uint8_t> key; |
|||
|
|||
bool operator==(const ProtobufKey &other) const { |
|||
return key == other.key; |
|||
} |
|||
}; |
|||
} // namespace libp2p::crypto
|
|||
|
|||
#endif // KAGOME_PROTOBUF_KEY_HPP
|
@ -0,0 +1,32 @@ |
|||
/**
|
|||
* Copyright Soramitsu Co., Ltd. All Rights Reserved. |
|||
* SPDX-License-Identifier: Apache-2.0 |
|||
*/ |
|||
|
|||
#include <libp2p/common/byteutil.hpp> |
|||
|
|||
namespace libp2p::common { |
|||
ByteArray &putUint8(ByteArray &bytes, uint8_t n) { |
|||
bytes.push_back(n); |
|||
return bytes; |
|||
} |
|||
|
|||
ByteArray &putUint16BE(ByteArray &bytes, uint16_t n) { |
|||
bytes.push_back(static_cast<unsigned char &&>((n >> 8u) & 0xFF)); |
|||
return putUint8(bytes, n); |
|||
} |
|||
|
|||
ByteArray &putUint32BE(ByteArray &bytes, uint32_t n) { |
|||
bytes.push_back(static_cast<unsigned char &&>((n >> 24u) & 0xFF)); |
|||
bytes.push_back(static_cast<unsigned char &&>((n >> 16u) & 0xFF)); |
|||
return putUint16BE(bytes, n); |
|||
} |
|||
|
|||
ByteArray &putUint64BE(ByteArray &bytes, uint64_t n) { |
|||
bytes.push_back(static_cast<unsigned char &&>((n >> 56u) & 0xFF)); |
|||
bytes.push_back(static_cast<unsigned char &&>((n >> 48u) & 0xFF)); |
|||
bytes.push_back(static_cast<unsigned char &&>((n >> 40u) & 0xFF)); |
|||
bytes.push_back(static_cast<unsigned char &&>((n >> 32u) & 0xFF)); |
|||
return putUint32BE(bytes, n); |
|||
} |
|||
} // namespace libp2p::common
|
@ -0,0 +1,46 @@ |
|||
/**
|
|||
* Copyright Soramitsu Co., Ltd. All Rights Reserved. |
|||
* SPDX-License-Identifier: Apache-2.0 |
|||
*/ |
|||
|
|||
#include <libp2p/common/literals.hpp> |
|||
|
|||
#include <libp2p/common/hexutil.hpp> |
|||
#include <libp2p/crypto/protobuf/protobuf_key.hpp> |
|||
#include <libp2p/multi/multiaddress.hpp> |
|||
#include <libp2p/multi/multihash.hpp> |
|||
#include <libp2p/peer/peer_id.hpp> |
|||
|
|||
namespace libp2p::common { |
|||
libp2p::common::Hash256 operator""_hash256(const char *c, size_t s) { |
|||
libp2p::common::Hash256 hash{}; |
|||
std::copy_n(c, std::min(s, 32ul), hash.rbegin()); |
|||
return hash; |
|||
} |
|||
|
|||
std::vector<uint8_t> operator""_v(const char *c, size_t s) { |
|||
std::vector<uint8_t> chars(c, c + s); // NOLINT
|
|||
return chars; |
|||
} |
|||
|
|||
std::vector<uint8_t> operator""_unhex(const char *c, size_t s) { |
|||
return libp2p::common::unhex(std::string_view(c, s)).value(); |
|||
} |
|||
|
|||
libp2p::multi::Multiaddress operator""_multiaddr(const char *c, size_t s) { |
|||
return libp2p::multi::Multiaddress::create(std::string_view(c, s)).value(); |
|||
} |
|||
|
|||
libp2p::multi::Multihash operator""_multihash(const char *c, size_t s) { |
|||
return libp2p::multi::Multihash::createFromHex(std::string_view(c, s)) |
|||
.value(); |
|||
} |
|||
|
|||
libp2p::peer::PeerId operator""_peerid(const char *c, size_t s) { |
|||
libp2p::crypto::PublicKey p; |
|||
p.data = std::vector<uint8_t>(c, c + s); // NOLINT
|
|||
return libp2p::peer::PeerId::fromPublicKey( |
|||
libp2p::crypto::ProtobufKey{p.data}) |
|||
.value(); |
|||
} |
|||
} // namespace libp2p::common
|
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue