Skip to content

Commit

Permalink
Merge pull request #4 from perun-network/libp2p_wire
Browse files Browse the repository at this point in the history
feat(account): add new APIs for interaction with server's AddressBook
  • Loading branch information
NhoxxKienn authored Jun 26, 2024
2 parents 6353887 + abb8801 commit 2ab2dc5
Show file tree
Hide file tree
Showing 3 changed files with 407 additions and 55 deletions.
58 changes: 20 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,47 +39,29 @@ Example usage:
The `Dialer` requires the other peers to be already "registered" to connect with them. Before dialing, the `Register` must be called.

Example (register peer before propose a channel with them):
```go
// OpenChannel opens a new channel with the specified peer and funding.
func (c *PaymentClient) OpenChannel(peer wire.Address, peerID string, amount float64) *PaymentChannel {
// We define the channel participants. The proposer has always index 0. Here
// we use the on-chain addresses as off-chain addresses, but we could also
// use different ones.
participants := []wire.Address{c.waddress, peer}

c.net.Dialer.Register(peer, peerID)

// We create an initial allocation which defines the starting balances.
initAlloc := channel.NewAllocation(2, c.currency)
initAlloc.SetAssetBalances(c.currency, []channel.Bal{
EthToWei(big.NewFloat(amount)), // Our initial balance.
big.NewInt(0), // Peer's initial balance.
})

// Prepare the channel proposal by defining the channel parameters.
challengeDuration := uint64(10) // On-chain challenge duration in seconds.
proposal, err := client.NewLedgerChannelProposal(
challengeDuration,
c.account,
initAlloc,
participants,
)
if err != nil {
panic(err)
}
````go
// Must be called at least once before attempting to connect with peer.
net.Dialer.Register(peer, peerID)
````

// Send the proposal.
ch, err := c.perunClient.ProposeChannel(context.TODO(), proposal)
if err != nil {
panic(err)
}
## Address Ressolver
A default address resolver was already built-in on the Perun-Relay-Server. You can use the provided APIs in order to `Register`, `Query`, `Deregister` your On-chain (L1) Address (Implementation of [Go-Perun] `wallet.Address`) to get the peer's `wire.Address` (`Peer.ID` of [Go-Libp2p])

// Start the on-chain event watcher. It automatically handles disputes.
c.startWatching(ch)
**Example:**
````go
// Should be used in the initialization of Perun-Client.
err := acc.RegisterOnChainAddress(onChainAddr)


// Query the peer's wire address, given its on-chain address.
peerID, err := acc.QueryOnChainAddress(peerOnChainAddr)


// Deregister the on-chain address, to be used before closing Perun-Client,
err = acc.DeregisterOnChainAddress(onChainAddr)

````

return newPaymentChannel(ch, c.currency)
}
```
## Test
Some unit tests are provided:
```
Expand Down
198 changes: 181 additions & 17 deletions p2p/account.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package p2p

import (
"bufio"
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"math/rand"
"net"

Expand All @@ -12,10 +15,17 @@ import (
"github.com/libp2p/go-libp2p-core/peer"
ma "github.com/multiformats/go-multiaddr"
"github.com/pkg/errors"
"perun.network/go-perun/wallet"
"perun.network/go-perun/wire"
)

const relayID = "QmVCPfUMr98PaaM8qbAQBgJ9jqc7XHpGp7AsyragdFDmgm"
const (
relayID = "QmVCPfUMr98PaaM8qbAQBgJ9jqc7XHpGp7AsyragdFDmgm"

queryProtocol = "/address-book/query/1.0.0" // Protocol for querying the relay-server for a peerID.
registerProtocol = "/address-book/register/1.0.0" // Protocol for registering an on-chain address with the relay-server.
removeProtocol = "/address-book/remove/1.0.0" // Protocol for deregistering an on-chain address with the relay-server.
)

// Account represents a libp2p wire account.
type Account struct {
Expand Down Expand Up @@ -45,29 +55,46 @@ func (acc *Account) Sign(data []byte) ([]byte, error) {

}

// NewRandomAccount generates a new random account.
func NewRandomAccount(rng *rand.Rand) *Account {
id, err := peer.Decode(relayID)
// MarshalPrivateKey marshals the account's private key to binary.
func (acc *Account) MarshalPrivateKey() ([]byte, error) {
return crypto.MarshalPrivateKey(acc.privateKey)
}

// NewAccountFromPrivateKeyBytes creates a new account from a given private key.
func NewAccountFromPrivateKeyBytes(prvKeyBytes []byte) (*Account, error) {
prvKey, err := crypto.UnmarshalPrivateKey(prvKeyBytes)
if err != nil {
err = errors.WithMessage(err, "decoding peer id of relay server")
return nil
return nil, errors.WithMessage(err, "unmarshalling private key")
}

// Get the IP address of the relay server.
ip, err := net.LookupIP("relay.perun.network")
relayInfo, relayAddr, err := getRelayServerInfo()
if err != nil {
panic(errors.WithMessage(err, "looking up IP address of relay.perun.network"))
panic(err)
}
relayAddr := "/ip4/" + ip[0].String() + "/tcp/5574"

relayMultiaddr, err := ma.NewMultiaddr(relayAddr)
// 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),
libp2p.EnableRelay(),
)
if err != nil {
panic(errors.WithMessage(err, "parsing relay multiadress"))
return nil, errors.WithMessage(err, "creating new libp2p client")
}

relayInfo := peer.AddrInfo{
ID: id,
Addrs: []ma.Multiaddr{relayMultiaddr},
if err := client.Connect(context.Background(), *relayInfo); err != nil {
client.Close()
return nil, errors.WithMessage(err, "connecting to the relay server")
}
return &Account{client, relayAddr, prvKey}, nil
}

// NewRandomAccount generates a new random account.
func NewRandomAccount(rng *rand.Rand) *Account {
relayInfo, relayAddr, err := getRelayServerInfo()
if err != nil {
panic(err)
}

// Creates a new RSA key pair for this host.
Expand All @@ -85,11 +112,148 @@ func NewRandomAccount(rng *rand.Rand) *Account {
libp2p.EnableRelay(),
)
if err != nil {
client.Close()
panic(err)
}

if err := client.Connect(context.Background(), relayInfo); err != nil {
if err := client.Connect(context.Background(), *relayInfo); err != nil {
client.Close()
panic(errors.WithMessage(err, "connecting to the relay server"))
}
return &Account{client, relayAddr, prvKey}
}

// RegisterOnChainAddress registers an on-chain address with the account to the relay-server's address book.
func (acc *Account) RegisterOnChainAddress(onChainAddr wallet.Address) error {
id, err := peer.Decode(relayID)
if err != nil {
err = errors.WithMessage(err, "decoding peer id of relay server")
return err
}

s, err := acc.NewStream(context.Background(), id, registerProtocol)
if err != nil {
return errors.WithMessage(err, "creating new stream")
}
defer s.Close()

var registerData struct {
OnChainAddress string
PeerID string
}
if onChainAddr == nil {
return errors.New("on-chain address is nil")
}
registerData.OnChainAddress = onChainAddr.String()
registerData.PeerID = acc.ID().String()

data, err := json.Marshal(registerData)
if err != nil {
return errors.WithMessage(err, "marshalling register data")
}

_, err = s.Write(data)
if err != nil {
return errors.WithMessage(err, "writing register data")
}

return nil
}

// Close closes the account.
func (acc *Account) Close() error {
return acc.Close()
}

// DeregisterOnChainAddress deregisters an on-chain address with the account from the relay-server's address book.
func (acc *Account) DeregisterOnChainAddress(onChainAddr wallet.Address) error {
relayInfo, _, err := getRelayServerInfo()
if err != nil {
return errors.WithMessage(err, "getting relay server info")
}

s, err := acc.NewStream(context.Background(), relayInfo.ID, removeProtocol)
if err != nil {
return errors.WithMessage(err, "creating new stream")
}
defer s.Close()

var unregisterData struct {
OnChainAddress string
PeerID string
}
unregisterData.OnChainAddress = onChainAddr.String()
unregisterData.PeerID = acc.ID().String()

data, err := json.Marshal(unregisterData)
if err != nil {
return errors.WithMessage(err, "marshalling register data")
}

_, err = s.Write(data)
if err != nil {
return errors.WithMessage(err, "writing register data")
}

return nil
}

// QueryOnChainAddress queries the relay-server for the peerID of a peer given its on-chain address.
func (acc *Account) QueryOnChainAddress(onChainAddr wallet.Address) (*Address, error) {
id, err := peer.Decode(relayID)
if err != nil {
err = errors.WithMessage(err, "decoding peer id of relay server")
return nil, err
}

s, err := acc.NewStream(context.Background(), id, queryProtocol)
if err != nil {
return nil, errors.WithMessage(err, "creating new stream")
}
defer s.Close()

rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))
rw.WriteString(fmt.Sprintf("%s\n", onChainAddr))
rw.Flush()

str, _ := rw.ReadString('\n')
if str == "" {
return nil, errors.New("empty response from relay server")
}
peerIDstr := str[:len(str)-1]
peerID, err := peer.Decode(peerIDstr)
if err != nil {
return nil, errors.WithMessage(err, "decoding peer id")
}

return &Address{peerID}, nil
}

func getRelayServerInfo() (*peer.AddrInfo, string, error) {
id, err := peer.Decode(relayID)
if err != nil {
err = errors.WithMessage(err, "decoding peer id of relay server")
return nil, "", err
}

// Get the IP address of the relay server.
ip, err := net.LookupIP("relay.perun.network")
if err != nil {
err = errors.WithMessage(err, "looking up IP address of relay.perun.network")
return nil, "", err
}
relayAddr := "/ip4/" + ip[0].String() + "/tcp/5574"

relayMultiaddr, err := ma.NewMultiaddr(relayAddr)
if err != nil {
err = errors.WithMessage(err, "parsing relay multiadress")
return nil, "", err
}

relayInfo := &peer.AddrInfo{
ID: id,
Addrs: []ma.Multiaddr{relayMultiaddr},
}

return relayInfo, relayAddr, nil
}
Loading

0 comments on commit 2ab2dc5

Please sign in to comment.