@@ -20,8 +20,10 @@ import (
20
20
"crypto/sha256"
21
21
"encoding/json"
22
22
"fmt"
23
+ "log"
23
24
"math/rand"
24
25
"net"
26
+ "time"
25
27
26
28
"github.com/libp2p/go-libp2p"
27
29
"github.com/libp2p/go-libp2p/core/crypto"
@@ -39,95 +41,85 @@ import (
39
41
const (
40
42
relayID = "QmVCPfUMr98PaaM8qbAQBgJ9jqc7XHpGp7AsyragdFDmgm"
41
43
44
+ keySize = 2048
45
+
42
46
queryProtocol = "/address-book/query/1.0.0" // Protocol for querying the relay-server for a peerID.
43
47
registerProtocol = "/address-book/register/1.0.0" // Protocol for registering an on-chain address with the relay-server.
44
48
removeProtocol = "/address-book/remove/1.0.0" // Protocol for deregistering an on-chain address with the relay-server.
45
49
)
46
50
47
- // Account represents a libp2p wire account containting a libp2p host.
51
+ // Account represents a libp2p wire account containing a libp2p host.
48
52
type Account struct {
49
53
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
78
58
}
79
59
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 ( )
83
63
if err != nil {
84
- return nil , errors . WithMessage (err , "unmarshalling private key" )
64
+ panic (err )
85
65
}
86
66
87
- relayInfo , relayAddr , err := getRelayServerInfo ()
67
+ // Creates a new RSA key pair for this host.
68
+ prvKey , _ , err := crypto .GenerateKeyPairWithReader (crypto .Ed25519 , keySize , rng )
88
69
if err != nil {
89
70
panic (err )
90
71
}
72
+
91
73
// Construct a new libp2p client for our relay-server.
92
74
// Identity(prvKey) - Use a RSA private key to generate the ID of the host.
93
75
// EnableRelay() - Enable relay system and configures itself as a node behind a NAT
94
76
client , err := libp2p .New (
77
+ libp2p .NoListenAddrs ,
95
78
libp2p .Identity (prvKey ),
96
79
libp2p .EnableRelay (),
97
80
)
98
81
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 )
100
89
}
101
90
102
91
if err := client .Connect (context .Background (), * relayInfo ); err != nil {
103
92
client .Close ()
104
- return nil , errors .WithMessage (err , "connecting to the relay server" )
93
+ panic ( errors .WithMessage (err , "connecting to the relay server" ) )
105
94
}
106
95
107
96
// Reserve connection
108
97
// Hosts that want to have messages relayed on their behalf need to reserve a slot
109
98
// with the circuit relay service host
110
- _ , err = libp2pclient .Reserve (context .Background (), client , * relayInfo )
99
+ resv , err : = libp2pclient .Reserve (context .Background (), client , * relayInfo )
111
100
if err != nil {
112
101
panic (errors .WithMessage (err , "failed to receive a relay reservation from relay server" ))
113
102
}
114
103
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
116
110
}
117
111
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 )
121
115
if err != nil {
122
- panic (err )
116
+ return nil , errors . WithMessage (err , "unmarshalling private key" )
123
117
}
124
118
125
- // Creates a new RSA key pair for this host.
126
- prvKey , _ , err := crypto .GenerateKeyPairWithReader (crypto .Ed25519 , 2048 , rng )
119
+ relayInfo , relayAddr , err := getRelayServerInfo ()
127
120
if err != nil {
128
121
panic (err )
129
122
}
130
-
131
123
// Construct a new libp2p client for our relay-server.
132
124
// Identity(prvKey) - Use a RSA private key to generate the ID of the host.
133
125
// EnableRelay() - Enable relay system and configures itself as a node behind a NAT
@@ -137,26 +129,57 @@ func NewRandomAccount(rng *rand.Rand) *Account {
137
129
libp2p .EnableRelay (),
138
130
)
139
131
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 )
142
137
}
143
138
144
- // Redialing hacked
145
- client .Network ().(* swarm.Swarm ).Backoff ().Clear (* & relayInfo .ID )
146
139
if err := client .Connect (context .Background (), * relayInfo ); err != nil {
147
140
client .Close ()
148
- panic ( errors .WithMessage (err , "connecting to the relay server" ) )
141
+ return nil , errors .WithMessage (err , "connecting to the relay server" )
149
142
}
150
143
151
144
// Reserve connection
152
145
// Hosts that want to have messages relayed on their behalf need to reserve a slot
153
146
// with the circuit relay service host
154
- _ , err = libp2pclient .Reserve (context .Background (), client , * relayInfo )
147
+ res , err : = libp2pclient .Reserve (context .Background (), client , * relayInfo )
155
148
if err != nil {
156
149
panic (errors .WithMessage (err , "failed to receive a relay reservation from relay server" ))
157
150
}
158
151
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 )
160
183
}
161
184
162
185
// 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 {
183
206
registerData .OnChainAddress = onChainAddr .String ()
184
207
registerData .PeerID = acc .ID ().String ()
185
208
186
- data , err := json .Marshal (registerData )
209
+ data , err := json .Marshal (registerData ) //nolint:musttag
187
210
if err != nil {
188
211
return errors .WithMessage (err , "marshalling register data" )
189
212
}
@@ -198,6 +221,7 @@ func (acc *Account) RegisterOnChainAddress(onChainAddr wallet.Address) error {
198
221
199
222
// Close closes the account.
200
223
func (acc * Account ) Close () error {
224
+ acc .closer ()
201
225
return acc .Host .Close ()
202
226
}
203
227
@@ -221,7 +245,7 @@ func (acc *Account) DeregisterOnChainAddress(onChainAddr wallet.Address) error {
221
245
unregisterData .OnChainAddress = onChainAddr .String ()
222
246
unregisterData .PeerID = acc .ID ().String ()
223
247
224
- data , err := json .Marshal (unregisterData )
248
+ data , err := json .Marshal (unregisterData ) //nolint:musttag
225
249
if err != nil {
226
250
return errors .WithMessage (err , "marshalling register data" )
227
251
}
@@ -249,7 +273,10 @@ func (acc *Account) QueryOnChainAddress(onChainAddr wallet.Address) (*Address, e
249
273
defer s .Close ()
250
274
251
275
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
+ }
253
280
rw .Flush ()
254
281
255
282
str , _ := rw .ReadString ('\n' )
@@ -293,3 +320,57 @@ func getRelayServerInfo() (*peer.AddrInfo, string, error) {
293
320
294
321
return relayInfo , relayAddr , nil
295
322
}
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
+ }
0 commit comments