bitxhub/internal/repo/network.go

234 lines
5.7 KiB
Go

package repo
import (
"fmt"
"io/ioutil"
"path/filepath"
"sort"
"strings"
"github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
crypto2 "github.com/meshplus/bitxhub-kit/crypto"
"github.com/meshplus/bitxhub/pkg/cert"
ma "github.com/multiformats/go-multiaddr"
)
const (
networkConfigFile = "network.toml"
nodePrivFile = "certs/node.priv"
)
// NetworkConfig .
// @param OtherNodes to fit original code
// @param OthersNodes for new network config
type NetworkConfig struct {
ID uint64
N uint64
LocalAddr string
Nodes []*NetworkNode
OtherNodes map[uint64]*peer.AddrInfo
}
// NetworkNode is the struct to describe network conf of a node
// @param ID is the id of the node, it is origined by sorting.
// @param Addrs is the address array of the node.
// @param Addr is the default used address the node.
type NetworkNode struct {
ID uint64
Addr string // the optimal address of a node
Addrs []string
}
// ReadinNetworkConfig is used for read in toml file
type ReadinNetworkConfig struct {
Addrs [][]string
}
// AddrToPeerInfo transfer addr to PeerInfo
// addr example: "/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64"
func AddrToPeerInfo(multiAddr string) (*peer.AddrInfo, error) {
maddr, err := ma.NewMultiaddr(multiAddr)
if err != nil {
return nil, err
}
return peer.AddrInfoFromP2pAddr(maddr)
}
// AddrsToPeerInfo transfer addrs to PeerInfo
func AddrsToPeerInfo(multiAddrs []string) ([]peer.AddrInfo, error) {
maddrs := []ma.Multiaddr{}
for _, multiAddr := range multiAddrs {
maddr, err := ma.NewMultiaddr(multiAddr)
if err != nil {
return nil, err
}
maddrs = append(maddrs, maddr)
}
return peer.AddrInfosFromP2pAddrs(maddrs...)
}
// loadNetworkConfig is compatible with old network.toml and support new network.toml config file
func loadNetworkConfig(repoRoot string) (*NetworkConfig, error) {
rdiNetworkConfig := &ReadinNetworkConfig{}
if err := ReadConfig(filepath.Join(repoRoot, networkConfigFile), "toml", rdiNetworkConfig); err != nil {
return nil, err
}
networkConfig := &NetworkConfig{}
for _, node := range rdiNetworkConfig.Addrs {
networkConfig.Nodes = append(networkConfig.Nodes, &NetworkNode{Addrs: node})
}
// whether new network format is new
if networkConfig.N == 0 { // judge whether new network format is new
networkConfig.N = uint64(len(networkConfig.Nodes))
}
if uint64(len(networkConfig.Nodes)) != networkConfig.N {
return nil, fmt.Errorf("wrong nodes number")
}
// use the first address of node as its default addr
for _, node := range networkConfig.Nodes {
if node.Addr == "" {
node.Addr = node.Addrs[0]
}
}
// read private key to get PeerID
PeerID, err := GetPidFromPrivFile(filepath.Join(repoRoot, nodePrivFile))
if err != nil {
return nil, err
}
// sort PeerId of nodes to produce IDs:
sort.Sort(networkConfig)
findSelf := false
for i, node := range networkConfig.Nodes {
// write ID into node struct:
node.ID = uint64(i + 1)
pid, err := MultiaddrToPeerID(networkConfig.Nodes[i].Addrs[0])
if err != nil {
return nil, err
}
if pid == PeerID {
// match PeerID to know node's self ID:
networkConfig.ID = node.ID
findSelf = true
}
}
if findSelf == false {
return nil, fmt.Errorf("PeerID of this node was not matched to any of these nodes")
}
for _, node := range networkConfig.Nodes {
if node.ID == networkConfig.ID {
networkConfig.LocalAddr = node.Addr
}
}
if networkConfig.LocalAddr == "" {
return nil, fmt.Errorf("lack of local address")
}
idx := strings.LastIndex(networkConfig.LocalAddr, "/p2p/")
if idx == -1 {
return nil, fmt.Errorf("pid is not existed in bootstrap")
}
networkConfig.LocalAddr = networkConfig.LocalAddr[:idx]
nodes := networkConfig.Nodes
m := make(map[uint64]*peer.AddrInfo)
for _, node := range nodes {
if node.ID != networkConfig.ID {
addrs, err := AddrsToPeerInfo(node.Addrs)
if err != nil {
return nil, fmt.Errorf("wrong network addr: %w", err)
}
if len(addrs) != 1 {
return nil, fmt.Errorf("different PeerIDs in the same node")
}
// overwrite addr if formatIsNew is true.
addr := &addrs[0]
m[node.ID] = addr
}
}
networkConfig.OtherNodes = m
return networkConfig, nil
}
// Len returns length of the struct to be sorted
func (p NetworkConfig) Len() int { return len(p.Nodes) }
// Less compares two iterms ascending(ASC)
func (p NetworkConfig) Less(i, j int) bool {
multiAddri := p.Nodes[i].Addrs[0]
multiAddrj := p.Nodes[j].Addrs[0]
maddri, err := ma.NewMultiaddr(multiAddri)
if err != nil {
panic(err)
}
maddrj, err := ma.NewMultiaddr(multiAddrj)
if err != nil {
panic(err)
}
_, idi := peer.SplitAddr(maddri)
if idi == "" {
panic(err)
}
_, idj := peer.SplitAddr(maddrj)
if idj == "" {
panic(err)
}
return idi < idj
}
// Swap swaps iterms
func (p NetworkConfig) Swap(i, j int) { p.Nodes[i], p.Nodes[j] = p.Nodes[j], p.Nodes[i] }
// GetPidFromPrivFile gets pid from libp2p node priv file
func GetPidFromPrivFile(privPath string) (string, error) {
data, err := ioutil.ReadFile(privPath)
if err != nil {
return "", fmt.Errorf("read private key: %w", err)
}
privKey, err := cert.ParsePrivateKey(data, crypto2.ECDSA_P256)
if err != nil {
return "", err
}
_, pk, err := crypto.KeyPairFromStdKey(privKey.K)
if err != nil {
return "", err
}
pid, err := peer.IDFromPublicKey(pk)
if err != nil {
return "", err
}
return pid.String(), nil
}
// MultiaddrToPeerID .
func MultiaddrToPeerID(multiAddr string) (string, error) {
maddri, err := ma.NewMultiaddr(multiAddr)
if err != nil {
return "", err
}
_, PeerID := peer.SplitAddr(maddri)
if PeerID == "" {
return "", err
}
return PeerID.String(), nil
}