github.com/braveheart12/just@v0.8.7/pulsar/pulsarconsensus.go (about) 1 /* 2 * Copyright 2019 Insolar Technologies 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 pulsar 18 19 import ( 20 "context" 21 "crypto" 22 "sync" 23 24 "github.com/insolar/insolar/core" 25 "github.com/insolar/insolar/instrumentation/inslogger" 26 "github.com/insolar/insolar/instrumentation/instracer" 27 "github.com/pkg/errors" 28 ) 29 30 // SetBftGridItem set item of the bftGrid in the thread-safe way 31 func (currentPulsar *Pulsar) SetBftGridItem(key string, value map[string]*BftCell) { 32 currentPulsar.BftGridLock.Lock() 33 currentPulsar.bftGrid[key] = value 34 defer currentPulsar.BftGridLock.Unlock() 35 } 36 37 // GetBftGridItem returns a grid item i nthe thread-safe way 38 func (currentPulsar *Pulsar) GetBftGridItem(row string, column string) *BftCell { 39 currentPulsar.BftGridLock.RLock() 40 defer currentPulsar.BftGridLock.RUnlock() 41 return currentPulsar.bftGrid[row][column] 42 } 43 44 // BftCell is a cell in NxN btf-grid 45 type BftCell struct { 46 signLock sync.RWMutex 47 entropyLock sync.RWMutex 48 isEntropyReceivedLock sync.RWMutex 49 50 Sign []byte 51 Entropy core.Entropy 52 IsEntropyReceived bool 53 } 54 55 // SetSign sets Sign in the thread-safe way 56 func (bftCell *BftCell) SetSign(sign []byte) { 57 bftCell.signLock.Lock() 58 defer bftCell.signLock.Unlock() 59 bftCell.Sign = sign 60 } 61 62 // GetSign gets Sign in the thread-safe way 63 func (bftCell *BftCell) GetSign() []byte { 64 bftCell.signLock.RLock() 65 defer bftCell.signLock.RUnlock() 66 return bftCell.Sign 67 } 68 69 // SetEntropy sets Entropy in the thread-safe way 70 func (bftCell *BftCell) SetEntropy(entropy core.Entropy) { 71 bftCell.entropyLock.Lock() 72 defer bftCell.entropyLock.Unlock() 73 bftCell.Entropy = entropy 74 } 75 76 // GetEntropy gets Entropy in the thread-safe way 77 func (bftCell *BftCell) GetEntropy() core.Entropy { 78 bftCell.entropyLock.RLock() 79 defer bftCell.entropyLock.RUnlock() 80 return bftCell.Entropy 81 } 82 83 // SetIsEntropyReceived sets IsEntropyReceived in the thread-safe way 84 func (bftCell *BftCell) SetIsEntropyReceived(isEntropyReceived bool) { 85 bftCell.isEntropyReceivedLock.Lock() 86 defer bftCell.isEntropyReceivedLock.Unlock() 87 bftCell.IsEntropyReceived = isEntropyReceived 88 } 89 90 // GetIsEntropyReceived gets IsEntropyReceived in the thread-safe way 91 func (bftCell *BftCell) GetIsEntropyReceived() bool { 92 bftCell.isEntropyReceivedLock.RLock() 93 defer bftCell.isEntropyReceivedLock.RUnlock() 94 return bftCell.IsEntropyReceived 95 } 96 97 func (currentPulsar *Pulsar) verify(ctx context.Context) { 98 ctx, span := instracer.StartSpan(ctx, "Pulsar.verify") 99 defer span.End() 100 101 logger := inslogger.FromContext(ctx) 102 logger.Debugf("[verify] - %v", currentPulsar.Config.MainListenerAddress) 103 104 if currentPulsar.IsStateFailed() { 105 return 106 107 } 108 if currentPulsar.isStandalone() { 109 currentPulsar.SetCurrentSlotEntropy(currentPulsar.GetGeneratedEntropy()) 110 currentPulsar.CurrentSlotPulseSender = currentPulsar.PublicKeyRaw 111 currentPulsar.StateSwitcher.SwitchToState(ctx, SendingPulse, nil) 112 return 113 } 114 115 type bftMember struct { 116 PubPem string 117 PubKey crypto.PublicKey 118 } 119 120 var finalEntropySet []core.Entropy 121 122 keys := []string{currentPulsar.PublicKeyRaw} 123 activePulsars := []*bftMember{{currentPulsar.PublicKeyRaw, currentPulsar.PublicKey}} 124 for key, neighbour := range currentPulsar.Neighbours { 125 activePulsars = append(activePulsars, &bftMember{key, neighbour.PublicKey}) 126 keys = append(keys, key) 127 } 128 129 // Check NxN consensus-matrix 130 wrongVectors := 0 131 for _, column := range activePulsars { 132 currentColumnStat := map[string]int{} 133 for _, row := range activePulsars { 134 bftCell := currentPulsar.GetBftGridItem(row.PubPem, column.PubPem) 135 136 if bftCell == nil { 137 currentColumnStat["nil"]++ 138 continue 139 } 140 141 publicKey, err := currentPulsar.KeyProcessor.ImportPublicKeyPEM([]byte(column.PubPem)) 142 if err != nil { 143 currentColumnStat["nil"]++ 144 continue 145 } 146 147 entropy := bftCell.GetEntropy() 148 ok := currentPulsar.CryptographyService.Verify(publicKey, core.SignatureFromBytes(bftCell.GetSign()), entropy[:]) 149 if !ok { 150 currentColumnStat["nil"]++ 151 continue 152 } 153 154 currentColumnStat[string(entropy[:])]++ 155 } 156 157 maxConfirmationsForEntropy := int(0) 158 var chosenEntropy core.Entropy 159 for key, value := range currentColumnStat { 160 if value > maxConfirmationsForEntropy && key != "nil" { 161 maxConfirmationsForEntropy = value 162 copy(chosenEntropy[:], []byte(key)[:core.EntropySize]) 163 } 164 } 165 166 if maxConfirmationsForEntropy >= currentPulsar.getMinimumNonTraitorsCount() { 167 finalEntropySet = append(finalEntropySet, chosenEntropy) 168 } else { 169 wrongVectors++ 170 } 171 } 172 173 if len(finalEntropySet) == 0 || wrongVectors > currentPulsar.getMaxTraitorsCount() { 174 currentPulsar.StateSwitcher.SwitchToState( 175 ctx, 176 Failed, 177 errors.Errorf("bft is broken. len(finalEntropySet) == %v, wrongVectors - %v", len(finalEntropySet), wrongVectors), 178 ) 179 return 180 } 181 182 var finalEntropy core.Entropy 183 184 for _, tempEntropy := range finalEntropySet { 185 for byteIndex := 0; byteIndex < core.EntropySize; byteIndex++ { 186 finalEntropy[byteIndex] ^= tempEntropy[byteIndex] 187 } 188 } 189 currentPulsar.finalizeBft(ctx, finalEntropy, keys) 190 } 191 192 func (currentPulsar *Pulsar) finalizeBft(ctx context.Context, finalEntropy core.Entropy, activePulsars []string) { 193 ctx, span := instracer.StartSpan(ctx, "Pulsar.finalizeBft") 194 defer span.End() 195 196 currentPulsar.SetCurrentSlotEntropy(&finalEntropy) 197 chosenPulsar, err := selectByEntropy( 198 currentPulsar.PlatformCryptographyScheme, finalEntropy, activePulsars, 1) 199 if err != nil { 200 currentPulsar.StateSwitcher.SwitchToState(ctx, Failed, err) 201 } 202 currentPulsar.CurrentSlotPulseSender = chosenPulsar[0] 203 if currentPulsar.CurrentSlotPulseSender == currentPulsar.PublicKeyRaw { 204 //here confirmation myself 205 payload := PulseSenderConfirmationPayload{core.PulseSenderConfirmation{ 206 ChosenPublicKey: currentPulsar.CurrentSlotPulseSender, 207 Entropy: *currentPulsar.GetCurrentSlotEntropy(), 208 PulseNumber: currentPulsar.ProcessingPulseNumber, 209 }} 210 hashProvider := currentPulsar.PlatformCryptographyScheme.IntegrityHasher() 211 hash, err := payload.Hash(hashProvider) 212 if err != nil { 213 currentPulsar.StateSwitcher.SwitchToState(ctx, Failed, err) 214 return 215 } 216 signature, err := currentPulsar.CryptographyService.Sign(hash) 217 if err != nil { 218 currentPulsar.StateSwitcher.SwitchToState(ctx, Failed, err) 219 return 220 } 221 222 currentPulsar.currentSlotSenderConfirmationsLock.Lock() 223 currentPulsar.CurrentSlotSenderConfirmations[currentPulsar.PublicKeyRaw] = core.PulseSenderConfirmation{ 224 ChosenPublicKey: currentPulsar.CurrentSlotPulseSender, 225 Signature: signature.Bytes(), 226 Entropy: *currentPulsar.GetCurrentSlotEntropy(), 227 PulseNumber: currentPulsar.ProcessingPulseNumber, 228 } 229 currentPulsar.currentSlotSenderConfirmationsLock.Unlock() 230 231 currentPulsar.StateSwitcher.SwitchToState(ctx, WaitingForPulseSigns, nil) 232 } else { 233 currentPulsar.StateSwitcher.SwitchToState(ctx, SendingPulseSign, nil) 234 } 235 }