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