| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- // Copyright 2016 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 network
- import (
- "context"
- "errors"
- "fmt"
- "net"
- "sync"
- "time"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/discover"
- "github.com/ethereum/go-ethereum/p2p/protocols"
- "github.com/ethereum/go-ethereum/rpc"
- "github.com/ethereum/go-ethereum/swarm/log"
- "github.com/ethereum/go-ethereum/swarm/state"
- )
- const (
- DefaultNetworkID = 3
- // ProtocolMaxMsgSize maximum allowed message size
- ProtocolMaxMsgSize = 10 * 1024 * 1024
- // timeout for waiting
- bzzHandshakeTimeout = 3000 * time.Millisecond
- )
- // BzzSpec is the spec of the generic swarm handshake
- var BzzSpec = &protocols.Spec{
- Name: "bzz",
- Version: 4,
- MaxMsgSize: 10 * 1024 * 1024,
- Messages: []interface{}{
- HandshakeMsg{},
- },
- }
- // DiscoverySpec is the spec for the bzz discovery subprotocols
- var DiscoverySpec = &protocols.Spec{
- Name: "hive",
- Version: 4,
- MaxMsgSize: 10 * 1024 * 1024,
- Messages: []interface{}{
- peersMsg{},
- subPeersMsg{},
- },
- }
- // Addr interface that peerPool needs
- type Addr interface {
- OverlayPeer
- Over() []byte
- Under() []byte
- String() string
- Update(OverlayAddr) OverlayAddr
- }
- // Peer interface represents an live peer connection
- type Peer interface {
- Addr // the address of a peer
- Conn // the live connection (protocols.Peer)
- LastActive() time.Time // last time active
- }
- // Conn interface represents an live peer connection
- type Conn interface {
- ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool
- Handshake(context.Context, interface{}, func(interface{}) error) (interface{}, error) // can send messages
- Send(context.Context, interface{}) error // can send messages
- Drop(error) // disconnect this peer
- Run(func(context.Context, interface{}) error) error // the run function to run a protocol
- Off() OverlayAddr
- }
- // BzzConfig captures the config params used by the hive
- type BzzConfig struct {
- OverlayAddr []byte // base address of the overlay network
- UnderlayAddr []byte // node's underlay address
- HiveParams *HiveParams
- NetworkID uint64
- }
- // Bzz is the swarm protocol bundle
- type Bzz struct {
- *Hive
- NetworkID uint64
- localAddr *BzzAddr
- mtx sync.Mutex
- handshakes map[discover.NodeID]*HandshakeMsg
- streamerSpec *protocols.Spec
- streamerRun func(*BzzPeer) error
- }
- // NewBzz is the swarm protocol constructor
- // arguments
- // * bzz config
- // * overlay driver
- // * peer store
- func NewBzz(config *BzzConfig, kad Overlay, store state.Store, streamerSpec *protocols.Spec, streamerRun func(*BzzPeer) error) *Bzz {
- return &Bzz{
- Hive: NewHive(config.HiveParams, kad, store),
- NetworkID: config.NetworkID,
- localAddr: &BzzAddr{config.OverlayAddr, config.UnderlayAddr},
- handshakes: make(map[discover.NodeID]*HandshakeMsg),
- streamerRun: streamerRun,
- streamerSpec: streamerSpec,
- }
- }
- // UpdateLocalAddr updates underlayaddress of the running node
- func (b *Bzz) UpdateLocalAddr(byteaddr []byte) *BzzAddr {
- b.localAddr = b.localAddr.Update(&BzzAddr{
- UAddr: byteaddr,
- OAddr: b.localAddr.OAddr,
- }).(*BzzAddr)
- return b.localAddr
- }
- // NodeInfo returns the node's overlay address
- func (b *Bzz) NodeInfo() interface{} {
- return b.localAddr.Address()
- }
- // Protocols return the protocols swarm offers
- // Bzz implements the node.Service interface
- // * handshake/hive
- // * discovery
- func (b *Bzz) Protocols() []p2p.Protocol {
- protocol := []p2p.Protocol{
- {
- Name: BzzSpec.Name,
- Version: BzzSpec.Version,
- Length: BzzSpec.Length(),
- Run: b.runBzz,
- NodeInfo: b.NodeInfo,
- },
- {
- Name: DiscoverySpec.Name,
- Version: DiscoverySpec.Version,
- Length: DiscoverySpec.Length(),
- Run: b.RunProtocol(DiscoverySpec, b.Hive.Run),
- NodeInfo: b.Hive.NodeInfo,
- PeerInfo: b.Hive.PeerInfo,
- },
- }
- if b.streamerSpec != nil && b.streamerRun != nil {
- protocol = append(protocol, p2p.Protocol{
- Name: b.streamerSpec.Name,
- Version: b.streamerSpec.Version,
- Length: b.streamerSpec.Length(),
- Run: b.RunProtocol(b.streamerSpec, b.streamerRun),
- })
- }
- return protocol
- }
- // APIs returns the APIs offered by bzz
- // * hive
- // Bzz implements the node.Service interface
- func (b *Bzz) APIs() []rpc.API {
- return []rpc.API{{
- Namespace: "hive",
- Version: "3.0",
- Service: b.Hive,
- }}
- }
- // RunProtocol is a wrapper for swarm subprotocols
- // returns a p2p protocol run function that can be assigned to p2p.Protocol#Run field
- // arguments:
- // * p2p protocol spec
- // * run function taking BzzPeer as argument
- // this run function is meant to block for the duration of the protocol session
- // on return the session is terminated and the peer is disconnected
- // the protocol waits for the bzz handshake is negotiated
- // the overlay address on the BzzPeer is set from the remote handshake
- func (b *Bzz) RunProtocol(spec *protocols.Spec, run func(*BzzPeer) error) func(*p2p.Peer, p2p.MsgReadWriter) error {
- return func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
- // wait for the bzz protocol to perform the handshake
- handshake, _ := b.GetHandshake(p.ID())
- defer b.removeHandshake(p.ID())
- select {
- case <-handshake.done:
- case <-time.After(bzzHandshakeTimeout):
- return fmt.Errorf("%08x: %s protocol timeout waiting for handshake on %08x", b.BaseAddr()[:4], spec.Name, p.ID().Bytes()[:4])
- }
- if handshake.err != nil {
- return fmt.Errorf("%08x: %s protocol closed: %v", b.BaseAddr()[:4], spec.Name, handshake.err)
- }
- // the handshake has succeeded so construct the BzzPeer and run the protocol
- peer := &BzzPeer{
- Peer: protocols.NewPeer(p, rw, spec),
- localAddr: b.localAddr,
- BzzAddr: handshake.peerAddr,
- lastActive: time.Now(),
- }
- return run(peer)
- }
- }
- // performHandshake implements the negotiation of the bzz handshake
- // shared among swarm subprotocols
- func (b *Bzz) performHandshake(p *protocols.Peer, handshake *HandshakeMsg) error {
- ctx, cancel := context.WithTimeout(context.Background(), bzzHandshakeTimeout)
- defer func() {
- close(handshake.done)
- cancel()
- }()
- rsh, err := p.Handshake(ctx, handshake, b.checkHandshake)
- if err != nil {
- handshake.err = err
- return err
- }
- handshake.peerAddr = rsh.(*HandshakeMsg).Addr
- return nil
- }
- // runBzz is the p2p protocol run function for the bzz base protocol
- // that negotiates the bzz handshake
- func (b *Bzz) runBzz(p *p2p.Peer, rw p2p.MsgReadWriter) error {
- handshake, _ := b.GetHandshake(p.ID())
- if !<-handshake.init {
- return fmt.Errorf("%08x: bzz already started on peer %08x", b.localAddr.Over()[:4], ToOverlayAddr(p.ID().Bytes())[:4])
- }
- close(handshake.init)
- defer b.removeHandshake(p.ID())
- peer := protocols.NewPeer(p, rw, BzzSpec)
- err := b.performHandshake(peer, handshake)
- if err != nil {
- log.Warn(fmt.Sprintf("%08x: handshake failed with remote peer %08x: %v", b.localAddr.Over()[:4], ToOverlayAddr(p.ID().Bytes())[:4], err))
- return err
- }
- // fail if we get another handshake
- msg, err := rw.ReadMsg()
- if err != nil {
- return err
- }
- msg.Discard()
- return errors.New("received multiple handshakes")
- }
- // BzzPeer is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer)
- // implements the Peer interface and all interfaces Peer implements: Addr, OverlayPeer
- type BzzPeer struct {
- *protocols.Peer // represents the connection for online peers
- localAddr *BzzAddr // local Peers address
- *BzzAddr // remote address -> implements Addr interface = protocols.Peer
- lastActive time.Time // time is updated whenever mutexes are releasing
- }
- func NewBzzTestPeer(p *protocols.Peer, addr *BzzAddr) *BzzPeer {
- return &BzzPeer{
- Peer: p,
- localAddr: addr,
- BzzAddr: NewAddrFromNodeID(p.ID()),
- }
- }
- // Off returns the overlay peer record for offline persistence
- func (p *BzzPeer) Off() OverlayAddr {
- return p.BzzAddr
- }
- // LastActive returns the time the peer was last active
- func (p *BzzPeer) LastActive() time.Time {
- return p.lastActive
- }
- /*
- Handshake
- * Version: 8 byte integer version of the protocol
- * NetworkID: 8 byte integer network identifier
- * Addr: the address advertised by the node including underlay and overlay connecctions
- */
- type HandshakeMsg struct {
- Version uint64
- NetworkID uint64
- Addr *BzzAddr
- // peerAddr is the address received in the peer handshake
- peerAddr *BzzAddr
- init chan bool
- done chan struct{}
- err error
- }
- // String pretty prints the handshake
- func (bh *HandshakeMsg) String() string {
- return fmt.Sprintf("Handshake: Version: %v, NetworkID: %v, Addr: %v", bh.Version, bh.NetworkID, bh.Addr)
- }
- // Perform initiates the handshake and validates the remote handshake message
- func (b *Bzz) checkHandshake(hs interface{}) error {
- rhs := hs.(*HandshakeMsg)
- if rhs.NetworkID != b.NetworkID {
- return fmt.Errorf("network id mismatch %d (!= %d)", rhs.NetworkID, b.NetworkID)
- }
- if rhs.Version != uint64(BzzSpec.Version) {
- return fmt.Errorf("version mismatch %d (!= %d)", rhs.Version, BzzSpec.Version)
- }
- return nil
- }
- // removeHandshake removes handshake for peer with peerID
- // from the bzz handshake store
- func (b *Bzz) removeHandshake(peerID discover.NodeID) {
- b.mtx.Lock()
- defer b.mtx.Unlock()
- delete(b.handshakes, peerID)
- }
- // GetHandshake returns the bzz handhake that the remote peer with peerID sent
- func (b *Bzz) GetHandshake(peerID discover.NodeID) (*HandshakeMsg, bool) {
- b.mtx.Lock()
- defer b.mtx.Unlock()
- handshake, found := b.handshakes[peerID]
- if !found {
- handshake = &HandshakeMsg{
- Version: uint64(BzzSpec.Version),
- NetworkID: b.NetworkID,
- Addr: b.localAddr,
- init: make(chan bool, 1),
- done: make(chan struct{}),
- }
- // when handhsake is first created for a remote peer
- // it is initialised with the init
- handshake.init <- true
- b.handshakes[peerID] = handshake
- }
- return handshake, found
- }
- // BzzAddr implements the PeerAddr interface
- type BzzAddr struct {
- OAddr []byte
- UAddr []byte
- }
- // Address implements OverlayPeer interface to be used in Overlay
- func (a *BzzAddr) Address() []byte {
- return a.OAddr
- }
- // Over returns the overlay address
- func (a *BzzAddr) Over() []byte {
- return a.OAddr
- }
- // Under returns the underlay address
- func (a *BzzAddr) Under() []byte {
- return a.UAddr
- }
- // ID returns the nodeID from the underlay enode address
- func (a *BzzAddr) ID() discover.NodeID {
- return discover.MustParseNode(string(a.UAddr)).ID
- }
- // Update updates the underlay address of a peer record
- func (a *BzzAddr) Update(na OverlayAddr) OverlayAddr {
- return &BzzAddr{a.OAddr, na.(Addr).Under()}
- }
- // String pretty prints the address
- func (a *BzzAddr) String() string {
- return fmt.Sprintf("%x <%s>", a.OAddr, a.UAddr)
- }
- // RandomAddr is a utility method generating an address from a public key
- func RandomAddr() *BzzAddr {
- key, err := crypto.GenerateKey()
- if err != nil {
- panic("unable to generate key")
- }
- pubkey := crypto.FromECDSAPub(&key.PublicKey)
- var id discover.NodeID
- copy(id[:], pubkey[1:])
- return NewAddrFromNodeID(id)
- }
- // NewNodeIDFromAddr transforms the underlay address to an adapters.NodeID
- func NewNodeIDFromAddr(addr Addr) discover.NodeID {
- log.Info(fmt.Sprintf("uaddr=%s", string(addr.Under())))
- node := discover.MustParseNode(string(addr.Under()))
- return node.ID
- }
- // NewAddrFromNodeID constucts a BzzAddr from a discover.NodeID
- // the overlay address is derived as the hash of the nodeID
- func NewAddrFromNodeID(id discover.NodeID) *BzzAddr {
- return &BzzAddr{
- OAddr: ToOverlayAddr(id.Bytes()),
- UAddr: []byte(discover.NewNode(id, net.IP{127, 0, 0, 1}, 30303, 30303).String()),
- }
- }
- // NewAddrFromNodeIDAndPort constucts a BzzAddr from a discover.NodeID and port uint16
- // the overlay address is derived as the hash of the nodeID
- func NewAddrFromNodeIDAndPort(id discover.NodeID, host net.IP, port uint16) *BzzAddr {
- return &BzzAddr{
- OAddr: ToOverlayAddr(id.Bytes()),
- UAddr: []byte(discover.NewNode(id, host, port, port).String()),
- }
- }
- // ToOverlayAddr creates an overlayaddress from a byte slice
- func ToOverlayAddr(id []byte) []byte {
- return crypto.Keccak256(id)
- }
|