github.com/klaytn/klaytn@v1.12.1/consensus/istanbul/backend/backend.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2017 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from quorum/consensus/istanbul/backend/backend.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package backend 22 23 import ( 24 "crypto/ecdsa" 25 "math/big" 26 "sync" 27 "sync/atomic" 28 "time" 29 30 lru "github.com/hashicorp/golang-lru" 31 "github.com/klaytn/klaytn/blockchain" 32 "github.com/klaytn/klaytn/blockchain/types" 33 "github.com/klaytn/klaytn/common" 34 "github.com/klaytn/klaytn/consensus" 35 "github.com/klaytn/klaytn/consensus/istanbul" 36 istanbulCore "github.com/klaytn/klaytn/consensus/istanbul/core" 37 "github.com/klaytn/klaytn/consensus/istanbul/validator" 38 "github.com/klaytn/klaytn/crypto" 39 "github.com/klaytn/klaytn/crypto/bls" 40 "github.com/klaytn/klaytn/event" 41 "github.com/klaytn/klaytn/governance" 42 "github.com/klaytn/klaytn/log" 43 "github.com/klaytn/klaytn/reward" 44 "github.com/klaytn/klaytn/storage/database" 45 ) 46 47 const ( 48 // fetcherID is the ID indicates the block is from Istanbul engine 49 fetcherID = "istanbul" 50 ) 51 52 var logger = log.NewModuleLogger(log.ConsensusIstanbulBackend) 53 54 type BackendOpts struct { 55 IstanbulConfig *istanbul.Config // Istanbul consensus core config 56 Rewardbase common.Address 57 PrivateKey *ecdsa.PrivateKey // Consensus message signing key 58 BlsSecretKey bls.SecretKey // Randao signing key. Required since Randao fork 59 DB database.DBManager 60 Governance governance.Engine // Governance parameter provider 61 BlsPubkeyProvider BlsPubkeyProvider // If not nil, override the default BLS public key provider 62 NodeType common.ConnType 63 } 64 65 func New(opts *BackendOpts) consensus.Istanbul { 66 recents, _ := lru.NewARC(inmemorySnapshots) 67 recentMessages, _ := lru.NewARC(inmemoryPeers) 68 knownMessages, _ := lru.NewARC(inmemoryMessages) 69 backend := &backend{ 70 config: opts.IstanbulConfig, 71 istanbulEventMux: new(event.TypeMux), 72 privateKey: opts.PrivateKey, 73 address: crypto.PubkeyToAddress(opts.PrivateKey.PublicKey), 74 blsSecretKey: opts.BlsSecretKey, 75 logger: logger.NewWith(), 76 db: opts.DB, 77 commitCh: make(chan *types.Result, 1), 78 recents: recents, 79 candidates: make(map[common.Address]bool), 80 coreStarted: false, 81 recentMessages: recentMessages, 82 knownMessages: knownMessages, 83 rewardbase: opts.Rewardbase, 84 governance: opts.Governance, 85 blsPubkeyProvider: opts.BlsPubkeyProvider, 86 nodetype: opts.NodeType, 87 rewardDistributor: reward.NewRewardDistributor(opts.Governance), 88 } 89 if backend.blsPubkeyProvider == nil { 90 backend.blsPubkeyProvider = newChainBlsPubkeyProvider() 91 } 92 93 backend.currentView.Store(&istanbul.View{Sequence: big.NewInt(0), Round: big.NewInt(0)}) 94 backend.core = istanbulCore.New(backend, backend.config) 95 return backend 96 } 97 98 // ---------------------------------------------------------------------------- 99 100 type backend struct { 101 config *istanbul.Config 102 istanbulEventMux *event.TypeMux 103 privateKey *ecdsa.PrivateKey 104 address common.Address 105 blsSecretKey bls.SecretKey 106 core istanbulCore.Engine 107 logger log.Logger 108 db database.DBManager 109 chain consensus.ChainReader 110 currentBlock func() *types.Block 111 hasBadBlock func(hash common.Hash) bool 112 113 // the channels for istanbul engine notifications 114 commitCh chan *types.Result 115 proposedBlockHash common.Hash 116 sealMu sync.Mutex 117 coreStarted bool 118 coreMu sync.RWMutex 119 120 // Current list of candidates we are pushing 121 candidates map[common.Address]bool 122 // Protects the signer fields 123 candidatesLock sync.RWMutex 124 // Snapshots for recent block to speed up reorgs 125 recents *lru.ARCCache 126 127 // event subscription for ChainHeadEvent event 128 broadcaster consensus.Broadcaster 129 130 recentMessages *lru.ARCCache // the cache of peer's messages 131 knownMessages *lru.ARCCache // the cache of self messages 132 133 rewardbase common.Address 134 currentView atomic.Value //*istanbul.View 135 136 // Reference to the governance.Engine 137 governance governance.Engine 138 139 // Reference to BlsPubkeyProvider 140 blsPubkeyProvider BlsPubkeyProvider 141 142 rewardDistributor *reward.RewardDistributor 143 144 // Node type 145 nodetype common.ConnType 146 147 isRestoringSnapshots atomic.Bool 148 } 149 150 func (sb *backend) NodeType() common.ConnType { 151 return sb.nodetype 152 } 153 154 func (sb *backend) GetRewardBase() common.Address { 155 return sb.rewardbase 156 } 157 158 func (sb *backend) SetCurrentView(view *istanbul.View) { 159 sb.currentView.Store(view) 160 } 161 162 // Address implements istanbul.Backend.Address 163 func (sb *backend) Address() common.Address { 164 return sb.address 165 } 166 167 // Validators implements istanbul.Backend.Validators 168 func (sb *backend) Validators(proposal istanbul.Proposal) istanbul.ValidatorSet { 169 return sb.getValidators(proposal.Number().Uint64(), proposal.Hash()) 170 } 171 172 // Broadcast implements istanbul.Backend.Broadcast 173 func (sb *backend) Broadcast(prevHash common.Hash, valSet istanbul.ValidatorSet, payload []byte) error { 174 // send to others 175 // TODO Check gossip again in event handle 176 // sb.Gossip(valSet, payload) 177 // send to self 178 msg := istanbul.MessageEvent{ 179 Hash: prevHash, 180 Payload: payload, 181 } 182 go sb.istanbulEventMux.Post(msg) 183 return nil 184 } 185 186 // Broadcast implements istanbul.Backend.Gossip 187 func (sb *backend) Gossip(valSet istanbul.ValidatorSet, payload []byte) error { 188 hash := istanbul.RLPHash(payload) 189 sb.knownMessages.Add(hash, true) 190 191 if sb.broadcaster != nil { 192 ps := sb.broadcaster.GetCNPeers() 193 for addr, p := range ps { 194 ms, ok := sb.recentMessages.Get(addr) 195 var m *lru.ARCCache 196 if ok { 197 m, _ = ms.(*lru.ARCCache) 198 if _, k := m.Get(hash); k { 199 // This peer had this event, skip it 200 continue 201 } 202 } else { 203 m, _ = lru.NewARC(inmemoryMessages) 204 } 205 206 m.Add(hash, true) 207 sb.recentMessages.Add(addr, m) 208 209 cmsg := &istanbul.ConsensusMsg{ 210 PrevHash: common.Hash{}, 211 Payload: payload, 212 } 213 214 // go p.Send(IstanbulMsg, payload) 215 go p.Send(IstanbulMsg, cmsg) 216 } 217 } 218 return nil 219 } 220 221 // checkInSubList checks if the node is in a sublist 222 func (sb *backend) checkInSubList(prevHash common.Hash, valSet istanbul.ValidatorSet) bool { 223 return valSet.CheckInSubList(prevHash, sb.currentView.Load().(*istanbul.View), sb.Address()) 224 } 225 226 // getTargetReceivers returns a map of nodes which need to receive a message 227 func (sb *backend) getTargetReceivers(prevHash common.Hash, valSet istanbul.ValidatorSet) map[common.Address]bool { 228 targets := make(map[common.Address]bool) 229 230 cv, ok := sb.currentView.Load().(*istanbul.View) 231 if !ok { 232 logger.Error("Failed to assert type from sb.currentView!!", "cv", cv) 233 return nil 234 } 235 view := &istanbul.View{ 236 Round: big.NewInt(cv.Round.Int64()), 237 Sequence: big.NewInt(cv.Sequence.Int64()), 238 } 239 240 proposer := valSet.GetProposer() 241 for i := 0; i < 2; i++ { 242 committee := valSet.SubListWithProposer(prevHash, proposer.Address(), view) 243 for _, val := range committee { 244 if val.Address() != sb.Address() { 245 targets[val.Address()] = true 246 } 247 } 248 view.Round = view.Round.Add(view.Round, common.Big1) 249 proposer = valSet.Selector(valSet, common.Address{}, view.Round.Uint64()) 250 } 251 return targets 252 } 253 254 // GossipSubPeer implements istanbul.Backend.Gossip 255 func (sb *backend) GossipSubPeer(prevHash common.Hash, valSet istanbul.ValidatorSet, payload []byte) map[common.Address]bool { 256 if !sb.checkInSubList(prevHash, valSet) { 257 return nil 258 } 259 260 hash := istanbul.RLPHash(payload) 261 sb.knownMessages.Add(hash, true) 262 263 targets := sb.getTargetReceivers(prevHash, valSet) 264 265 if sb.broadcaster != nil && len(targets) > 0 { 266 ps := sb.broadcaster.FindCNPeers(targets) 267 for addr, p := range ps { 268 ms, ok := sb.recentMessages.Get(addr) 269 var m *lru.ARCCache 270 if ok { 271 m, _ = ms.(*lru.ARCCache) 272 if _, k := m.Get(hash); k { 273 // This peer had this event, skip it 274 continue 275 } 276 } else { 277 m, _ = lru.NewARC(inmemoryMessages) 278 } 279 280 m.Add(hash, true) 281 sb.recentMessages.Add(addr, m) 282 283 cmsg := &istanbul.ConsensusMsg{ 284 PrevHash: prevHash, 285 Payload: payload, 286 } 287 288 go p.Send(IstanbulMsg, cmsg) 289 } 290 } 291 return targets 292 } 293 294 // Commit implements istanbul.Backend.Commit 295 func (sb *backend) Commit(proposal istanbul.Proposal, seals [][]byte) error { 296 // Check if the proposal is a valid block 297 block, ok := proposal.(*types.Block) 298 if !ok { 299 sb.logger.Error("Invalid proposal, %v", proposal) 300 return errInvalidProposal 301 } 302 h := block.Header() 303 round := sb.currentView.Load().(*istanbul.View).Round.Int64() 304 h = types.SetRoundToHeader(h, round) 305 // Append seals into extra-data 306 err := writeCommittedSeals(h, seals) 307 if err != nil { 308 return err 309 } 310 // update block's header 311 block = block.WithSeal(h) 312 313 sb.logger.Info("Committed", "number", proposal.Number().Uint64(), "hash", proposal.Hash(), "address", sb.Address()) 314 // - if the proposed and committed blocks are the same, send the proposed hash 315 // to commit channel, which is being watched inside the engine.Seal() function. 316 // - otherwise, we try to insert the block. 317 // -- if success, the ChainHeadEvent event will be broadcasted, try to build 318 // the next block and the previous Seal() will be stopped. 319 // -- otherwise, a error will be returned and a round change event will be fired. 320 if sb.proposedBlockHash == block.Hash() { 321 // feed block hash to Seal() and wait the Seal() result 322 sb.commitCh <- &types.Result{Block: block, Round: round} 323 return nil 324 } 325 326 if sb.broadcaster != nil { 327 sb.broadcaster.Enqueue(fetcherID, block) 328 } 329 return nil 330 } 331 332 // EventMux implements istanbul.Backend.EventMux 333 func (sb *backend) EventMux() *event.TypeMux { 334 return sb.istanbulEventMux 335 } 336 337 // Verify implements istanbul.Backend.Verify 338 func (sb *backend) Verify(proposal istanbul.Proposal) (time.Duration, error) { 339 // Check if the proposal is a valid block 340 block, ok := proposal.(*types.Block) 341 if !ok { 342 sb.logger.Error("Invalid proposal, %v", proposal) 343 return 0, errInvalidProposal 344 } 345 346 // check bad block 347 if sb.HasBadProposal(block.Hash()) { 348 return 0, blockchain.ErrBlacklistedHash 349 } 350 351 // check block body 352 txnHash := types.DeriveSha(block.Transactions(), block.Number()) 353 if txnHash != block.Header().TxHash { 354 return 0, errMismatchTxhashes 355 } 356 357 // verify the header of proposed block 358 err := sb.VerifyHeader(sb.chain, block.Header(), false) 359 // ignore errEmptyCommittedSeals error because we don't have the committed seals yet 360 if err == nil || err == errEmptyCommittedSeals { 361 return 0, nil 362 } else if err == consensus.ErrFutureBlock { 363 return time.Unix(block.Header().Time.Int64(), 0).Sub(now()), consensus.ErrFutureBlock 364 } 365 return 0, err 366 } 367 368 // Sign implements istanbul.Backend.Sign 369 func (sb *backend) Sign(data []byte) ([]byte, error) { 370 hashData := crypto.Keccak256([]byte(data)) 371 return crypto.Sign(hashData, sb.privateKey) 372 } 373 374 // CheckSignature implements istanbul.Backend.CheckSignature 375 func (sb *backend) CheckSignature(data []byte, address common.Address, sig []byte) error { 376 signer, err := cacheSignatureAddresses(data, sig) 377 if err != nil { 378 logger.Error("Failed to get signer address", "err", err) 379 return err 380 } 381 // Compare derived addresses 382 if signer != address { 383 return errInvalidSignature 384 } 385 return nil 386 } 387 388 // HasPropsal implements istanbul.Backend.HashBlock 389 func (sb *backend) HasPropsal(hash common.Hash, number *big.Int) bool { 390 return sb.chain.GetHeader(hash, number.Uint64()) != nil 391 } 392 393 // GetProposer implements istanbul.Backend.GetProposer 394 func (sb *backend) GetProposer(number uint64) common.Address { 395 if h := sb.chain.GetHeaderByNumber(number); h != nil { 396 a, _ := sb.Author(h) 397 return a 398 } 399 return common.Address{} 400 } 401 402 // ParentValidators implements istanbul.Backend.GetParentValidators 403 func (sb *backend) ParentValidators(proposal istanbul.Proposal) istanbul.ValidatorSet { 404 if block, ok := proposal.(*types.Block); ok { 405 return sb.getValidators(block.Number().Uint64()-1, block.ParentHash()) 406 } 407 408 // TODO-Klaytn-Governance The following return case should not be called. Refactor it to error handling. 409 return validator.NewValidatorSet(nil, nil, 410 istanbul.ProposerPolicy(sb.chain.Config().Istanbul.ProposerPolicy), 411 sb.chain.Config().Istanbul.SubGroupSize, 412 sb.chain) 413 } 414 415 func (sb *backend) getValidators(number uint64, hash common.Hash) istanbul.ValidatorSet { 416 snap, err := sb.snapshot(sb.chain, number, hash, nil, false) 417 if err != nil { 418 logger.Error("Snapshot not found.", "err", err) 419 // TODO-Klaytn-Governance The following return case should not be called. Refactor it to error handling. 420 return validator.NewValidatorSet(nil, nil, 421 istanbul.ProposerPolicy(sb.chain.Config().Istanbul.ProposerPolicy), 422 sb.chain.Config().Istanbul.SubGroupSize, 423 sb.chain) 424 } 425 return snap.ValSet 426 } 427 428 func (sb *backend) LastProposal() (istanbul.Proposal, common.Address) { 429 block := sb.currentBlock() 430 431 var proposer common.Address 432 if block.Number().Cmp(common.Big0) > 0 { 433 var err error 434 proposer, err = sb.Author(block.Header()) 435 if err != nil { 436 sb.logger.Error("Failed to get block proposer", "err", err) 437 return nil, common.Address{} 438 } 439 } 440 441 // Return header only block here since we don't need block body 442 return block, proposer 443 } 444 445 func (sb *backend) HasBadProposal(hash common.Hash) bool { 446 if sb.hasBadBlock == nil { 447 return false 448 } 449 return sb.hasBadBlock(hash) 450 }