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 }