github.com/cranelv/ethereum_mpc@v0.0.0-20191031014521-23aeb1415092/consensus_pbft/pbft/broadcast.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package pbft
    18  
    19  import (
    20  	"fmt"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/consensus_pbft"
    25  	"github.com/ethereum/go-ethereum/consensus_pbft/pbftTypes"
    26  	"github.com/ethereum/go-ethereum/consensus_pbft/message"
    27  	"github.com/ethereum/go-ethereum/consensus_pbft/singletons"
    28  	"github.com/ethereum/go-ethereum/consensus_pbft/consensusInterface"
    29  )
    30  
    31  
    32  type broadcaster struct {
    33  	comm consensus_pbft.NetworkStack
    34  	identify consensusInterface.ValidatorIdentifyInterface
    35  	f                uint32
    36  	broadcastTimeout time.Duration
    37  	msgChans         map[pbftTypes.ReplicaID]chan *sendRequest
    38  	closed           sync.WaitGroup
    39  	closedCh         chan struct{}
    40  }
    41  
    42  type sendRequest struct {
    43  	msg  *message.Message
    44  	done chan bool
    45  }
    46  
    47  func newBroadcaster(self pbftTypes.ReplicaID, N uint32, f uint32, broadcastTimeout time.Duration,
    48  	identify consensusInterface.ValidatorIdentifyInterface, c consensus_pbft.NetworkStack) *broadcaster {
    49  	queueSize := 10 // XXX increase after testing
    50  
    51  	chans := make(map[pbftTypes.ReplicaID]chan *sendRequest)
    52  	b := &broadcaster{
    53  		comm:             c,
    54  		identify: 		  identify,
    55  		f:                f,
    56  		broadcastTimeout: broadcastTimeout,
    57  		msgChans:         chans,
    58  		closedCh:         make(chan struct{}),
    59  	}
    60  	for i := uint32(0); i < N; i++ {
    61  		if pbftTypes.ReplicaID(i) == self {
    62  			continue
    63  		}
    64  		chans[pbftTypes.ReplicaID(i)] = make(chan *sendRequest, queueSize)
    65  	}
    66  
    67  	// We do not start the go routines in the above loop to avoid concurrent map read/writes
    68  	for i := uint32(0); i < N; i++ {
    69  		if pbftTypes.ReplicaID(i) == self {
    70  			continue
    71  		}
    72  		go b.drainer(pbftTypes.ReplicaID(i))
    73  	}
    74  
    75  	return b
    76  }
    77  
    78  func (b *broadcaster) Close() {
    79  	close(b.closedCh)
    80  	b.closed.Wait()
    81  }
    82  
    83  func (b *broadcaster) Wait() {
    84  	b.closed.Wait()
    85  }
    86  
    87  func (b *broadcaster) drainerSend(dest pbftTypes.ReplicaID, send *sendRequest, successLastTime bool) bool {
    88  	// Note, successLastTime is purely used to avoid flooding the log with unnecessary warning messages when a network problem is encountered
    89  	singletons.Log.Info("broadcaster drainerSend:","dest",dest)
    90  	defer func() {
    91  		b.closed.Done()
    92  	}()
    93  	nodeID, err := b.identify.GetValidatorNodeId(dest)
    94  	if err != nil {
    95  		if successLastTime {
    96  			singletons.Log.Warnf("could not get handle for replica %d", dest)
    97  		}
    98  		send.done <- false
    99  		return false
   100  	}
   101  
   102  	err = b.comm.Unicast(send.msg, nodeID)
   103  	if err != nil {
   104  		if successLastTime {
   105  			singletons.Log.Warnf("could not send to replica %d: %v", dest, err)
   106  		}
   107  		send.done <- false
   108  		return false
   109  	}
   110  
   111  	send.done <- true
   112  	return true
   113  
   114  }
   115  
   116  func (b *broadcaster) drainer(dest pbftTypes.ReplicaID) {
   117  	successLastTime := false
   118  	destChan, exsit := b.msgChans[dest] // Avoid doing the map lookup every send
   119  	if !exsit {
   120  		singletons.Log.Warnf("could not get message channel for replica %d", dest)
   121  		return
   122  	}
   123  
   124  	for {
   125  		select {
   126  		case send := <-destChan:
   127  			successLastTime = b.drainerSend(dest, send, successLastTime)
   128  		case <-b.closedCh:
   129  			for {
   130  				// Drain the message channel to free calling waiters before we shut down
   131  				select {
   132  				case send := <-destChan:
   133  					send.done <- false
   134  					b.closed.Done()
   135  				default:
   136  					return
   137  				}
   138  			}
   139  		}
   140  	}
   141  }
   142  
   143  func (b *broadcaster) unicastOne(msg *message.Message, dest pbftTypes.ReplicaID, wait chan bool) {
   144  	select {
   145  	case b.msgChans[dest] <- &sendRequest{
   146  		msg:  msg,
   147  		done: wait,
   148  	}:
   149  	default:
   150  		// If this channel is full, we must discard the message and flag it as done
   151  		singletons.Log.Error("unicastOne default")
   152  		wait <- false
   153  		b.closed.Done()
   154  	}
   155  }
   156  
   157  func (b *broadcaster) send(msg *message.Message, dest *pbftTypes.ReplicaID) error {
   158  	select {
   159  	case <-b.closedCh:
   160  		return fmt.Errorf("broadcaster closed")
   161  	default:
   162  	}
   163  
   164  	var destCount uint32
   165  	var required uint32
   166  	if dest != nil {
   167  		destCount = 1
   168  		required = 1
   169  	} else {
   170  		destCount = uint32(len(b.msgChans))
   171  		required = destCount - b.f
   172  	}
   173  
   174  	wait := make(chan bool, destCount)
   175  
   176  	if dest != nil {
   177  		b.closed.Add(1)
   178  		b.unicastOne(msg, *dest, wait)
   179  	} else {
   180  		b.closed.Add(len(b.msgChans))
   181  		for i := range b.msgChans {
   182  			b.unicastOne(msg, i, wait)
   183  		}
   184  	}
   185  
   186  	succeeded := uint32(0)
   187  	timer := time.NewTimer(b.broadcastTimeout)
   188  
   189  	// This loop will try to send, until one of:
   190  	// a) the required number of sends succeed
   191  	// b) all sends complete regardless of success
   192  	// c) the timeout expires and the required number of sends have returned
   193  outer:
   194  	for i := uint32(0); i < destCount; i++ {
   195  		select {
   196  		case success := <-wait:
   197  			if success {
   198  				succeeded++
   199  				if succeeded >= required {
   200  					break outer
   201  				}
   202  			}
   203  		case <-timer.C:
   204  			for i := i; i < required; i++ {
   205  				<-wait
   206  			}
   207  			break outer
   208  		}
   209  	}
   210  
   211  	return nil
   212  }
   213  
   214  func (b *broadcaster) Unicast(msg *message.Message, dest pbftTypes.ReplicaID) error {
   215  	return b.send(msg, &dest)
   216  }
   217  
   218  func (b *broadcaster) Broadcast(msg *message.Message) error {
   219  	return b.send(msg, nil)
   220  }