-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from perun-network/libp2p_wire
feat: libp2p wire/net Bus implementation
- Loading branch information
Showing
12 changed files
with
1,266 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module github.com/perun-network/perun-libp2p-wire | ||
|
||
go 1.16 | ||
|
||
require ( | ||
github.com/libp2p/go-libp2p v0.13.0 | ||
github.com/libp2p/go-libp2p-core v0.8.5 | ||
github.com/multiformats/go-multiaddr v0.3.3 | ||
github.com/pkg/errors v0.9.1 | ||
github.com/stretchr/testify v1.9.0 | ||
perun.network/go-perun v0.11.0 | ||
polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package p2p | ||
|
||
import ( | ||
"context" | ||
"crypto/sha256" | ||
"math/rand" | ||
|
||
"github.com/libp2p/go-libp2p" | ||
"github.com/libp2p/go-libp2p-core/crypto" | ||
"github.com/libp2p/go-libp2p-core/host" | ||
"github.com/pkg/errors" | ||
"perun.network/go-perun/wire" | ||
) | ||
|
||
// Account represents a libp2p wire account. | ||
type Account struct { | ||
host.Host | ||
privateKey crypto.PrivKey | ||
} | ||
|
||
// Address returns the account's address. | ||
func (acc *Account) Address() wire.Address { | ||
return &Address{acc.ID()} | ||
} | ||
|
||
// Sign signs the given message with the account's private key. | ||
func (acc *Account) Sign(data []byte) ([]byte, error) { | ||
// Extract the private key from the account. | ||
if acc.privateKey == nil { | ||
return nil, errors.New("private key not set") | ||
} | ||
hashed := sha256.Sum256(data) | ||
|
||
signature, err := acc.privateKey.Sign(hashed[:]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return signature, nil | ||
|
||
} | ||
|
||
// NewRandomAccount generates a new random account. | ||
func NewRandomAccount(rng *rand.Rand) *Account { | ||
// Creates a new RSA key pair for this host. | ||
prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, rng) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
// Construct a new libp2p client for our relay-server. | ||
// Identity(prvKey) - Use a RSA private key to generate the ID of the host. | ||
// EnableRelay() - Enable relay system and configures itself as a node behind a NAT | ||
client, err := libp2p.New( | ||
context.Background(), | ||
libp2p.Identity(prvKey), | ||
) | ||
|
||
if err != nil { | ||
panic(err) | ||
} | ||
return &Account{client, prvKey} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package p2p | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
pkgtest "polycry.pt/poly-go/test" | ||
) | ||
|
||
func TestNewAccount(t *testing.T) { | ||
rng := pkgtest.Prng(t) | ||
acc := NewRandomAccount(rng) | ||
assert.NotNil(t, acc) | ||
} | ||
|
||
func getHost(t *testing.T) *Account { | ||
rng := pkgtest.Prng(t) | ||
acc := NewRandomAccount(rng) | ||
assert.NotNil(t, acc) | ||
return acc | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package p2p | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
|
||
"github.com/libp2p/go-libp2p-core/crypto" | ||
"github.com/libp2p/go-libp2p-core/peer" | ||
"perun.network/go-perun/wire" | ||
) | ||
|
||
// Address is a peer address for wire discovery. | ||
type Address struct { | ||
peer.ID | ||
} | ||
|
||
// NewAddress returns a new address. | ||
func NewAddress(id peer.ID) *Address { | ||
return &Address{ID: id} | ||
} | ||
|
||
// Equal returns whether the two addresses are equal. | ||
func (a *Address) Equal(b wire.Address) bool { | ||
bTyped, ok := b.(*Address) | ||
if !ok { | ||
panic("wrong type") | ||
} | ||
|
||
return a.ID == bTyped.ID | ||
} | ||
|
||
// Cmp compares the byte representation of two addresses. For `a.Cmp(b)` | ||
// returns -1 if a < b, 0 if a == b, 1 if a > b. | ||
func (a *Address) Cmp(b wire.Address) int { | ||
bTyped, ok := b.(*Address) | ||
if !ok { | ||
panic("wrong type") | ||
} | ||
if a.ID < bTyped.ID { | ||
return -1 | ||
} else if a.ID == bTyped.ID { | ||
return 0 | ||
} | ||
return 1 | ||
|
||
} | ||
|
||
// NewRandomAddress returns a new random peer address. | ||
func NewRandomAddress(rng *rand.Rand) *Address { | ||
_, publicKey, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, rng) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
id, err := peer.IDFromPublicKey(publicKey) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return &Address{id} | ||
} | ||
|
||
// Verify verifies the signature of a message. | ||
func (a *Address) Verify(msg []byte, sig []byte) error { | ||
publicKey, err := a.ExtractPublicKey() | ||
if err != nil { | ||
return fmt.Errorf("extracting public key: %w", err) | ||
} | ||
|
||
b, err := publicKey.Verify(msg, sig) | ||
if err != nil { | ||
return fmt.Errorf("verifying signature: %w", err) | ||
} | ||
if b { | ||
return nil | ||
} | ||
return fmt.Errorf("signature verification failed") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package p2p_test | ||
|
||
import ( | ||
"math/rand" | ||
"testing" | ||
|
||
"github.com/perun-network/perun-libp2p-wire/p2p" | ||
"perun.network/go-perun/wire" | ||
"perun.network/go-perun/wire/test" | ||
) | ||
|
||
func TestAddress(t *testing.T) { | ||
test.TestAddressImplementation(t, func() wire.Address { | ||
return p2p.NewAddress("") | ||
}, func(rng *rand.Rand) wire.Address { | ||
return p2p.NewRandomAddress(rng) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package p2p | ||
|
||
import ( | ||
"github.com/libp2p/go-libp2p-core/host" | ||
"github.com/libp2p/go-libp2p-core/peer" | ||
"github.com/multiformats/go-multiaddr" | ||
"github.com/pkg/errors" | ||
"perun.network/go-perun/wire/net" | ||
perunio "perun.network/go-perun/wire/perunio/serializer" | ||
) | ||
|
||
// Net contains the client's components for the P2P communication. | ||
type Net struct { | ||
*net.Bus | ||
*Listener | ||
*Dialer | ||
MultiAddr string | ||
} | ||
|
||
// NewP2PBus creates a dialer, listener, and a bus for the given account `acc` | ||
// and includes them in the returned P2P Net. | ||
func NewP2PBus(acc *Account) (*Net, error) { | ||
listener := NewP2PListener(acc) | ||
dialer := NewP2PDialer(acc) | ||
bus := net.NewBus(acc, dialer, perunio.Serializer()) | ||
|
||
multiAddr, err := getHostMA(acc) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Net{Bus: bus, Dialer: dialer, Listener: listener, MultiAddr: multiAddr.String()}, nil | ||
} | ||
|
||
// getHostMA returns the first multiaddress of the given host. | ||
func getHostMA(host host.Host) (multiaddr.Multiaddr, error) { | ||
peerInfo := peer.AddrInfo{ | ||
ID: host.ID(), | ||
Addrs: host.Addrs(), | ||
} | ||
addrs, err := peer.AddrInfoToP2pAddrs(&peerInfo) | ||
return addrs[0], errors.Wrap(err, "converting peer info to multiaddress") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package p2p | ||
|
||
import ( | ||
"context" | ||
"sync" | ||
|
||
"github.com/libp2p/go-libp2p-core/host" | ||
"github.com/libp2p/go-libp2p-core/peer" | ||
ma "github.com/multiformats/go-multiaddr" | ||
"github.com/pkg/errors" | ||
"perun.network/go-perun/wire" | ||
wirenet "perun.network/go-perun/wire/net" | ||
pkgsync "polycry.pt/poly-go/sync" | ||
) | ||
|
||
// Dialer is a dialer for p2p connections. | ||
type Dialer struct { | ||
mutex sync.RWMutex // Protects peers. | ||
peers map[wire.AddrKey]string | ||
host host.Host | ||
closer pkgsync.Closer | ||
} | ||
|
||
// NewP2PDialer creates a new dialer for the given account. | ||
func NewP2PDialer(acc *Account) *Dialer { | ||
return &Dialer{ | ||
host: acc, | ||
peers: make(map[wire.AddrKey]string), | ||
} | ||
} | ||
|
||
// Dial implements Dialer.Dial(). | ||
func (d *Dialer) Dial(ctx context.Context, addr wire.Address, serializer wire.EnvelopeSerializer) (wirenet.Conn, error) { | ||
peerMA, ok := d.get(wire.Key(addr)) | ||
if !ok { | ||
return nil, errors.New("peer not found") | ||
} | ||
|
||
_peerMA, err := ma.NewMultiaddr(peerMA) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to parse multiaddress of peer") | ||
} | ||
|
||
peerAddrInfo, err := peer.AddrInfoFromP2pAddr(_peerMA) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "converting peer multiaddress to address info") | ||
} | ||
if err := d.host.Connect(ctx, *peerAddrInfo); err != nil { | ||
return nil, errors.Wrap(err, "failed to dial peer: failed to connecting to peer") | ||
} | ||
|
||
s, err := d.host.NewStream(ctx, peerAddrInfo.ID, "/client") | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to dial peer: failed to creating a new stream") | ||
} | ||
|
||
return wirenet.NewIoConn(s, serializer), nil | ||
} | ||
|
||
// Register registers a p2p multiaddress for a peer wire address. | ||
func (d *Dialer) Register(addr wire.Address, p2pAddress string) { | ||
d.mutex.Lock() | ||
defer d.mutex.Unlock() | ||
|
||
d.peers[wire.Key(addr)] = p2pAddress | ||
} | ||
|
||
// Close closes the dialer by closing the underlying libp2p host. | ||
func (d *Dialer) Close() error { | ||
if err := d.closer.Close(); err != nil { | ||
return err | ||
} | ||
return d.host.Close() | ||
} | ||
|
||
// get returns the p2p multiaddress for the given address if registered. | ||
func (d *Dialer) get(addr wire.AddrKey) (p2pAddress string, ok bool) { | ||
d.mutex.RLock() | ||
defer d.mutex.RUnlock() | ||
p2pAddress, ok = d.peers[addr] | ||
return | ||
} |
Oops, something went wrong.