Fabric 1.0原始碼分析(15)gossip(流言演算法)
# Fabric 1.0原始碼筆記 之 gossip(流言演算法)
## 1、gossip概述
gossip,翻譯為流言蜚語,即為一種可最終達到一致的演算法。最終一致的另外的含義就是,不保證同時達到一致。
gossip中有三種基本的操作:
* push - A節點將資料(key,value,version)及對應的版本號推送給B節點,B節點更新A中比自己新的資料
* pull - A僅將資料key,version推送給B,B將本地比A新的資料(Key,value,version)推送給A,A更新本地
* push/pull - 與pull類似,只是多了一步,A再將本地比B新的資料推送給B,B更新本地
gossip在Fabric中作用:
* 管理組織內節點和通道資訊,並加測節點是否線上或離線。
* 廣播資料,使組織內相同channel的節點同步相同資料。
* 管理新加入的節點,並同步資料到新節點。
gossip程式碼,分佈在gossip、peer/gossip目錄下,目錄結構如下:
* gossip目錄:
* service目錄,GossipService介面定義及實現。
* integration目錄,NewGossipComponent工具函式。
* gossip目錄,Gossip介面定義及實現。
* comm目錄,GossipServer介面實現。
* state目錄,GossipStateProvider介面定義及實現(狀態複製)。
* api目錄:訊息加密服務介面定義。
* crypto.go,MessageCryptoService介面定義。
* channel.go,SecurityAdvisor介面定義。
* peer/gossip目錄:
* mcs.go,MessageCryptoService介面實現,即mspMessageCryptoService結構體及方法。
* sa.go,SecurityAdvisor介面實現,即mspSecurityAdvisor結構體及方法。
GossipServer更詳細內容,參考:[Fabric 1.0原始碼筆記 之 gossip(流言演算法) #GossipServer(Gossip服務端)](GossipServer.md)
## 2、GossipService介面定義及實現
### 2.1、GossipService介面定義
```go
type GossipService interface {
gossip.Gossip
NewConfigEventer() ConfigProcessor
InitializeChannel(chainID string, committer committer.Committer, endpoints []string)
GetBlock(chainID string, index uint64) *common.Block
AddPayload(chainID string, payload *proto.Payload) error
}
//程式碼在gossip/service/gossip_service.go
```
補充gossip.Gossip:
```go
type Gossip interface {
Send(msg *proto.GossipMessage, peers ...*comm.RemotePeer)
Peers() []discovery.NetworkMember
PeersOfChannel(common.ChainID) []discovery.NetworkMember
UpdateMetadata(metadata []byte)
UpdateChannelMetadata(metadata []byte, chainID common.ChainID)
Gossip(msg *proto.GossipMessage)
Accept(acceptor common.MessageAcceptor, passThrough bool) (<-chan *proto.GossipMessage, <-chan proto.ReceivedMessage)
JoinChan(joinMsg api.JoinChannelMessage, chainID common.ChainID)
SuspectPeers(s api.PeerSuspector)
Stop()
}
//程式碼在gossip/gossip/gossip.go
```
### 2.2、GossipService介面實現
GossipService介面實現,即gossipServiceImpl結構體及方法。
```go
type gossipSvc gossip.Gossip
type gossipServiceImpl struct {
gossipSvc
chains map[string]state.GossipStateProvider //鏈
leaderElection map[string]election.LeaderElectionService //選舉服務
deliveryService deliverclient.DeliverService
deliveryFactory DeliveryServiceFactory
lock sync.RWMutex
idMapper identity.Mapper
mcs api.MessageCryptoService
peerIdentity []byte
secAdv api.SecurityAdvisor
}
//初始化Gossip Service,調取InitGossipServiceCustomDeliveryFactory()
func InitGossipService(peerIdentity []byte, endpoint string, s *grpc.Server, mcs api.MessageCryptoService,secAdv api.SecurityAdvisor, secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) error
//初始化Gossip Service
func InitGossipServiceCustomDeliveryFactory(peerIdentity []byte, endpoint string, s *grpc.Server,factory DeliveryServiceFactory, mcs api.MessageCryptoService, secAdv api.SecurityAdvisor,secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) error
//獲取gossipServiceInstance
func GetGossipService() GossipService
//調取 newConfigEventer(g)
func (g *gossipServiceImpl) NewConfigEventer() ConfigProcessor
//初始化通道
func (g *gossipServiceImpl) InitializeChannel(chainID string, committer committer.Committer, endpoints []string)
func (g *gossipServiceImpl) configUpdated(config Config)
func (g *gossipServiceImpl) GetBlock(chainID string, index uint64) *common.Block
func (g *gossipServiceImpl) AddPayload(chainID string, payload *proto.Payload) error
//停止gossip服務
func (g *gossipServiceImpl) Stop()
func (g *gossipServiceImpl) newLeaderElectionComponent(chainID string, callback func(bool)) election.LeaderElectionService
func (g *gossipServiceImpl) amIinChannel(myOrg string, config Config) bool
func (g *gossipServiceImpl) onStatusChangeFactory(chainID string, committer blocksprovider.LedgerInfo) func(bool)
func orgListFromConfig(config Config) []string
//程式碼在gossip/service/gossip_service.go
```
#### 2.2.1、func InitGossipService(peerIdentity []byte, endpoint string, s *grpc.Server, mcs api.MessageCryptoService,secAdv api.SecurityAdvisor, secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) error
```go
func InitGossipService(peerIdentity []byte, endpoint string, s *grpc.Server, mcs api.MessageCryptoService,
secAdv api.SecurityAdvisor, secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) error {
util.GetLogger(util.LoggingElectionModule, "")
return InitGossipServiceCustomDeliveryFactory(peerIdentity, endpoint, s, &deliveryFactoryImpl{},
mcs, secAdv, secureDialOpts, bootPeers...)
}
func InitGossipServiceCustomDeliveryFactory(peerIdentity []byte, endpoint string, s *grpc.Server,
factory DeliveryServiceFactory, mcs api.MessageCryptoService, secAdv api.SecurityAdvisor,
secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) error {
var err error
var gossip gossip.Gossip
once.Do(func() {
//peer.gossip.endpoint,本節點在組織內的gossip id,或者使用peerEndpoint.Address
if overrideEndpoint := viper.GetString("peer.gossip.endpoint"); overrideEndpoint != "" {
endpoint = overrideEndpoint
}
idMapper := identity.NewIdentityMapper(mcs, peerIdentity)
gossip, err = integration.NewGossipComponent(peerIdentity, endpoint, s, secAdv,
mcs, idMapper, secureDialOpts, bootPeers...)
gossipServiceInstance = &gossipServiceImpl{
mcs: mcs,
gossipSvc: gossip,
chains: make(map[string]state.GossipStateProvider),
leaderElection: make(map[string]election.LeaderElectionService),
deliveryFactory: factory,
idMapper: idMapper,
peerIdentity: peerIdentity,
secAdv: secAdv,
}
})
return err
}
//程式碼在gossip/service/gossip_service.go
```
#### 2.2.2、func (g *gossipServiceImpl) Stop()
```go
func (g *gossipServiceImpl) Stop() {
g.lock.Lock()
defer g.lock.Unlock()
for _, ch := range g.chains {
logger.Info("Stopping chain", ch)
ch.Stop()
}
for chainID, electionService := range g.leaderElection {
logger.Infof("Stopping leader election for %s", chainID)
electionService.Stop()
}
g.gossipSvc.Stop()
if g.deliveryService != nil {
g.deliveryService.Stop()
}
}
//程式碼在gossip/service/gossip_service.go
```
#### 2.2.3、func (g *gossipServiceImpl) InitializeChannel(chainID string, committer committer.Committer, endpoints []string)
```go
func (g *gossipServiceImpl) InitializeChannel(chainID string, committer committer.Committer, endpoints []string) {
g.lock.Lock()
defer g.lock.Unlock()
//構造GossipStateProviderImpl,並啟動goroutine處理從orderer或其他節點接收的block
g.chains[chainID] = state.NewGossipStateProvider(chainID, g, committer, g.mcs)
if g.deliveryService == nil {
var err error
g.deliveryService, err = g.deliveryFactory.Service(gossipServiceInstance, endpoints, g.mcs)
}
if g.deliveryService != nil {
//如下兩者只可以二選一
leaderElection := viper.GetBool("peer.gossip.useLeaderElection") //是否動態選舉代表節點
isStaticOrgLeader := viper.GetBool("peer.gossip.orgLeader") //是否指定本節點為組織代表節點
if leaderElection {
//選舉代表節點
g.leaderElection[chainID] = g.newLeaderElectionComponent(chainID, g.onStatusChangeFactory(chainID, committer))
} else if isStaticOrgLeader {
//如果指定本節點為代表節點,則啟動Deliver client
g.deliveryService.StartDeliverForChannel(chainID, committer, func() {})
}
}
//程式碼在gossip/service/gossip_service.go
```
#### 2.2.4、func (g *gossipServiceImpl) AddPayload(chainID string, payload *proto.Payload) error
```go
func (g *gossipServiceImpl) AddPayload(chainID string, payload *proto.Payload) error {
g.lock.RLock()
defer g.lock.RUnlock()
return g.chains[chainID].AddPayload(payload)
}
//程式碼在gossip/service/gossip_service.go
```
## 3、NewGossipComponent工具函式
```go
func NewGossipComponent(peerIdentity []byte, endpoint string, s *grpc.Server,
secAdv api.SecurityAdvisor, cryptSvc api.MessageCryptoService, idMapper identity.Mapper,
secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) (gossip.Gossip, error) {
//peer.gossip.externalEndpoint為節點被組織外節點感知時的地址
externalEndpoint := viper.GetString("peer.gossip.externalEndpoint")
//endpoint,取自peer.address,即節點對外服務的地址
//bootPeers取自peer.gossip.bootstrap,即啟動節點後所進行gossip連線的初始節點,可為多個
conf, err := newConfig(endpoint, externalEndpoint, bootPeers...)
gossipInstance := gossip.NewGossipService(conf, s, secAdv, cryptSvc, idMapper,
peerIdentity, secureDialOpts)
return gossipInstance, nil
}
func newConfig(selfEndpoint string, externalEndpoint string, bootPeers ...string) (*gossip.Config, error) {
_, p, err := net.SplitHostPort(selfEndpoint)
port, err := strconv.ParseInt(p, 10, 64) //節點對外服務的埠
var cert *tls.Certificate
if viper.GetBool("peer.tls.enabled") {
certTmp, err := tls.LoadX509KeyPair(config.GetPath("peer.tls.cert.file"), config.GetPath("peer.tls.key.file"))
cert = &certTmp
}
return &gossip.Config{
BindPort: int(port),
BootstrapPeers: bootPeers,
ID: selfEndpoint,
MaxBlockCountToStore: util.GetIntOrDefault("peer.gossip.maxBlockCountToStore", 100),
MaxPropagationBurstLatency: util.GetDurationOrDefault("peer.gossip.maxPropagationBurstLatency", 10*time.Millisecond),
MaxPropagationBurstSize: util.GetIntOrDefault("peer.gossip.maxPropagationBurstSize", 10),
PropagateIterations: util.GetIntOrDefault("peer.gossip.propagateIterations", 1),
PropagatePeerNum: util.GetIntOrDefault("peer.gossip.propagatePeerNum", 3),
PullInterval: util.GetDurationOrDefault("peer.gossip.pullInterval", 4*time.Second),
PullPeerNum: util.GetIntOrDefault("peer.gossip.pullPeerNum", 3),
InternalEndpoint: selfEndpoint,
ExternalEndpoint: externalEndpoint,
PublishCertPeriod: util.GetDurationOrDefault("peer.gossip.publishCertPeriod", 10*time.Second),
RequestStateInfoInterval: util.GetDurationOrDefault("peer.gossip.requestStateInfoInterval", 4*time.Second),
PublishStateInfoInterval: util.GetDurationOrDefault("peer.gossip.publishStateInfoInterval", 4*time.Second),
SkipBlockVerification: viper.GetBool("peer.gossip.skipBlockVerification"),
TLSServerCert: cert,
}, nil
}
//程式碼在gossip/integration/integration.go
```
## 4、Gossip介面定義及實現
### 4.1、Gossip介面定義
```go
type Gossip interface {
//向節點傳送訊息
Send(msg *proto.GossipMessage, peers ...*comm.RemotePeer)
//返回活的節點
Peers() []discovery.NetworkMember
//返回指定通道的活的節點
PeersOfChannel(common.ChainID) []discovery.NetworkMember
//更新metadata
UpdateMetadata(metadata []byte)
//更新通道metadata
UpdateChannelMetadata(metadata []byte, chainID common.ChainID)
//向網路內其他節點傳送資料
Gossip(msg *proto.GossipMessage)
Accept(acceptor common.MessageAcceptor, passThrough bool) (<-chan *proto.GossipMessage, <-chan proto.ReceivedMessage)
//加入通道
JoinChan(joinMsg api.JoinChannelMessage, chainID common.ChainID)
//驗證可疑節點身份,並關閉無效連結
SuspectPeers(s api.PeerSuspector)
//停止Gossip服務
Stop()
}
//程式碼在gossip/gossip/gossip.go
```
### 4.2、Config結構體定義
```go
type Config struct {
BindPort int //繫結的埠,僅用於測試
ID string //本例項id,即對外開放的節點地址
BootstrapPeers []string //啟動時要連結的節點
PropagateIterations int //取自peer.gossip.propagateIterations,訊息轉發的次數,預設為1
PropagatePeerNum int //取自peer.gossip.propagatePeerNum,訊息推送給節點的個數,預設為3
MaxBlockCountToStore int //取自peer.gossip.maxBlockCountToStore,儲存到記憶體中的區塊個數上限,預設100
MaxPropagationBurstSize int //取自peer.gossip.maxPropagationBurstSize,儲存的最大訊息個數,超過則轉發給其他節點,預設為10
MaxPropagationBurstLatency time.Duration //取自peer.gossip.maxPropagationBurstLatency,儲存訊息的最大時間,超過則轉發給其他節點,預設為10毫秒
PullInterval time.Duration //取自peer.gossip.pullInterval,拉取訊息的時間間隔,預設為4秒
PullPeerNum int //取自peer.gossip.pullPeerNum,從指定個數的節點拉取資訊,預設3個
SkipBlockVerification bool //取自peer.gossip.skipBlockVerification,是否不對區塊訊息進行校驗,預設false即需校驗
PublishCertPeriod time.Duration //取自peer.gossip.publishCertPeriod,包括在訊息中的啟動證書的時間,預設10s
PublishStateInfoInterval time.Duration //取自peer.gossip.publishStateInfoInterval,向其他節點推送狀態資訊的時間間隔,預設4s
RequestStateInfoInterval time.Duration //取自peer.gossip.requestStateInfoInterval,從其他節點拉取狀態資訊的時間間隔,預設4s
TLSServerCert *tls.Certificate //本節點TLS 證書
InternalEndpoint string //本節點在組織內使用的地址
ExternalEndpoint string //本節點對外部組織公佈的地址
}
//程式碼在gossip/gossip/gossip.go
```
### 4.3、Gossip介面實現
Gossip介面介面實現,即gossipServiceImpl結構體及方法。
#### 4.3.1、gossipServiceImpl結構體及方法
```go
type gossipServiceImpl struct {
selfIdentity api.PeerIdentityType
includeIdentityPeriod time.Time
certStore *certStore
idMapper identity.Mapper
presumedDead chan common.PKIidType
disc discovery.Discovery
comm comm.Comm
incTime time.Time
selfOrg api.OrgIdentityType
*comm.ChannelDeMultiplexer
logger *logging.Logger
stopSignal *sync.WaitGroup
conf *Config
toDieChan chan struct{}
stopFlag int32
emitter batchingEmitter
discAdapter *discoveryAdapter
secAdvisor api.SecurityAdvisor
chanState *channelState
disSecAdap *discoverySecurityAdapter
mcs api.MessageCryptoService
stateInfoMsgStore msgstore.MessageStore
}
func NewGossipService(conf *Config, s *grpc.Server, secAdvisor api.SecurityAdvisor,
func NewGossipServiceWithServer(conf *Config, secAdvisor api.SecurityAdvisor, mcs api.MessageCryptoService,
func (g *gossipServiceImpl) JoinChan(joinMsg api.JoinChannelMessage, chainID common.ChainID)
func (g *gossipServiceImpl) SuspectPeers(isSuspected api.PeerSuspector)
func (g *gossipServiceImpl) Gossip(msg *proto.GossipMessage)
func (g *gossipServiceImpl) Send(msg *proto.GossipMessage, peers ...*comm.RemotePeer)
func (g *gossipServiceImpl) Peers() []discovery.NetworkMember
func (g *gossipServiceImpl) PeersOfChannel(channel common.ChainID) []discovery.NetworkMember
func (g *gossipServiceImpl) Stop()
func (g *gossipServiceImpl) UpdateMetadata(md []byte)
func (g *gossipServiceImpl) UpdateChannelMetadata(md []byte, chainID common.ChainID)
func (g *gossipServiceImpl) Accept(acceptor common.MessageAcceptor, passThrough bool) (<-chan *proto.GossipMessage, <-chan proto.ReceivedMessage)
func (g *gossipServiceImpl) newStateInfoMsgStore() msgstore.MessageStore
func (g *gossipServiceImpl) selfNetworkMember() discovery.NetworkMember
func newChannelState(g *gossipServiceImpl) *channelState
func createCommWithoutServer(s *grpc.Server, cert *tls.Certificate, idStore identity.Mapper,
func createCommWithServer(port int, idStore identity.Mapper, identity api.PeerIdentityType,
func (g *gossipServiceImpl) toDie() bool
func (g *gossipServiceImpl) periodicalIdentityValidationAndExpiration()
func (g *gossipServiceImpl) periodicalIdentityValidation(suspectFunc api.PeerSuspector, interval time.Duration)
func (g *gossipServiceImpl) learnAnchorPeers(orgOfAnchorPeers api.OrgIdentityType, anchorPeers []api.AnchorPeer)
func (g *gossipServiceImpl) handlePresumedDead()
func (g *gossipServiceImpl) syncDiscovery()
func (g *gossipServiceImpl) start()
func (g *gossipServiceImpl) acceptMessages(incMsgs <-chan proto.ReceivedMessage)
func (g *gossipServiceImpl) handleMessage(m proto.ReceivedMessage)
func (g *gossipServiceImpl) forwardDiscoveryMsg(msg proto.ReceivedMessage)
func (g *gossipServiceImpl) validateMsg(msg proto.ReceivedMessage) bool
func (g *gossipServiceImpl) sendGossipBatch(a []interface{})
func (g *gossipServiceImpl) gossipBatch(msgs []*proto.SignedGossipMessage)
func (g *gossipServiceImpl) sendAndFilterSecrets(msg *proto.SignedGossipMessage, peers ...*comm.RemotePeer)
func (g *gossipServiceImpl) gossipInChan(messages []*proto.SignedGossipMessage, chanRoutingFactory channelRoutingFilterFactory)
func selectOnlyDiscoveryMessages(m interface{}) bool
func (g *gossipServiceImpl) newDiscoverySecurityAdapter() *discoverySecurityAdapter
func (sa *discoverySecurityAdapter) ValidateAliveMsg(m *proto.SignedGossipMessage) bool
func (sa *discoverySecurityAdapter) SignMessage(m *proto.GossipMessage, internalEndpoint string) *proto.Envelope
func (sa *discoverySecurityAdapter) validateAliveMsgSignature(m *proto.SignedGossipMessage, identity api.PeerIdentityType) bool
func (g *gossipServiceImpl) createCertStorePuller() pull.Mediator
func (g *gossipServiceImpl) sameOrgOrOurOrgPullFilter(msg proto.ReceivedMessage) func(string) bool
func (g *gossipServiceImpl) connect2BootstrapPeers()
func (g *gossipServiceImpl) createStateInfoMsg(metadata []byte, chainID common.ChainID) (*proto.SignedGossipMessage, error)
func (g *gossipServiceImpl) hasExternalEndpoint(PKIID common.PKIidType) bool
func (g *gossipServiceImpl) isInMyorg(member discovery.NetworkMember) bool
func (g *gossipServiceImpl) getOrgOfPeer(PKIID common.PKIidType) api.OrgIdentityType
func (g *gossipServiceImpl) validateLeadershipMessage(msg *proto.SignedGossipMessage) error
func (g *gossipServiceImpl) validateStateInfoMsg(msg *proto.SignedGossipMessage) error
func (g *gossipServiceImpl) disclosurePolicy(remotePeer *discovery.NetworkMember) (discovery.Sieve, discovery.EnvelopeFilter)
func (g *gossipServiceImpl) peersByOriginOrgPolicy(peer discovery.NetworkMember) filter.RoutingFilter
func partitionMessages(pred common.MessageAcceptor, a []*proto.SignedGossipMessage) ([]*proto.SignedGossipMessage, []*proto.SignedGossipMessage)
func extractChannels(a []*proto.SignedGossipMessage) []common.ChainID
func (g *gossipServiceImpl) newDiscoveryAdapter() *discoveryAdapter
func (da *discoveryAdapter) close()
func (da *discoveryAdapter) toDie() bool
func (da *discoveryAdapter) Gossip(msg *proto.SignedGossipMessage)
func (da *discoveryAdapter) SendToPeer(peer *discovery.NetworkMember, msg *proto.SignedGossipMessage)
func (da *discoveryAdapter) Ping(peer *discovery.NetworkMember) bool
func (da *discoveryAdapter) Accept() <-chan *proto.SignedGossipMessage
func (da *discoveryAdapter) PresumedDead() <-chan common.PKIidType
func (da *discoveryAdapter) CloseConn(peer *discovery.NetworkMember)
//程式碼在gossip/gossip/gossip_impl.go
```
#### 4.3.2、func NewGossipService(conf *Config, s *grpc.Server, secAdvisor api.SecurityAdvisor,mcs api.MessageCryptoService, idMapper identity.Mapper, selfIdentity api.PeerIdentityType,secureDialOpts api.PeerSecureDialOpts) Gossip
建立附加到grpc.Server上的Gossip例項。
```go
func NewGossipService(conf *Config, s *grpc.Server, secAdvisor api.SecurityAdvisor,
mcs api.MessageCryptoService, idMapper identity.Mapper, selfIdentity api.PeerIdentityType,
secureDialOpts api.PeerSecureDialOpts) Gossip {
var c comm.Comm
var err error
lgr := util.GetLogger(util.LoggingGossipModule, conf.ID)
if s == nil { 建立並啟動gRPC Server,以及註冊GossipServer例項
c, err = createCommWithServer(conf.BindPort, idMapper, selfIdentity, secureDialOpts)
} else { //將GossipServer例項註冊至peerServer
c, err = createCommWithoutServer(s, conf.TLSServerCert, idMapper, selfIdentity, secureDialOpts)
}
g := &gossipServiceImpl{
selfOrg: secAdvisor.OrgByPeerIdentity(selfIdentity),
secAdvisor: secAdvisor,
selfIdentity: selfIdentity,
presumedDead: make(chan common.PKIidType, presumedDeadChanSize),
idMapper: idMapper,
disc: nil,
mcs: mcs,
comm: c,
conf: conf,
ChannelDeMultiplexer: comm.NewChannelDemultiplexer(),
logger: lgr,
toDieChan: make(chan struct{}, 1),
stopFlag: int32(0),
stopSignal: &sync.WaitGroup{},
includeIdentityPeriod: time.Now().Add(conf.PublishCertPeriod),
}
g.stateInfoMsgStore = g.newStateInfoMsgStore()
g.chanState = newChannelState(g)
g.emitter = newBatchingEmitter(conf.PropagateIterations,
conf.MaxPropagationBurstSize, conf.MaxPropagationBurstLatency,
g.sendGossipBatch)
g.discAdapter = g.newDiscoveryAdapter()
g.disSecAdap = g.newDiscoverySecurityAdapter()
g.disc = discovery.NewDiscoveryService(g.selfNetworkMember(), g.discAdapter, g.disSecAdap, g.disclosurePolicy)
g.certStore = newCertStore(g.createCertStorePuller(), idMapper, selfIdentity, mcs)
go g.start()
go g.periodicalIdentityValidationAndExpiration()
go g.connect2BootstrapPeers()
return g
}
//程式碼在gossip/gossip/gossip_impl.go
```
#### 4.3.3、go g.start()
```go
func (g *gossipServiceImpl) start() {
go g.syncDiscovery()
go g.handlePresumedDead()
msgSelector := func(msg interface{}) bool {
gMsg, isGossipMsg := msg.(proto.ReceivedMessage)
if !isGossipMsg {
return false
}
isConn := gMsg.GetGossipMessage().GetConn() != nil
isEmpty := gMsg.GetGossipMessage().GetEmpty() != nil
return !(isConn || isEmpty)
}
incMsgs := g.comm.Accept(msgSelector)
go g.acceptMessages(incMsgs)
}
//程式碼在gossip/gossip/gossip_impl.go
```
go g.acceptMessages(incMsgs)程式碼如下:
```go
func (g *gossipServiceImpl) acceptMessages(incMsgs <-chan proto.ReceivedMessage) {
defer g.logger.Debug("Exiting")
g.stopSignal.Add(1)
defer g.stopSignal.Done()
for {
select {
case s := <-g.toDieChan:
g.toDieChan <- s
return
case msg := <-incMsgs:
g.handleMessage(msg) //此處會調取gc.HandleMessage(m),處理轉發訊息
}
}
}
//程式碼在gossip/gossip/gossip_impl.go
```
gc.HandleMessage(m)程式碼如下:
```go
func (gc *gossipChannel) HandleMessage(msg proto.ReceivedMessage) {
m := msg.GetGossipMessage()
orgID := gc.GetOrgOfPeer(msg.GetConnectionInfo().ID)
if m.IsDataMsg() || m.IsStateInfoMsg() {
added := false
if m.IsDataMsg() {
added = gc.blockMsgStore.Add(msg.GetGossipMessage())
} else {
added = gc.stateInfoMsgStore.Add(msg.GetGossipMessage())
}
if added {
gc.Gossip(msg.GetGossipMessage()) //轉發給其他節點,最終會呼叫func (g *gossipServiceImpl) gossipBatch(msgs []*proto.SignedGossipMessage)實現批量分發
gc.DeMultiplex(m)
if m.IsDataMsg() {
gc.blocksPuller.Add(msg.GetGossipMessage())
}
}
return
}
//...
}
//程式碼在gossip/gossip/channel/channel.go
```
func (g *gossipServiceImpl) gossipBatch(msgs []*proto.SignedGossipMessage)程式碼如下:
```go
func (g *gossipServiceImpl) gossipBatch(msgs []*proto.SignedGossipMessage) {
var blocks []*proto.SignedGossipMessage
var stateInfoMsgs []*proto.SignedGossipMessage
var orgMsgs []*proto.SignedGossipMessage
var leadershipMsgs []*proto.SignedGossipMessage
isABlock := func(o interface{}) bool {
return o.(*proto.SignedGossipMessage).IsDataMsg()
}
isAStateInfoMsg := func(o interface{}) bool {
return o.(*proto.SignedGossipMessage).IsStateInfoMsg()
}
isOrgRestricted := func(o interface{}) bool {
return aliveMsgsWithNoEndpointAndInOurOrg(o) || o.(*proto.SignedGossipMessage).IsOrgRestricted()
}
isLeadershipMsg := func(o interface{}) bool {
return o.(*proto.SignedGossipMessage).IsLeadershipMsg()
}
// Gossip blocks
blocks, msgs = partitionMessages(isABlock, msgs)
g.gossipInChan(blocks, func(gc channel.GossipChannel) filter.RoutingFilter {
return filter.CombineRoutingFilters(gc.EligibleForChannel, gc.IsMemberInChan, g.isInMyorg)
})
// Gossip Leadership messages
leadershipMsgs, msgs = partitionMessages(isLeadershipMsg, msgs)
g.gossipInChan(leadershipMsgs, func(gc channel.GossipChannel) filter.RoutingFilter {
return filter.CombineRoutingFilters(gc.EligibleForChannel, gc.IsMemberInChan, g.isInMyorg)
})
// Gossip StateInfo messages
stateInfoMsgs, msgs = partitionMessages(isAStateInfoMsg, msgs)
for _, stateInfMsg := range stateInfoMsgs {
peerSelector := g.isInMyorg
gc := g.chanState.lookupChannelForGossipMsg(stateInfMsg.GossipMessage)
if gc != nil && g.hasExternalEndpoint(stateInfMsg.GossipMessage.GetStateInfo().PkiId) {
peerSelector = gc.IsMemberInChan
}
peers2Send := filter.SelectPeers(g.conf.PropagatePeerNum, g.disc.GetMembership(), peerSelector)
g.comm.Send(stateInfMsg, peers2Send...)
}
// Gossip messages restricted to our org
orgMsgs, msgs = partitionMessages(isOrgRestricted, msgs)
peers2Send := filter.SelectPeers(g.conf.PropagatePeerNum, g.disc.GetMembership(), g.isInMyorg)
for _, msg := range orgMsgs {
g.comm.Send(msg, peers2Send...)
}
// Finally, gossip the remaining messages
for _, msg := range msgs {
selectByOriginOrg := g.peersByOriginOrgPolicy(discovery.NetworkMember{PKIid: msg.GetAliveMsg().Membership.PkiId})
peers2Send := filter.SelectPeers(g.conf.PropagatePeerNum, g.disc.GetMembership(), selectByOriginOrg)
g.sendAndFilterSecrets(msg, peers2Send...)
}
}
//程式碼在gossip/gossip/gossip_impl.go
```
## 5、GossipStateProvider介面定義及實現(狀態複製)
### 5.1、GossipStateProvider介面定義
通過狀態複製填充缺少的塊,以及傳送請求從其他節點獲取缺少的塊
```go
type GossipStateProvider interface {
//按索引獲取塊
GetBlock(index uint64) *common.Block
//新增塊
AddPayload(payload *proto.Payload) error
//終止狀態複製
Stop()
}
//程式碼在gossip/state/state.go
```
### 5.2、GossipStateProvider介面實現
```go
type GossipStateProviderImpl struct {
mcs api.MessageCryptoService //訊息加密服務
chainID string //Chain id
gossip GossipAdapter //gossiping service
gossipChan <-chan *proto.GossipMessage
commChan <-chan proto.ReceivedMessage
payloads PayloadsBuffer //Payloads佇列快取
committer committer.Committer
stateResponseCh chan proto.ReceivedMessage
stateRequestCh chan proto.ReceivedMessage
stopCh chan struct{}
done sync.WaitGroup
once sync.Once
stateTransferActive int32
}
func (s *GossipStateProviderImpl) GetBlock(index uint64) *common.Block
func (s *GossipStateProviderImpl) AddPayload(payload *proto.Payload) error
func (s *GossipStateProviderImpl) Stop()
func NewGossipStateProvider(chainID string, g GossipAdapter, committer committer.Committer, mcs api.MessageCryptoService) GossipStateProvider
func (s *GossipStateProviderImpl) listen()
func (s *GossipStateProviderImpl) directMessage(msg proto.ReceivedMessage)
func (s *GossipStateProviderImpl) processStateRequests()
func (s *GossipStateProviderImpl) handleStateRequest(msg proto.ReceivedMessage)
func (s *GossipStateProviderImpl) handleStateResponse(msg proto.ReceivedMessage) (uint64, error)
func (s *GossipStateProviderImpl) queueNewMessage(msg *proto.GossipMessage)
func (s *GossipStateProviderImpl) deliverPayloads()
func (s *GossipStateProviderImpl) antiEntropy()
func (s *GossipStateProviderImpl) maxAvailableLedgerHeight() uint64
func (s *GossipStateProviderImpl) requestBlocksInRange(start uint64, end uint64)
func (s *GossipStateProviderImpl) stateRequestMessage(beginSeq uint64, endSeq uint64) *proto.GossipMessage
func (s *GossipStateProviderImpl) selectPeerToRequestFrom(height uint64) (*comm.RemotePeer, error)
func (s *GossipStateProviderImpl) filterPeers(predicate func(peer discovery.NetworkMember) bool) []*comm.RemotePeer
func (s *GossipStateProviderImpl) hasRequiredHeight(height uint64) func(peer discovery.NetworkMember) bool
func (s *GossipStateProviderImpl) addPayload(payload *proto.Payload, blockingMode bool) error
func (s *GossipStateProviderImpl) commitBlock(block *common.Block) error
//程式碼在gossip/state/state.go
```
#### 5.2.1、func (s *GossipStateProviderImpl) AddPayload(payload *proto.Payload) error
```go
func (s *GossipStateProviderImpl) AddPayload(payload *proto.Payload) error {
blockingMode := blocking
if viper.GetBool("peer.gossip.nonBlockingCommitMode") { //非阻塞提交模式
blockingMode = false
}
return s.addPayload(payload, blockingMode)
}
//程式碼在gossip/state/state.go
```
s.addPayload(payload, blockingMode)程式碼如下:
```go
func (s *GossipStateProviderImpl) addPayload(payload *proto.Payload, blockingMode bool) error {
height, err := s.committer.LedgerHeight()
return s.payloads.Push(payload) //加入快取
}
//程式碼在gossip/state/state.go
```
#### 5.2.2、func NewGossipStateProvider(chainID string, g GossipAdapter, committer committer.Committer, mcs api.MessageCryptoService) GossipStateProvider
```go
func NewGossipStateProvider(chainID string, g GossipAdapter, committer committer.Committer, mcs api.MessageCryptoService) GossipStateProvider {
logger := util.GetLogger(util.LoggingStateModule, "")
gossipChan, _ := g.Accept(func(message interface{}) bool {
return message.(*proto.GossipMessage).IsDataMsg() &&
bytes.Equal(message.(*proto.GossipMessage).Channel, []byte(chainID))
}, false)
remoteStateMsgFilter := func(message interface{}) bool {
receivedMsg := message.(proto.ReceivedMessage)
msg := receivedMsg.GetGossipMessage()
connInfo := receivedMsg.GetConnectionInfo()
authErr := mcs.VerifyByChannel(msg.Channel, connInfo.Identity, connInfo.Auth.Signature, connInfo.Auth.SignedData)
return true
}
_, commChan := g.Accept(remoteStateMsgFilter, true)
height, err := committer.LedgerHeight()
s := &GossipStateProviderImpl{
mcs: mcs,
chainID: chainID,
gossip: g,
gossipChan: gossipChan,
commChan: commChan,
payloads: NewPayloadsBuffer(height),
committer: committer,
stateResponseCh: make(chan proto.ReceivedMessage, defChannelBufferSize),
stateRequestCh: make(chan proto.ReceivedMessage, defChannelBufferSize),
stopCh: make(chan struct{}, 1),
stateTransferActive: 0,
once: sync.Once{},
}
nodeMetastate := NewNodeMetastate(height - 1)
b, err := nodeMetastate.Bytes()
s.done.Add(4)
go s.listen()
go s.deliverPayloads() //處理從orderer獲取的塊
go s.antiEntropy() //處理缺失塊
go s.processStateRequests() //處理狀態請求訊息
return s
}
//程式碼在gossip/state/state.go
```
#### 5.2.3、func (s *GossipStateProviderImpl) deliverPayloads()
```go
func (s *GossipStateProviderImpl) deliverPayloads() {
defer s.done.Done()
for {
select {
case <-s.payloads.Ready():
for payload := s.payloads.Pop(); payload != nil; payload = s.payloads.Pop() {
rawBlock := &common.Block{}
err := pb.Unmarshal(payload.Data, rawBlock)
err := s.commitBlock(rawBlock)
}
case <-s.stopCh:
s.stopCh <- struct{}{}
logger.Debug("State provider has been stoped, finishing to push new blocks.")
return
}
}
}
func (s *GossipStateProviderImpl) commitBlock(block *common.Block) error {
err := s.committer.Commit(block)
nodeMetastate := NewNodeMetastate(block.Header.Number)
b, err := nodeMetastate.Bytes()
return nil
}
//程式碼在gossip/state/state.go
```
#### 5.2.4、func (s *GossipStateProviderImpl) antiEntropy()
定時獲取當前高度和節點中最大高度的差值
```go
func (s *GossipStateProviderImpl) antiEntropy() {
defer s.done.Done()
for {
select {
case <-s.stopCh:
s.stopCh <- struct{}{}
return
case <-time.After(defAntiEntropyInterval):
current, err := s.committer.LedgerHeight()
max := s.maxAvailableLedgerHeight() //最大高度
s.requestBlocksInRange(uint64(current), uint64(max))
}
}
}
//程式碼在gossip/state/state.go
```
s.requestBlocksInRange(uint64(current), uint64(max))程式碼如下:
```go
func (s *GossipStateProviderImpl) requestBlocksInRange(start uint64, end uint64) {
atomic.StoreInt32(&s.stateTransferActive, 1)
defer atomic.StoreInt32(&s.stateTransferActive, 0)
for prev := start; prev <= end; {
next := min(end, prev+defAntiEntropyBatchSize) //一批最大10個
gossipMsg := s.stateRequestMessage(prev, next) //構造GossipMessage_StateRequest
responseReceived := false
tryCounts := 0
for !responseReceived {
peer, err := s.selectPeerToRequestFrom(next) //確定peer
s.gossip.Send(gossipMsg, peer)
tryCounts++
select {
case msg := <-s.stateResponseCh:
if msg.GetGossipMessage().Nonce != gossipMsg.Nonce {
continue
}
index, err := s.handleStateResponse(msg)
prev = index + 1
responseReceived = true
case <-time.After(defAntiEntropyStateResponseTimeout):
case <-s.stopCh:
s.stopCh <- struct{}{}
return
}
}
}
}
//程式碼在gossip/state/state.go
```
index, err := s.handleStateResponse(msg)程式碼如下:
```go
func (s *GossipStateProviderImpl) handleStateResponse(msg proto.ReceivedMessage) (uint64, error) {
max := uint64(0)
response := msg.GetGossipMessage().GetStateResponse()
for _, payload := range response.GetPayloads() {
err := s.mcs.VerifyBlock(common2.ChainID(s.chainID), payload.SeqNum, payload.Data)
err := s.addPayload(payload, blocking) //存入本地
}
return max, nil
}
//程式碼在gossip/state/state.go
```
committer更詳細內容,參考:[Fabric 1.0原始碼筆記 之 Peer #committer(提交者)](../peer/committer.md)
#### 5.2.5、func (s *GossipStateProviderImpl) listen()
```go
func (s *GossipStateProviderImpl) listen() {
defer s.done.Done()
for {
select {
case msg := <-s.gossipChan:
//處理從通道中其他節點分發的訊息
go s.queueNewMessage(msg)
case msg := <-s.commChan:
logger.Debug("Direct message ", msg)
go s.directMessage(msg)
case <-s.stopCh:
s.stopCh <- struct{}{}
return
}
}
}
//程式碼在gossip/state/state.go
```
go s.queueNewMessage(msg)程式碼如下:
```go
func (s *GossipStateProviderImpl) queueNewMessage(msg *proto.GossipMessage) {
dataMsg := msg.GetDataMsg()
if dataMsg != nil {
err := s.addPayload(dataMsg.GetPayload(), nonBlocking) //寫入本地
}
}
//程式碼在gossip/state/state.go
```
## 10、MessageCryptoService介面及實現
MessageCryptoService介面定義:訊息加密服務。
```go
type MessageCryptoService interface {
//獲取Peer身份的PKI ID(Public Key Infrastructure,公鑰基礎設施)
GetPKIidOfCert(peerIdentity PeerIdentityType) common.PKIidType
//校驗塊簽名
VerifyBlock(chainID common.ChainID, seqNum uint64, signedBlock []byte) error
//使用Peer的簽名金鑰對訊息簽名
Sign(msg []byte) ([]byte, error)
//校驗簽名是否為有效簽名
Verify(peerIdentity PeerIdentityType, signature, message []byte) error
//在特定通道下校驗簽名是否為有效簽名
VerifyByChannel(chainID common.ChainID, peerIdentity PeerIdentityType, signature, message []byte) error
//校驗Peer身份
ValidateIdentity(peerIdentity PeerIdentityType) error
}
//程式碼在gossip/api/crypto.go
```
MessageCryptoService介面實現,即mspMessageCryptoService結構體及方法:
```go
type mspMessageCryptoService struct {
channelPolicyManagerGetter policies.ChannelPolicyManagerGetter //通道策略管理器,type ChannelPolicyManagerGetter interface
localSigner crypto.LocalSigner //本地簽名者,type LocalSigner interface
deserializer mgmt.DeserializersManager //反序列化管理器,type DeserializersManager interface
}
//構造mspMessageCryptoService
func NewMCS(channelPolicyManagerGetter policies.ChannelPolicyManagerGetter, localSigner crypto.LocalSigner, deserializer mgmt.DeserializersManager) api.MessageCryptoService
//調取s.getValidatedIdentity(peerIdentity)
func (s *mspMessageCryptoService) ValidateIdentity(peerIdentity api.PeerIdentityType) error
func (s *mspMessageCryptoService) GetPKIidOfCert(peerIdentity api.PeerIdentityType) common.PKIidType
func (s *mspMessageCryptoService) VerifyBlock(chainID common.ChainID, seqNum uint64, signedBlock []byte) error
func (s *mspMessageCryptoService) Sign(msg []byte) ([]byte, error)
func (s *mspMessageCryptoService) Verify(peerIdentity api.PeerIdentityType, signature, message []byte) error
func (s *mspMessageCryptoService) VerifyByChannel(chainID common.ChainID, peerIdentity api.PeerIdentityType, signature, message []byte) error
func (s *mspMessageCryptoService) getValidatedIdentity(peerIdentity api.PeerIdentityType) (msp.Identity, common.ChainID, error)
//程式碼在peer/gossip/mcs.go
```
## 20、本文使用的網路內容
* [hyperledger fabric 程式碼分析之 gossip 協議](https://zhuanlan.zhihu.com/p/27989809)
* [fabric原始碼解析14——peer的gossip服務之初始化](http://blog.csdn.net/idsuf698987/article/details/77898724)
網址:http://www.qukuailianxueyuan.io/
欲領取造幣技術與全套虛擬機器資料
區塊鏈技術交流QQ群:756146052 備註:CSDN
尹成學院微信:備註:CSDN
網址:http://www.qukuailianxueyuan.io/
欲領取造幣技術與全套虛擬機器資料
區塊鏈技術交流QQ群:756146052 備註:CSDN
尹成學院微信:備註:CSDN
相關文章
- Fabric 1.0原始碼分析(16)gossip(流言演算法) #GossipServer(Gossip服務端)原始碼Go演算法Server服務端
- Fabric 1.0原始碼分析(17)gossip(流言演算法) #deliverclient(deliver客戶端)原始碼Go演算法client客戶端
- 區塊鏈教程Fabric1.0原始碼分析流言演算法Gossip服務端二-兄弟連區塊鏈原始碼演算法Go服務端
- 區塊鏈教程Fabric1.0原始碼分析流言演算法Gossip服務端一兄弟連區塊鏈教程區塊鏈原始碼演算法Go服務端
- Fabric 1.0原始碼分析(25) Orderer原始碼
- Fabric 1.0原始碼分析(31) Peer原始碼
- Fabric 1.0原始碼分析(3)Chaincode(鏈碼)原始碼AI
- Fabric 1.0原始碼分析(40) Proposal(提案)原始碼
- Fabric 1.0原始碼分析(14) flogging(Fabric日誌系統)原始碼
- Fabric 1.0原始碼分析(18) Ledger(賬本)原始碼
- Fabric 1.0原始碼分析(43) Tx(Transaction 交易)原始碼
- Fabric 1.0原始碼分析(47)Fabric 1.0.4 go程式碼量統計原始碼Go
- Fabric 1.0原始碼分析(42)scc(系統鏈碼)原始碼
- Fabric 1.0原始碼分析(13)events(事件服務)原始碼事件
- Fabric 1.0原始碼分析(26)Orderer #ledger(Orderer Ledger)原始碼
- Fabric 1.0原始碼分析(39) policy(背書策略)原始碼
- Fabric 1.0原始碼分析(45)gRPC(Fabric中註冊的gRPC Service)原始碼RPC
- Fabric 1.0原始碼分析(10)consenter(共識外掛)原始碼
- Fabric 1.0原始碼分析(23)LevelDB(KV資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(44)Tx #RWSet(讀寫集)原始碼
- Fabric 1.0原始碼分析(5)Chaincode(鏈碼)體系總結原始碼AI
- Fabric 1.0原始碼分析(6)configtx(配置交易) #ChannelConfig(通道配置)原始碼
- Fabric 1.0原始碼分析(20) Ledger #idStore(ledgerID資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(29) Orderer #multichain(多鏈支援包)原始碼AI
- Fabric 1.0原始碼分析(30) Orderer #BroadcastServer(Broadcast服務端)原始碼ASTServer服務端
- Fabric 1.0原始碼分析(35)Peer #EndorserServer(Endorser服務端)原始碼Server服務端
- Fabric 1.0原始碼分析(36) Peer #EndorserClient(Endorser客戶端)原始碼client客戶端
- Fabric 1.0原始碼分析(37) Peer #DeliverClient(Deliver客戶端)原始碼client客戶端
- Fabric 1.0原始碼分析(38) Peer #BroadcastClient(Broadcast客戶端)原始碼ASTclient客戶端
- Fabric 1.0原始碼分析(41)putils(protos/utils工具包)原始碼
- 基於Gossip流言演算法實現alertmanager高可用Go演算法
- Fabric 1.0原始碼分析(46)ECDSA(橢圓曲線數字簽名演算法)原始碼演算法
- Fabric 1.0原始碼分析(2) blockfile(區塊檔案儲存)原始碼BloC
- Fabric 1.0原始碼分析(7)configtx(配置交易) #configtxgen(生成通道配置)原始碼
- Fabric 1.0原始碼分析(9)configtx(配置交易)體系介紹原始碼
- Fabric 1.0原始碼分析(19) Ledger #statedb(狀態資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(21)Ledger #historydb(歷史資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(22)Ledger #blkstorage(block檔案儲存)原始碼BloC