Skip to content

Commit 848f107

Browse files
committed
feat(libp2p): add dialer and listener
Signed-off-by: Minh Huy Tran <huy@perun.network>
1 parent eff95d6 commit 848f107

File tree

8 files changed

+556
-82
lines changed

8 files changed

+556
-82
lines changed

wallet/address_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func TestCloneAddresses(t *testing.T) {
119119
addrs := wallettest.NewRandomAddressArray(rng, 3, TestBackendID)
120120
addrs0 := wallet.CloneAddresses(addrs)
121121
require.Equal(t, addrs, addrs0)
122-
require.NotSame(t, addrs, addrs0)
122+
require.NotSame(t, &addrs, &addrs0)
123123
for i, a := range addrs {
124124
require.NotSame(t, a, addrs0[i])
125125
}

wire/net/libp2p/account.go

Lines changed: 136 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import (
2020
"crypto/sha256"
2121
"encoding/json"
2222
"fmt"
23+
"log"
2324
"math/rand"
2425
"net"
26+
"time"
2527

2628
"github.com/libp2p/go-libp2p"
2729
"github.com/libp2p/go-libp2p/core/crypto"
@@ -39,95 +41,85 @@ import (
3941
const (
4042
relayID = "QmVCPfUMr98PaaM8qbAQBgJ9jqc7XHpGp7AsyragdFDmgm"
4143

44+
keySize = 2048
45+
4246
queryProtocol = "/address-book/query/1.0.0" // Protocol for querying the relay-server for a peerID.
4347
registerProtocol = "/address-book/register/1.0.0" // Protocol for registering an on-chain address with the relay-server.
4448
removeProtocol = "/address-book/remove/1.0.0" // Protocol for deregistering an on-chain address with the relay-server.
4549
)
4650

47-
// Account represents a libp2p wire account containting a libp2p host.
51+
// Account represents a libp2p wire account containing a libp2p host.
4852
type Account struct {
4953
host.Host
50-
relayAddr string
51-
privateKey crypto.PrivKey
52-
}
53-
54-
// Address returns the account's address.
55-
func (acc *Account) Address() wire.Address {
56-
return &Address{acc.ID()}
57-
}
58-
59-
// Sign signs the given message with the account's private key.
60-
func (acc *Account) Sign(data []byte) ([]byte, error) {
61-
// Extract the private key from the account.
62-
if acc.privateKey == nil {
63-
return nil, errors.New("private key not set")
64-
}
65-
hashed := sha256.Sum256(data)
66-
67-
signature, err := acc.privateKey.Sign(hashed[:])
68-
if err != nil {
69-
return nil, err
70-
}
71-
return signature, nil
72-
73-
}
74-
75-
// MarshalPrivateKey marshals the account's private key to binary.
76-
func (acc *Account) MarshalPrivateKey() ([]byte, error) {
77-
return crypto.MarshalPrivateKey(acc.privateKey)
54+
relayAddr string
55+
privateKey crypto.PrivKey
56+
reservation *libp2pclient.Reservation
57+
closer context.CancelFunc
7858
}
7959

80-
// NewAccountFromPrivateKeyBytes creates a new account from a given private key.
81-
func NewAccountFromPrivateKeyBytes(prvKeyBytes []byte) (*Account, error) {
82-
prvKey, err := crypto.UnmarshalPrivateKey(prvKeyBytes)
60+
// NewRandomAccount generates a new random account.
61+
func NewRandomAccount(rng *rand.Rand) *Account {
62+
relayInfo, relayAddr, err := getRelayServerInfo()
8363
if err != nil {
84-
return nil, errors.WithMessage(err, "unmarshalling private key")
64+
panic(err)
8565
}
8666

87-
relayInfo, relayAddr, err := getRelayServerInfo()
67+
// Creates a new RSA key pair for this host.
68+
prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.Ed25519, keySize, rng)
8869
if err != nil {
8970
panic(err)
9071
}
72+
9173
// Construct a new libp2p client for our relay-server.
9274
// Identity(prvKey) - Use a RSA private key to generate the ID of the host.
9375
// EnableRelay() - Enable relay system and configures itself as a node behind a NAT
9476
client, err := libp2p.New(
77+
libp2p.NoListenAddrs,
9578
libp2p.Identity(prvKey),
9679
libp2p.EnableRelay(),
9780
)
9881
if err != nil {
99-
return nil, errors.WithMessage(err, "creating new libp2p client")
82+
client.Close()
83+
panic(err)
84+
}
85+
86+
// Redialing hacked
87+
if sw, ok := client.Network().(*swarm.Swarm); ok {
88+
sw.Backoff().Clear(relayInfo.ID)
10089
}
10190

10291
if err := client.Connect(context.Background(), *relayInfo); err != nil {
10392
client.Close()
104-
return nil, errors.WithMessage(err, "connecting to the relay server")
93+
panic(errors.WithMessage(err, "connecting to the relay server"))
10594
}
10695

10796
// Reserve connection
10897
// Hosts that want to have messages relayed on their behalf need to reserve a slot
10998
// with the circuit relay service host
110-
_, err = libp2pclient.Reserve(context.Background(), client, *relayInfo)
99+
resv, err := libp2pclient.Reserve(context.Background(), client, *relayInfo)
111100
if err != nil {
112101
panic(errors.WithMessage(err, "failed to receive a relay reservation from relay server"))
113102
}
114103

115-
return &Account{client, relayAddr, prvKey}, nil
104+
ctx, cancel := context.WithCancel(context.Background())
105+
acc := &Account{client, relayAddr, prvKey, resv, cancel}
106+
107+
go acc.keepReservationAlive(ctx, *relayInfo)
108+
109+
return acc
116110
}
117111

118-
// NewRandomAccount generates a new random account.
119-
func NewRandomAccount(rng *rand.Rand) *Account {
120-
relayInfo, relayAddr, err := getRelayServerInfo()
112+
// NewAccountFromPrivateKeyBytes creates a new account from a given private key.
113+
func NewAccountFromPrivateKeyBytes(prvKeyBytes []byte) (*Account, error) {
114+
prvKey, err := crypto.UnmarshalPrivateKey(prvKeyBytes)
121115
if err != nil {
122-
panic(err)
116+
return nil, errors.WithMessage(err, "unmarshalling private key")
123117
}
124118

125-
// Creates a new RSA key pair for this host.
126-
prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.Ed25519, 2048, rng)
119+
relayInfo, relayAddr, err := getRelayServerInfo()
127120
if err != nil {
128121
panic(err)
129122
}
130-
131123
// Construct a new libp2p client for our relay-server.
132124
// Identity(prvKey) - Use a RSA private key to generate the ID of the host.
133125
// EnableRelay() - Enable relay system and configures itself as a node behind a NAT
@@ -137,26 +129,57 @@ func NewRandomAccount(rng *rand.Rand) *Account {
137129
libp2p.EnableRelay(),
138130
)
139131
if err != nil {
140-
client.Close()
141-
panic(err)
132+
return nil, errors.WithMessage(err, "creating new libp2p client")
133+
}
134+
135+
if sw, ok := client.Network().(*swarm.Swarm); ok {
136+
sw.Backoff().Clear(relayInfo.ID)
142137
}
143138

144-
// Redialing hacked
145-
client.Network().(*swarm.Swarm).Backoff().Clear(*&relayInfo.ID)
146139
if err := client.Connect(context.Background(), *relayInfo); err != nil {
147140
client.Close()
148-
panic(errors.WithMessage(err, "connecting to the relay server"))
141+
return nil, errors.WithMessage(err, "connecting to the relay server")
149142
}
150143

151144
// Reserve connection
152145
// Hosts that want to have messages relayed on their behalf need to reserve a slot
153146
// with the circuit relay service host
154-
_, err = libp2pclient.Reserve(context.Background(), client, *relayInfo)
147+
res, err := libp2pclient.Reserve(context.Background(), client, *relayInfo)
155148
if err != nil {
156149
panic(errors.WithMessage(err, "failed to receive a relay reservation from relay server"))
157150
}
158151

159-
return &Account{client, relayAddr, prvKey}
152+
ctx, cancel := context.WithCancel(context.Background())
153+
acc := &Account{client, relayAddr, prvKey, res, cancel}
154+
155+
go acc.keepReservationAlive(ctx, *relayInfo)
156+
157+
return acc, nil
158+
}
159+
160+
// Address returns the account's address.
161+
func (acc *Account) Address() wire.Address {
162+
return &Address{acc.ID()}
163+
}
164+
165+
// Sign signs the given message with the account's private key.
166+
func (acc *Account) Sign(data []byte) ([]byte, error) {
167+
// Extract the private key from the account.
168+
if acc.privateKey == nil {
169+
return nil, errors.New("private key not set")
170+
}
171+
hashed := sha256.Sum256(data)
172+
173+
signature, err := acc.privateKey.Sign(hashed[:])
174+
if err != nil {
175+
return nil, err
176+
}
177+
return signature, nil
178+
}
179+
180+
// MarshalPrivateKey marshals the account's private key to binary.
181+
func (acc *Account) MarshalPrivateKey() ([]byte, error) {
182+
return crypto.MarshalPrivateKey(acc.privateKey)
160183
}
161184

162185
// RegisterOnChainAddress registers an on-chain address with the account to the relay-server's address book.
@@ -183,7 +206,7 @@ func (acc *Account) RegisterOnChainAddress(onChainAddr wallet.Address) error {
183206
registerData.OnChainAddress = onChainAddr.String()
184207
registerData.PeerID = acc.ID().String()
185208

186-
data, err := json.Marshal(registerData)
209+
data, err := json.Marshal(registerData) //nolint:musttag
187210
if err != nil {
188211
return errors.WithMessage(err, "marshalling register data")
189212
}
@@ -198,6 +221,7 @@ func (acc *Account) RegisterOnChainAddress(onChainAddr wallet.Address) error {
198221

199222
// Close closes the account.
200223
func (acc *Account) Close() error {
224+
acc.closer()
201225
return acc.Host.Close()
202226
}
203227

@@ -221,7 +245,7 @@ func (acc *Account) DeregisterOnChainAddress(onChainAddr wallet.Address) error {
221245
unregisterData.OnChainAddress = onChainAddr.String()
222246
unregisterData.PeerID = acc.ID().String()
223247

224-
data, err := json.Marshal(unregisterData)
248+
data, err := json.Marshal(unregisterData) //nolint:musttag
225249
if err != nil {
226250
return errors.WithMessage(err, "marshalling register data")
227251
}
@@ -249,7 +273,10 @@ func (acc *Account) QueryOnChainAddress(onChainAddr wallet.Address) (*Address, e
249273
defer s.Close()
250274

251275
rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))
252-
rw.WriteString(fmt.Sprintf("%s\n", onChainAddr))
276+
_, err = fmt.Fprintf(rw, "%s\n", onChainAddr)
277+
if err != nil {
278+
return nil, errors.WithMessage(err, "writing on-chain address")
279+
}
253280
rw.Flush()
254281

255282
str, _ := rw.ReadString('\n')
@@ -293,3 +320,57 @@ func getRelayServerInfo() (*peer.AddrInfo, string, error) {
293320

294321
return relayInfo, relayAddr, nil
295322
}
323+
324+
// getHost returns a new random account for testing.
325+
func getHost(rng *rand.Rand) *Account {
326+
acc := NewRandomAccount(rng)
327+
log.Println(acc.ID())
328+
return acc
329+
}
330+
331+
// keepReservationAlive keeps the reservation alive by periodically renewing it.
332+
func (acc *Account) keepReservationAlive(ctx context.Context, ai peer.AddrInfo) {
333+
const (
334+
reserveInterval = 50 * time.Second // slightly less than ReserveTimeout
335+
initialBackoff = 2 * time.Second
336+
maxBackoff = 1 * time.Minute
337+
)
338+
339+
ticker := time.NewTicker(reserveInterval)
340+
defer ticker.Stop()
341+
342+
backoff := initialBackoff
343+
344+
for {
345+
select {
346+
case <-ctx.Done():
347+
log.Println("keepReservationAlive: context cancelled")
348+
return
349+
350+
case <-ticker.C:
351+
newReservation, err := libp2pclient.Reserve(ctx, acc.Host, ai)
352+
if err != nil {
353+
log.Printf("keepReservationAlive: reservation failed: %v; retrying in %s", err, backoff)
354+
355+
timer := time.NewTimer(backoff)
356+
select {
357+
case <-ctx.Done():
358+
timer.Stop()
359+
return
360+
case <-timer.C:
361+
if backoff < maxBackoff {
362+
backoff *= 2
363+
if backoff > maxBackoff {
364+
backoff = maxBackoff
365+
}
366+
}
367+
}
368+
continue
369+
}
370+
371+
acc.reservation = newReservation
372+
backoff = initialBackoff
373+
log.Println("keepReservationAlive: reservation successfully renewed")
374+
}
375+
}
376+
}

wire/net/libp2p/account_test.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// you may not use this file except in compliance with the License.
55
// You may obtain a copy of the License at
66
//
7-
// http://www.apache.org/licenses/LICENSE-2.0
7+
// http://www.apache.org/licenses/LICENSE-2.0
88
//
99
// Unless required by applicable law or agreed to in writing, software
1010
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -15,8 +15,6 @@
1515
package libp2p_test
1616

1717
import (
18-
"log"
19-
"math/rand"
2018
"testing"
2119
"time"
2220

@@ -35,12 +33,6 @@ func TestNewAccount(t *testing.T) {
3533
defer acc.Close()
3634
}
3735

38-
func getHost(rng *rand.Rand) *libp2p.Account {
39-
acc := libp2p.NewRandomAccount(rng)
40-
log.Println(acc.Host.ID())
41-
return acc
42-
}
43-
4436
func TestAddressBookRegister(t *testing.T) {
4537
rng := pkgtest.Prng(t)
4638
acc := libp2p.NewRandomAccount(rng)
@@ -227,7 +219,6 @@ func TestAddressBookRegisterQueryMultiple(t *testing.T) {
227219
assert.NoError(t, err)
228220
}
229221

230-
// Test NewAccountFromPrivateKey
231222
func TestNewAccountFromPrivateKey(t *testing.T) {
232223
rng := pkgtest.Prng(t)
233224
acc := libp2p.NewRandomAccount(rng)

0 commit comments

Comments
 (0)