diff --git a/defaults.go b/defaults.go index d11302690c..8f7dc94671 100644 --- a/defaults.go +++ b/defaults.go @@ -27,8 +27,8 @@ import ( // Useful when you want to extend, but not replace, the supported transport // security protocols. var DefaultSecurity = ChainOptions( - Security(noise.ID, noise.New), Security(tls.ID, tls.New), + Security(noise.ID, noise.New), ) // DefaultMuxers configures libp2p to use the stream connection multiplexers. diff --git a/p2p/test/security/bench_test.go b/p2p/test/security/bench_test.go new file mode 100644 index 0000000000..e3b0b937e6 --- /dev/null +++ b/p2p/test/security/bench_test.go @@ -0,0 +1,130 @@ +package benchmark + +import ( + "context" + crand "crypto/rand" + "io" + "net" + "sync" + "testing" + + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/sec" + "github.com/libp2p/go-libp2p/p2p/security/noise" + tls "github.com/libp2p/go-libp2p/p2p/security/tls" + "github.com/stretchr/testify/assert" +) + +type Factory func(*testing.B, crypto.PrivKey) sec.SecureTransport + +func benchmarkThroughput(b *testing.B, size int, factory Factory) { + privA, pubA, err := crypto.GenerateEd25519Key(crand.Reader) + assert.NoError(b, err) + idA, err := peer.IDFromPublicKey(pubA) + assert.NoError(b, err) + tptA := factory(b, privA) + + privB, pubB, err := crypto.GenerateEd25519Key(crand.Reader) + assert.NoError(b, err) + idB, err := peer.IDFromPublicKey(pubB) + assert.NoError(b, err) + tptB := factory(b, privB) + + // pipe here serialize the decryption and encryption, we might want both parallelised to reduce context switching impact on the benchmark. + // https://github.com/golang/go/issues/34502 would be ideal for the parallel usecase. + p1, p2 := net.Pipe() + var ready sync.Mutex // wait for completed handshake + var finished sync.Mutex // wait until all data has been received + ready.Lock() + finished.Lock() + go func() { + defer finished.Unlock() + conn, err := tptB.SecureInbound(context.Background(), p2, idA) + assert.NoError(b, err) + ready.Unlock() + + _, err = io.Copy(io.Discard, conn) + assert.NoError(b, err) + }() + + conn, err := tptA.SecureOutbound(context.Background(), p1, idB) + assert.NoError(b, err) + ready.Lock() + + buf := make([]byte, size) + b.SetBytes(int64(len(buf))) + b.ResetTimer() + + for i := b.N; i != 0; i-- { + _, err = conn.Write(buf[:]) + assert.NoError(b, err) + } + conn.Close() + + finished.Lock() +} +func benchmarkHandshakes(b *testing.B, factory Factory) { + privA, pubA, err := crypto.GenerateEd25519Key(crand.Reader) + assert.NoError(b, err) + idA, err := peer.IDFromPublicKey(pubA) + assert.NoError(b, err) + tptA := factory(b, privA) + + privB, pubB, err := crypto.GenerateEd25519Key(crand.Reader) + assert.NoError(b, err) + idB, err := peer.IDFromPublicKey(pubB) + assert.NoError(b, err) + tptB := factory(b, privB) + + pipes := make(chan net.Conn, 1) + + var finished sync.Mutex // wait until all data has been transfered + finished.Lock() + go func() { + defer finished.Unlock() + var throwAway [1]byte + for p := range pipes { + conn, err := tptB.SecureInbound(context.Background(), p, idA) + assert.NoError(b, err) + _, err = conn.Read(throwAway[:]) // read because currently the tls transport handshake when calling Read. + assert.ErrorIs(b, err, io.EOF) + } + }() + b.ResetTimer() + + for i := b.N; i != 0; i-- { + p1, p2 := net.Pipe() + pipes <- p2 + conn, err := tptA.SecureOutbound(context.Background(), p1, idB) + assert.NoError(b, err) + assert.NoError(b, conn.Close()) + } + close(pipes) + + finished.Lock() +} + +func bench(b *testing.B, factory Factory) { + b.Run("throughput", func(b *testing.B) { + b.Run("32KiB", func(b *testing.B) { benchmarkThroughput(b, 32*1024, factory) }) + b.Run("1MiB", func(b *testing.B) { benchmarkThroughput(b, 1024*1024, factory) }) + }) + b.Run("handshakes", func(b *testing.B) { benchmarkHandshakes(b, factory) }) +} + +func BenchmarkNoise(b *testing.B) { + bench(b, func(b *testing.B, priv crypto.PrivKey) sec.SecureTransport { + tpt, err := noise.New("", priv, nil) + assert.NoError(b, err) + return tpt + }) +} + +func BenchmarkTLS(b *testing.B) { + bench(b, func(b *testing.B, priv crypto.PrivKey) sec.SecureTransport { + tpt, err := tls.New("", priv, nil) + assert.NoError(b, err) + return tpt + }) +}