2020-03-29 21:32:01 +08:00
|
|
|
package peermgr
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2020-05-08 16:08:21 +08:00
|
|
|
"sync"
|
2020-03-29 21:32:01 +08:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/Rican7/retry"
|
|
|
|
"github.com/Rican7/retry/strategy"
|
|
|
|
"github.com/ethereum/go-ethereum/event"
|
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
|
|
"github.com/libp2p/go-libp2p-core/protocol"
|
|
|
|
"github.com/meshplus/bitxhub-model/pb"
|
|
|
|
"github.com/meshplus/bitxhub/internal/ledger"
|
|
|
|
"github.com/meshplus/bitxhub/internal/model"
|
|
|
|
"github.com/meshplus/bitxhub/internal/model/events"
|
|
|
|
"github.com/meshplus/bitxhub/internal/repo"
|
|
|
|
"github.com/meshplus/bitxhub/pkg/cert"
|
2020-08-24 14:17:46 +08:00
|
|
|
network "github.com/meshplus/go-lightp2p"
|
2020-03-29 21:32:01 +08:00
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
protocolID protocol.ID = "/B1txHu6/1.0.0" // magic protocol
|
|
|
|
)
|
|
|
|
|
|
|
|
type Swarm struct {
|
2020-11-05 11:04:53 +08:00
|
|
|
repo *repo.Repo
|
|
|
|
p2p network.Network
|
|
|
|
logger logrus.FieldLogger
|
|
|
|
peers map[uint64]*peer.AddrInfo
|
|
|
|
connectedPeers sync.Map
|
|
|
|
ledger ledger.Ledger
|
2020-03-29 21:32:01 +08:00
|
|
|
orderMessageFeed event.Feed
|
2020-11-05 11:04:53 +08:00
|
|
|
enablePing bool
|
|
|
|
pingTimeout time.Duration
|
2020-03-29 21:32:01 +08:00
|
|
|
|
|
|
|
ctx context.Context
|
|
|
|
cancel context.CancelFunc
|
|
|
|
}
|
|
|
|
|
2020-12-04 19:58:01 +08:00
|
|
|
func New(repoConfig *repo.Repo, logger logrus.FieldLogger, ledger ledger.Ledger) (*Swarm, error) {
|
2020-08-24 14:17:46 +08:00
|
|
|
var protocolIDs = []string{string(protocolID)}
|
2020-04-21 22:56:19 +08:00
|
|
|
p2p, err := network.New(
|
2020-12-04 19:58:01 +08:00
|
|
|
network.WithLocalAddr(repoConfig.NetworkConfig.LocalAddr),
|
|
|
|
network.WithPrivateKey(repoConfig.Key.Libp2pPrivKey),
|
2020-08-24 14:17:46 +08:00
|
|
|
network.WithProtocolIDs(protocolIDs),
|
2020-04-21 22:56:19 +08:00
|
|
|
network.WithLogger(logger),
|
2020-12-03 12:50:06 +08:00
|
|
|
network.WithNotify(notifiee{}),
|
2020-03-29 21:32:01 +08:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("create p2p: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-12-04 19:58:01 +08:00
|
|
|
peers, err := repoConfig.NetworkConfig.GetPeers()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("get peers:%w", err)
|
|
|
|
}
|
|
|
|
|
2020-03-29 21:32:01 +08:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
|
|
|
|
return &Swarm{
|
2020-12-04 19:58:01 +08:00
|
|
|
repo: repoConfig,
|
2020-03-29 21:32:01 +08:00
|
|
|
p2p: p2p,
|
|
|
|
logger: logger,
|
|
|
|
ledger: ledger,
|
2020-12-04 19:58:01 +08:00
|
|
|
enablePing: repoConfig.Config.Ping.Enable,
|
|
|
|
pingTimeout: repoConfig.Config.Ping.Duration,
|
|
|
|
peers: peers,
|
2020-05-08 16:08:21 +08:00
|
|
|
connectedPeers: sync.Map{},
|
2020-03-29 21:32:01 +08:00
|
|
|
ctx: ctx,
|
|
|
|
cancel: cancel,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (swarm *Swarm) Start() error {
|
2020-04-21 22:56:19 +08:00
|
|
|
swarm.p2p.SetMessageHandler(swarm.handleMessage)
|
|
|
|
|
2020-03-29 21:32:01 +08:00
|
|
|
if err := swarm.p2p.Start(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-12-04 19:58:01 +08:00
|
|
|
for id, addr := range swarm.OtherPeers() {
|
2020-03-29 21:32:01 +08:00
|
|
|
go func(id uint64, addr *peer.AddrInfo) {
|
|
|
|
if err := retry.Retry(func(attempt uint) error {
|
2020-08-24 14:17:46 +08:00
|
|
|
if err := swarm.p2p.Connect(*addr); err != nil {
|
2020-03-29 21:32:01 +08:00
|
|
|
swarm.logger.WithFields(logrus.Fields{
|
|
|
|
"node": id,
|
|
|
|
"error": err,
|
|
|
|
}).Error("Connect failed")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-11-24 14:14:54 +08:00
|
|
|
if err := swarm.verifyCertOrDisconnect(id); err != nil {
|
2020-03-29 21:32:01 +08:00
|
|
|
if attempt != 0 && attempt%5 == 0 {
|
|
|
|
swarm.logger.WithFields(logrus.Fields{
|
|
|
|
"node": id,
|
|
|
|
"error": err,
|
|
|
|
}).Error("Verify cert")
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
swarm.logger.WithFields(logrus.Fields{
|
|
|
|
"node": id,
|
|
|
|
}).Info("Connect successfully")
|
|
|
|
|
2020-05-08 16:08:21 +08:00
|
|
|
swarm.connectedPeers.Store(id, addr)
|
2020-03-29 21:32:01 +08:00
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
strategy.Wait(1*time.Second),
|
|
|
|
); err != nil {
|
|
|
|
swarm.logger.Error(err)
|
|
|
|
}
|
|
|
|
}(id, addr)
|
|
|
|
}
|
|
|
|
|
2020-11-05 11:04:53 +08:00
|
|
|
if swarm.enablePing {
|
|
|
|
go swarm.Ping()
|
|
|
|
}
|
|
|
|
|
2020-03-29 21:32:01 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (swarm *Swarm) Stop() error {
|
|
|
|
swarm.cancel()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-24 14:14:54 +08:00
|
|
|
func (swarm *Swarm) verifyCertOrDisconnect(id uint64) error {
|
|
|
|
if err := swarm.verifyCert(id); err != nil {
|
|
|
|
if err = swarm.p2p.Disconnect(swarm.peers[id].ID.String()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-05 11:04:53 +08:00
|
|
|
func (swarm *Swarm) Ping() {
|
|
|
|
ticker := time.NewTicker(swarm.pingTimeout)
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
|
|
|
fields := logrus.Fields{}
|
|
|
|
swarm.connectedPeers.Range(func(key, value interface{}) bool {
|
|
|
|
info := value.(*peer.AddrInfo)
|
|
|
|
pingCh, err := swarm.p2p.Ping(info.ID.String())
|
|
|
|
if err != nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case res := <-pingCh:
|
|
|
|
fields[fmt.Sprintf("%d", key.(uint64))] = res.RTT
|
|
|
|
case <-time.After(time.Second * 5):
|
|
|
|
swarm.logger.Errorf("ping to node %d timeout", key.(uint64))
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
swarm.logger.WithFields(fields).Warning("ping time")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 16:32:11 +08:00
|
|
|
func (swarm *Swarm) AsyncSend(id uint64, msg *pb.Message) error {
|
2020-03-29 21:32:01 +08:00
|
|
|
if err := swarm.checkID(id); err != nil {
|
|
|
|
return fmt.Errorf("p2p send: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := msg.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-24 14:17:46 +08:00
|
|
|
addr := swarm.peers[id].ID.String()
|
|
|
|
return swarm.p2p.AsyncSend(addr, data)
|
2020-03-29 21:32:01 +08:00
|
|
|
}
|
|
|
|
|
2020-08-24 14:17:46 +08:00
|
|
|
func (swarm *Swarm) SendWithStream(s network.Stream, msg *pb.Message) error {
|
2020-03-29 21:32:01 +08:00
|
|
|
data, err := msg.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-08-24 14:17:46 +08:00
|
|
|
return s.AsyncSend(data)
|
2020-03-29 21:32:01 +08:00
|
|
|
}
|
|
|
|
|
2020-04-21 16:32:11 +08:00
|
|
|
func (swarm *Swarm) Send(id uint64, msg *pb.Message) (*pb.Message, error) {
|
2020-03-29 21:32:01 +08:00
|
|
|
if err := swarm.checkID(id); err != nil {
|
|
|
|
return nil, fmt.Errorf("check id: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := msg.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-24 14:17:46 +08:00
|
|
|
addr := swarm.peers[id].ID.String()
|
|
|
|
ret, err := swarm.p2p.Send(addr, data)
|
2020-03-29 21:32:01 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("sync send: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
m := &pb.Message{}
|
2020-08-24 14:17:46 +08:00
|
|
|
if err := m.Unmarshal(ret); err != nil {
|
2020-03-29 21:32:01 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return m, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (swarm *Swarm) Broadcast(msg *pb.Message) error {
|
2020-08-24 14:17:46 +08:00
|
|
|
addrs := make([]string, 0, len(swarm.peers))
|
2020-12-04 19:58:01 +08:00
|
|
|
for _, addr := range swarm.OtherPeers() {
|
2020-08-24 14:17:46 +08:00
|
|
|
addrs = append(addrs, addr.ID.String())
|
2020-03-29 21:32:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
data, err := msg.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-08-24 14:17:46 +08:00
|
|
|
return swarm.p2p.Broadcast(addrs, data)
|
2020-03-29 21:32:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (swarm *Swarm) Peers() map[uint64]*peer.AddrInfo {
|
|
|
|
m := make(map[uint64]*peer.AddrInfo)
|
2020-12-04 19:58:01 +08:00
|
|
|
for id, node := range swarm.peers {
|
|
|
|
m[id] = node
|
2020-03-29 21:32:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
func (swarm *Swarm) OtherPeers() map[uint64]*peer.AddrInfo {
|
|
|
|
m := swarm.Peers()
|
2020-11-20 16:37:47 +08:00
|
|
|
if swarm.repo != nil {
|
|
|
|
delete(m, swarm.repo.NetworkConfig.ID)
|
|
|
|
}
|
2020-03-29 21:32:01 +08:00
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
func (swarm *Swarm) SubscribeOrderMessage(ch chan<- events.OrderMessageEvent) event.Subscription {
|
|
|
|
return swarm.orderMessageFeed.Subscribe(ch)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (swarm *Swarm) verifyCert(id uint64) error {
|
2020-04-21 16:32:11 +08:00
|
|
|
if err := swarm.checkID(id); err != nil {
|
|
|
|
return fmt.Errorf("check id: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-03-29 21:32:01 +08:00
|
|
|
msg := &pb.Message{
|
|
|
|
Type: pb.Message_FETCH_CERT,
|
|
|
|
}
|
|
|
|
|
2020-04-21 16:32:11 +08:00
|
|
|
ret, err := swarm.Send(id, msg)
|
2020-03-29 21:32:01 +08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("sync send: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
certs := &model.CertsMessage{}
|
|
|
|
if err := certs.Unmarshal(ret.Data); err != nil {
|
|
|
|
return fmt.Errorf("unmarshal certs: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeCert, err := cert.ParseCert(certs.NodeCert)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("parse node cert: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
agencyCert, err := cert.ParseCert(certs.AgencyCert)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("parse agency cert: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := verifyCerts(nodeCert, agencyCert, swarm.repo.Certs.CACert); err != nil {
|
|
|
|
return fmt.Errorf("verify certs: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-08-24 14:17:46 +08:00
|
|
|
err = swarm.p2p.Disconnect(swarm.peers[id].ID.String())
|
2020-03-29 21:32:01 +08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("disconnect peer: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (swarm *Swarm) checkID(id uint64) error {
|
|
|
|
if swarm.peers[id] == nil {
|
|
|
|
return fmt.Errorf("wrong id: %d", id)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|