integration: stabilize pre-verack disconnect cycles

In this commit, we make the pre-verack lifecycle test count only peers that
btcd has admitted and processed through version exchange.

The source handshake limit can close a rapid follow-up socket while the
previous disconnect is still unwinding. This made the client-side version
write fail with EPIPE before the intended lifecycle path ran. We now wait for
a version response from btcd, retry rejected attempts under a deadline, and
still disconnect without sending verack.

This preserves all 50 peerDone without peerAdd cycles while removing the
scheduler-dependent admission race from the test.
This commit is contained in:
Olaoluwa Osuntokun 2026-07-21 16:59:33 -07:00
parent 7e9414fa6c
commit 76ba88422f

View file

@ -357,22 +357,27 @@ func TestSyncManagerRaceCorruption(t *testing.T) {
done, heightBefore, heightAfter) done, heightBefore, heightAfter)
} }
// dialAndSendVersion connects to nodeAddr and sends a version // dialPreVerackPeer connects to nodeAddr and exchanges version messages without
// message, returning the open connection. The caller is // sending verack. The caller is responsible for closing the returned
// responsible for closing it. // connection.
func dialAndSendVersion( func dialPreVerackPeer(nodeAddr string) (net.Conn, error) {
t *testing.T, nodeAddr string,
) net.Conn {
t.Helper()
conn, err := net.DialTimeout("tcp", nodeAddr, 5*time.Second) conn, err := net.DialTimeout("tcp", nodeAddr, 5*time.Second)
require.NoError(t, err) if err != nil {
return nil, err
}
connected := false
defer func() {
if !connected {
_ = conn.Close()
}
}()
_ = conn.SetDeadline(time.Now().Add(5 * time.Second)) _ = conn.SetDeadline(time.Now().Add(5 * time.Second))
nodeTCP, err := net.ResolveTCPAddr("tcp", nodeAddr) nodeTCP, err := net.ResolveTCPAddr("tcp", nodeAddr)
require.NoError(t, err) if err != nil {
return nil, err
}
you := wire.NewNetAddress( you := wire.NewNetAddress(
nodeTCP, wire.SFNodeNetwork|wire.SFNodeWitness, nodeTCP, wire.SFNodeNetwork|wire.SFNodeWitness,
@ -391,9 +396,22 @@ func dialAndSendVersion(
err = wire.WriteMessage( err = wire.WriteMessage(
conn, msgVersion, wire.ProtocolVersion, wire.SimNet, conn, msgVersion, wire.ProtocolVersion, wire.SimNet,
) )
require.NoError(t, err) if err != nil {
return nil, err
}
return conn msg, _, err := wire.ReadMessage(
conn, wire.ProtocolVersion, wire.SimNet,
)
if err != nil {
return nil, err
}
if _, ok := msg.(*wire.MsgVersion); !ok {
return nil, fmt.Errorf("expected version message, got %T", msg)
}
connected = true
return conn, nil
} }
// TestPreVerackDisconnect verifies that a peer disconnecting // TestPreVerackDisconnect verifies that a peer disconnecting
@ -409,15 +427,36 @@ func TestPreVerackDisconnect(t *testing.T) {
nodeAddr := harness.P2PAddress() nodeAddr := harness.P2PAddress()
// Connect and send version, then disconnect before receiving or // Connect and exchange version messages, then disconnect without sending
// sending verack. This is expected to produce a peerDone without // verack. This is expected to produce a peerDone without a preceding
// a preceding peerAdd in the lifecycle channel. // peerAdd in the lifecycle channel.
const preVerackAttempts = 50 const (
preVerackAttempts = 50
preVerackRetryTimeout = 5 * time.Second
preVerackRetryDelay = 10 * time.Millisecond
)
retries := 0
for i := 0; i < preVerackAttempts; i++ { for i := 0; i < preVerackAttempts; i++ {
conn := dialAndSendVersion(t, nodeAddr) deadline := time.Now().Add(preVerackRetryTimeout)
conn.Close() for {
conn, err := dialPreVerackPeer(nodeAddr)
if err == nil {
require.NoError(t, conn.Close())
break
}
if time.Now().After(deadline) {
t.Fatalf("pre-verack attempt %d did not complete: %v",
i+1, err)
}
retries++
time.Sleep(preVerackRetryDelay)
}
} }
t.Logf("completed %d pre-verack disconnects with %d retries",
preVerackAttempts, retries)
// Allow the node time to process all the disconnects. // Allow the node time to process all the disconnects.
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)