plugeth/p2p/discover/v5_session.go
Felix Lange b7394d7942
p2p/discover: add initial discovery v5 implementation (#20750)
This adds an implementation of the current discovery v5 spec.

There is full integration with cmd/devp2p and enode.Iterator in this
version. In theory we could enable the new protocol as a replacement of
discovery v4 at any time. In practice, there will likely be a few more
changes to the spec and implementation before this can happen.
2020-04-08 09:57:23 +02:00

124 lines
3.7 KiB
Go

// Copyright 2020 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package discover
import (
crand "crypto/rand"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/hashicorp/golang-lru/simplelru"
)
// The sessionCache keeps negotiated encryption keys and
// state for in-progress handshakes in the Discovery v5 wire protocol.
type sessionCache struct {
sessions *simplelru.LRU
handshakes map[sessionID]*whoareyouV5
clock mclock.Clock
}
// sessionID identifies a session or handshake.
type sessionID struct {
id enode.ID
addr string
}
// session contains session information
type session struct {
writeKey []byte
readKey []byte
nonceCounter uint32
}
func newSessionCache(maxItems int, clock mclock.Clock) *sessionCache {
cache, err := simplelru.NewLRU(maxItems, nil)
if err != nil {
panic("can't create session cache")
}
return &sessionCache{
sessions: cache,
handshakes: make(map[sessionID]*whoareyouV5),
clock: clock,
}
}
// nextNonce creates a nonce for encrypting a message to the given session.
func (sc *sessionCache) nextNonce(id enode.ID, addr string) []byte {
n := make([]byte, gcmNonceSize)
crand.Read(n)
return n
}
// session returns the current session for the given node, if any.
func (sc *sessionCache) session(id enode.ID, addr string) *session {
item, ok := sc.sessions.Get(sessionID{id, addr})
if !ok {
return nil
}
return item.(*session)
}
// readKey returns the current read key for the given node.
func (sc *sessionCache) readKey(id enode.ID, addr string) []byte {
if s := sc.session(id, addr); s != nil {
return s.readKey
}
return nil
}
// writeKey returns the current read key for the given node.
func (sc *sessionCache) writeKey(id enode.ID, addr string) []byte {
if s := sc.session(id, addr); s != nil {
return s.writeKey
}
return nil
}
// storeNewSession stores new encryption keys in the cache.
func (sc *sessionCache) storeNewSession(id enode.ID, addr string, r, w []byte) {
sc.sessions.Add(sessionID{id, addr}, &session{
readKey: r, writeKey: w,
})
}
// getHandshake gets the handshake challenge we previously sent to the given remote node.
func (sc *sessionCache) getHandshake(id enode.ID, addr string) *whoareyouV5 {
return sc.handshakes[sessionID{id, addr}]
}
// storeSentHandshake stores the handshake challenge sent to the given remote node.
func (sc *sessionCache) storeSentHandshake(id enode.ID, addr string, challenge *whoareyouV5) {
challenge.sent = sc.clock.Now()
sc.handshakes[sessionID{id, addr}] = challenge
}
// deleteHandshake deletes handshake data for the given node.
func (sc *sessionCache) deleteHandshake(id enode.ID, addr string) {
delete(sc.handshakes, sessionID{id, addr})
}
// handshakeGC deletes timed-out handshakes.
func (sc *sessionCache) handshakeGC() {
deadline := sc.clock.Now().Add(-handshakeTimeout)
for key, challenge := range sc.handshakes {
if challenge.sent < deadline {
delete(sc.handshakes, key)
}
}
}