Skip to content

Commit

Permalink
defaults: do TLS by default for encryption (libp2p#2650)
Browse files Browse the repository at this point in the history
* p2p/security: add transport independent benchmarks

* defaults: do TLS by default for encryption

Tls has much better throughput, the handshake benchmark is fairly noisy, there is no significant performance difference, however it does allocate more.

```
goos: linux
goarch: amd64
cpu: AMD Ryzen 5 3600 6-Core Processor
BenchmarkNoise/throughput/32KiB-12 24984	     46605 ns/op	 703.10 MB/s	      37 B/op	       2 allocs/op
BenchmarkNoise/throughput/1MiB-12   1134	   1459483 ns/op	 718.46 MB/s	     663 B/op	      34 allocs/op
BenchmarkNoise/handshakes-12        1302	   1054533 ns/op	   32691 B/op	     348 allocs/op
BenchmarkTls/throughput/32KiB-12   49006	     24309 ns/op	1347.99 MB/s	      50 B/op	       2 allocs/op
BenchmarkTls/throughput/1MiB-12     1747	    778498 ns/op	1346.92 MB/s	    1603 B/op	      64 allocs/op
BenchmarkTls/handshakes-12          1116           1045475 ns/op	  105257 B/op	    1478 allocs/op
```
  • Loading branch information
Jorropo authored Jan 24, 2024
1 parent c681c4a commit e33f4c4
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 1 deletion.
2 changes: 1 addition & 1 deletion defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
130 changes: 130 additions & 0 deletions p2p/test/security/bench_test.go
Original file line number Diff line number Diff line change
@@ -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
})
}

0 comments on commit e33f4c4

Please sign in to comment.