Skip to content

Commit effbd00

Browse files
committed
feat(libp2p): add exchange addr tests
Signed-off-by: Minh Huy Tran <huy@perun.network>
1 parent 848f107 commit effbd00

File tree

6 files changed

+215
-3
lines changed

6 files changed

+215
-3
lines changed

wire/net/libp2p/account.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,6 @@ func getRelayServerInfo() (*peer.AddrInfo, string, error) {
324324
// getHost returns a new random account for testing.
325325
func getHost(rng *rand.Rand) *Account {
326326
acc := NewRandomAccount(rng)
327-
log.Println(acc.ID())
328327
return acc
329328
}
330329

@@ -345,8 +344,11 @@ func (acc *Account) keepReservationAlive(ctx context.Context, ai peer.AddrInfo)
345344
select {
346345
case <-ctx.Done():
347346
log.Println("keepReservationAlive: context cancelled")
347+
err := acc.Close()
348+
if err != nil {
349+
panic(err)
350+
}
348351
return
349-
350352
case <-ticker.C:
351353
newReservation, err := libp2pclient.Reserve(ctx, acc.Host, ai)
352354
if err != nil {

wire/net/libp2p/address.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@ import (
2222
"github.com/libp2p/go-libp2p/core/crypto"
2323
"github.com/libp2p/go-libp2p/core/peer"
2424
"github.com/pkg/errors"
25+
"perun.network/go-perun/wallet"
2526
"perun.network/go-perun/wire"
2627
)
2728

29+
// testBackendID is the identifier for the simulated Backend.
30+
const testBackendID = 0
31+
2832
// Address is a peer address for wire discovery.
2933
type Address struct {
3034
peer.ID
@@ -49,6 +53,21 @@ func NewRandomAddress(rng *rand.Rand) *Address {
4953
return &Address{id}
5054
}
5155

56+
// NewRandomAddresses returns a new random peer address.
57+
func NewRandomAddresses(rng *rand.Rand) map[wallet.BackendID]wire.Address {
58+
_, publicKey, err := crypto.GenerateKeyPairWithReader(crypto.RSA, keySize, rng)
59+
if err != nil {
60+
panic(err)
61+
}
62+
63+
id, err := peer.IDFromPublicKey(publicKey)
64+
if err != nil {
65+
panic(err)
66+
}
67+
a := Address{id}
68+
return map[wallet.BackendID]wire.Address{testBackendID: &a}
69+
}
70+
5271
// Equal returns whether the two addresses are equal.
5372
func (a *Address) Equal(b wire.Address) bool {
5473
bTyped, ok := b.(*Address)

wire/net/libp2p/dialer_internal_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func TestDialer_Register(t *testing.T) {
6161
}
6262

6363
func TestDialer_Dial(t *testing.T) {
64-
timeout := 1000 * time.Millisecond
64+
timeout := 2 * time.Second
6565
rng := pkgtest.Prng(t)
6666

6767
lHost := getHost(rng)

wire/net/libp2p/init_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ package libp2p_test
1616

1717
import (
1818
_ "perun.network/go-perun/backend/sim/wallet" // Initializes the randomizers.
19+
_ "perun.network/go-perun/backend/sim/wire" // Reninit the sim wire package.
1920
)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2025 - See NOTICE file for copyright holders.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package libp2p
16+
17+
import (
18+
"sync"
19+
20+
"github.com/pkg/errors"
21+
22+
"perun.network/go-perun/wire"
23+
wirenet "perun.network/go-perun/wire/net"
24+
"polycry.pt/poly-go/sync/atomic"
25+
)
26+
27+
var _ wirenet.Conn = (*MockConn)(nil)
28+
29+
type MockConn struct {
30+
mutex sync.Mutex
31+
closed atomic.Bool
32+
recvQueue chan *wire.Envelope
33+
34+
sent func(*wire.Envelope) // observes sent messages.
35+
}
36+
37+
func newMockConn() *MockConn {
38+
return &MockConn{
39+
sent: func(*wire.Envelope) {},
40+
recvQueue: make(chan *wire.Envelope, 1),
41+
}
42+
}
43+
44+
func (c *MockConn) Send(e *wire.Envelope) error {
45+
c.mutex.Lock()
46+
defer c.mutex.Unlock()
47+
if c.closed.IsSet() {
48+
return errors.New("closed")
49+
}
50+
c.sent(e)
51+
return nil
52+
}
53+
54+
func (c *MockConn) Recv() (*wire.Envelope, error) {
55+
c.mutex.Lock()
56+
defer c.mutex.Unlock()
57+
if c.closed.IsSet() {
58+
return nil, errors.New("closed")
59+
}
60+
return <-c.recvQueue, nil
61+
}
62+
63+
func (c *MockConn) Close() error {
64+
if !c.closed.TrySet() {
65+
return errors.New("double close")
66+
}
67+
return nil
68+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright 2025 - See NOTICE file for copyright holders.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// This test uses the wire/net/libp2p implementation of Account and Address
16+
// to test the default implementation of wire.
17+
//
18+
//nolint:testpackage
19+
package libp2p
20+
21+
import (
22+
"context"
23+
"math/rand"
24+
"net"
25+
"sync"
26+
"testing"
27+
"time"
28+
29+
"perun.network/go-perun/channel"
30+
31+
"github.com/stretchr/testify/assert"
32+
"github.com/stretchr/testify/require"
33+
34+
"perun.network/go-perun/wire"
35+
wirenet "perun.network/go-perun/wire/net"
36+
perunio "perun.network/go-perun/wire/perunio/serializer"
37+
wiretest "perun.network/go-perun/wire/test"
38+
ctxtest "polycry.pt/poly-go/context/test"
39+
"polycry.pt/poly-go/test"
40+
)
41+
42+
const timeout = 100 * time.Millisecond
43+
44+
func TestExchangeAddrs_ConnFail(t *testing.T) {
45+
rng := test.Prng(t)
46+
a, _ := newPipeConnPair()
47+
a.Close()
48+
acc := wiretest.NewRandomAccountMap(rng, channel.TestBackendID)
49+
defer acc[channel.TestBackendID].(*Account).Close()
50+
addr, err := wirenet.ExchangeAddrsPassive(context.Background(), acc, a)
51+
assert.Nil(t, addr)
52+
assert.Error(t, err)
53+
}
54+
55+
func TestExchangeAddrs_Success(t *testing.T) {
56+
rng := test.Prng(t)
57+
conn0, conn1 := newPipeConnPair()
58+
defer conn0.Close()
59+
account0, account1 := wiretest.NewRandomAccountMap(rng, channel.TestBackendID), wiretest.NewRandomAccountMap(rng, channel.TestBackendID)
60+
defer account0[channel.TestBackendID].(*Account).Close()
61+
defer account1[channel.TestBackendID].(*Account).Close()
62+
var wg sync.WaitGroup
63+
wg.Add(1)
64+
65+
go func() {
66+
defer wg.Done()
67+
defer conn1.Close()
68+
69+
recvAddr0, err := wirenet.ExchangeAddrsPassive(context.Background(), account1, conn1)
70+
assert.NoError(t, err)
71+
assert.True(t, channel.EqualWireMaps(recvAddr0, wire.AddressMapfromAccountMap(account0)))
72+
}()
73+
74+
err := wirenet.ExchangeAddrsActive(context.Background(), account0, wire.AddressMapfromAccountMap(account1), conn0)
75+
require.NoError(t, err)
76+
77+
wg.Wait()
78+
}
79+
80+
func TestExchangeAddrs_BogusMsg(t *testing.T) {
81+
rng := test.Prng(t)
82+
acc := wiretest.NewRandomAccountMap(rng, channel.TestBackendID)
83+
defer acc[channel.TestBackendID].(*Account).Close()
84+
conn := newMockConn()
85+
conn.recvQueue <- newRandomEnvelope(rng, wire.NewPingMsg())
86+
addr, err := wirenet.ExchangeAddrsPassive(context.Background(), acc, conn)
87+
88+
assert.Error(t, err, "ExchangeAddrs should error when peer sends a non-AuthResponseMsg")
89+
assert.Nil(t, addr)
90+
}
91+
92+
func TestExchangeAddrs_Timeout(t *testing.T) {
93+
rng := test.Prng(t)
94+
a, _ := newPipeConnPair()
95+
96+
ctx, cancel := context.WithTimeout(context.Background(), timeout)
97+
defer cancel()
98+
ctxtest.AssertTerminates(t, 20*timeout, func() {
99+
acc := wiretest.NewRandomAccountMap(rng, channel.TestBackendID)
100+
defer acc[channel.TestBackendID].(*Account).Close()
101+
addr, err := wirenet.ExchangeAddrsPassive(ctx, acc, a)
102+
assert.Nil(t, addr)
103+
assert.Error(t, err)
104+
})
105+
}
106+
107+
// newPipeConnPair creates endpoints that are connected via pipes.
108+
func newPipeConnPair() (a wirenet.Conn, b wirenet.Conn) {
109+
c0, c1 := net.Pipe()
110+
ser := perunio.Serializer()
111+
return wirenet.NewIoConn(c0, ser), wirenet.NewIoConn(c1, ser)
112+
}
113+
114+
// NewRandomEnvelope returns an envelope around message m with random sender and
115+
// recipient generated using randomness from rng.
116+
func newRandomEnvelope(rng *rand.Rand, m wire.Msg) *wire.Envelope {
117+
return &wire.Envelope{
118+
Sender: NewRandomAddresses(rng),
119+
Recipient: NewRandomAddresses(rng),
120+
Msg: m,
121+
}
122+
}

0 commit comments

Comments
 (0)