github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/consensus/istanbul/backend/backend.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package backend 18 19 import ( 20 "crypto/ecdsa" 21 "math/big" 22 "sync" 23 "time" 24 25 "github.com/electroneum/electroneum-sc/common" 26 "github.com/electroneum/electroneum-sc/consensus" 27 "github.com/electroneum/electroneum-sc/consensus/istanbul" 28 istanbulcommon "github.com/electroneum/electroneum-sc/consensus/istanbul/common" 29 qbftcore "github.com/electroneum/electroneum-sc/consensus/istanbul/core" 30 qbftengine "github.com/electroneum/electroneum-sc/consensus/istanbul/engine" 31 qbfttypes "github.com/electroneum/electroneum-sc/consensus/istanbul/types" 32 "github.com/electroneum/electroneum-sc/consensus/istanbul/validator" 33 "github.com/electroneum/electroneum-sc/core" 34 "github.com/electroneum/electroneum-sc/core/types" 35 "github.com/electroneum/electroneum-sc/crypto" 36 "github.com/electroneum/electroneum-sc/ethdb" 37 "github.com/electroneum/electroneum-sc/event" 38 "github.com/electroneum/electroneum-sc/log" 39 lru "github.com/hashicorp/golang-lru" 40 ) 41 42 const ( 43 // fetcherID is the ID indicates the block is from Istanbul engine 44 fetcherID = "istanbul" 45 ) 46 47 // New creates an Ethereum backend for Istanbul core engine. 48 func New(config istanbul.Config, privateKey *ecdsa.PrivateKey, db ethdb.Database) *Backend { 49 // Allocate the snapshot caches and create the engine 50 recents, _ := lru.NewARC(inmemorySnapshots) 51 recentsEmission, _ := lru.NewARC(inmemoryEmissions) 52 recentMessages, _ := lru.NewARC(inmemoryPeers) 53 knownMessages, _ := lru.NewARC(inmemoryMessages) 54 55 sb := &Backend{ 56 config: &config, 57 istanbulEventMux: new(event.TypeMux), 58 privateKey: privateKey, 59 address: crypto.PubkeyToAddress(privateKey.PublicKey), 60 logger: log.New(), 61 db: db, 62 commitCh: make(chan *types.Block, 1), 63 recents: recents, 64 recentsEmission: recentsEmission, 65 candidates: make(map[common.Address]bool), 66 coreStarted: false, 67 recentMessages: recentMessages, 68 knownMessages: knownMessages, 69 } 70 71 sb.qbftEngine = qbftengine.NewEngine(sb.config, sb.address, sb.Sign) 72 73 return sb 74 } 75 76 // ---------------------------------------------------------------------------- 77 78 type Backend struct { 79 config *istanbul.Config 80 81 privateKey *ecdsa.PrivateKey 82 address common.Address 83 84 core istanbul.Core 85 86 qbftEngine *qbftengine.Engine 87 88 istanbulEventMux *event.TypeMux 89 90 logger log.Logger 91 92 db ethdb.Database 93 94 chain consensus.ChainHeaderReader 95 currentBlock func() *types.Block 96 hasBadBlock func(db ethdb.Reader, hash common.Hash) bool 97 98 // the channels for istanbul engine notifications 99 commitCh chan *types.Block 100 proposedBlockHash common.Hash 101 sealMu sync.Mutex 102 coreStarted bool 103 coreMu sync.RWMutex 104 105 // Current list of candidates we are pushing 106 candidates map[common.Address]bool 107 // Protects the signer fields 108 candidatesLock sync.RWMutex 109 // Snapshots for recent block to speed up reorgs 110 recents *lru.ARCCache 111 // Emission for recent blocks 112 recentsEmission *lru.ARCCache 113 114 // event subscription for ChainHeadEvent event 115 broadcaster consensus.Broadcaster 116 117 recentMessages *lru.ARCCache // the cache of peer's messages 118 knownMessages *lru.ARCCache // the cache of self messages 119 } 120 121 func (sb *Backend) Engine() istanbul.Engine { 122 return sb.EngineForBlockNumber(nil) 123 } 124 125 func (sb *Backend) EngineForBlockNumber(blockNumber *big.Int) istanbul.Engine { 126 return sb.qbftEngine 127 } 128 129 // zekun: HACK 130 func (sb *Backend) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { 131 return sb.EngineForBlockNumber(parent.Number).CalcDifficulty(chain, time, parent) 132 } 133 134 // Address implements istanbul.Backend.Address 135 func (sb *Backend) Address() common.Address { 136 return sb.Engine().Address() 137 } 138 139 // Validators implements istanbul.Backend.Validators 140 func (sb *Backend) Validators(proposal istanbul.Proposal) istanbul.ValidatorSet { 141 return sb.getValidators(proposal.Number().Uint64(), proposal.Hash()) 142 } 143 144 // Broadcast implements istanbul.Backend.Broadcast 145 func (sb *Backend) Broadcast(valSet istanbul.ValidatorSet, code uint64, payload []byte) error { 146 // send to others 147 sb.Gossip(valSet, code, payload) 148 // send to self 149 msg := istanbul.MessageEvent{ 150 Code: code, 151 Payload: payload, 152 } 153 go sb.istanbulEventMux.Post(msg) 154 return nil 155 } 156 157 // Gossip implements istanbul.Backend.Gossip 158 func (sb *Backend) Gossip(valSet istanbul.ValidatorSet, code uint64, payload []byte) error { 159 hash := istanbul.RLPHash(payload) 160 sb.knownMessages.Add(hash, true) 161 162 targets := make(map[common.Address]bool) 163 for _, val := range valSet.List() { 164 if val.Address() != sb.Address() { 165 targets[val.Address()] = true 166 } 167 } 168 if sb.broadcaster != nil && len(targets) > 0 { 169 ps := sb.broadcaster.FindPeers(targets) 170 for addr, p := range ps { 171 ms, ok := sb.recentMessages.Get(addr) 172 var m *lru.ARCCache 173 if ok { 174 m, _ = ms.(*lru.ARCCache) 175 if _, k := m.Get(hash); k { 176 // This peer had this event, skip it 177 continue 178 } 179 } else { 180 m, _ = lru.NewARC(inmemoryMessages) 181 } 182 183 m.Add(hash, true) 184 sb.recentMessages.Add(addr, m) 185 186 var outboundCode uint64 = istanbulMsg 187 if _, ok := qbfttypes.MessageCodes()[code]; ok { 188 outboundCode = code 189 } 190 go p.SendQBFTConsensus(outboundCode, payload) 191 } 192 } 193 return nil 194 } 195 196 // Commit implements istanbul.Backend.Commit 197 func (sb *Backend) Commit(proposal istanbul.Proposal, seals [][]byte, round *big.Int) (err error) { 198 // Check if the proposal is a valid block 199 block, ok := proposal.(*types.Block) 200 if !ok { 201 sb.logger.Error("IBFT: invalid block proposal", "proposal", proposal) 202 return istanbulcommon.ErrInvalidProposal 203 } 204 205 // Commit header 206 h := block.Header() 207 err = sb.EngineForBlockNumber(h.Number).CommitHeader(h, seals, round) 208 if err != nil { 209 return 210 } 211 212 // Remove ValidatorSet added to ProposerPolicy registry, if not done, the registry keeps increasing size with each block height 213 sb.config.ProposerPolicy.ClearRegistry() 214 215 // update block's header 216 block = block.WithSeal(h) 217 218 sb.logger.Info("IBFT: block proposal committed", "author", sb.Address(), "hash", proposal.Hash(), "number", proposal.Number().Uint64()) 219 220 // - if the proposed and committed blocks are the same, send the proposed hash 221 // to commit channel, which is being watched inside the engine.Seal() function. 222 // - otherwise, we try to insert the block. 223 // -- if success, the ChainHeadEvent event will be broadcasted, try to build 224 // the next block and the previous Seal() will be stopped. 225 // -- otherwise, a error will be returned and a round change event will be fired. 226 if sb.proposedBlockHash == block.Hash() { 227 // feed block hash to Seal() and wait the Seal() result 228 sb.commitCh <- block 229 return nil 230 } 231 232 if sb.broadcaster != nil { 233 sb.broadcaster.Enqueue(fetcherID, block) 234 } 235 236 return nil 237 } 238 239 // EventMux implements istanbul.Backend.EventMux 240 func (sb *Backend) EventMux() *event.TypeMux { 241 return sb.istanbulEventMux 242 } 243 244 // Verify implements istanbul.Backend.Verify 245 func (sb *Backend) Verify(proposal istanbul.Proposal) (time.Duration, error) { 246 // Check if the proposal is a valid block 247 block, ok := proposal.(*types.Block) 248 if !ok { 249 sb.logger.Error("IBFT: invalid block proposal", "proposal", proposal) 250 return 0, istanbulcommon.ErrInvalidProposal 251 } 252 253 // check bad block 254 if sb.HasBadProposal(block.Hash()) { 255 sb.logger.Warn("IBFT: bad block proposal", "proposal", proposal) 256 return 0, core.ErrBannedHash 257 } 258 259 header := block.Header() 260 snap, err := sb.snapshot(sb.chain, header.Number.Uint64()-1, header.ParentHash, nil) 261 if err != nil { 262 return 0, err 263 } 264 265 return sb.EngineForBlockNumber(header.Number).VerifyBlockProposal(sb.chain, block, snap.ValSet) 266 } 267 268 // Sign implements istanbul.Backend.Sign 269 func (sb *Backend) Sign(data []byte) ([]byte, error) { 270 hashData := crypto.Keccak256(data) 271 return crypto.Sign(hashData, sb.privateKey) 272 } 273 274 // SignWithoutHashing implements istanbul.Backend.SignWithoutHashing and signs input data with the backend's private key without hashing the input data 275 func (sb *Backend) SignWithoutHashing(data []byte) ([]byte, error) { 276 return crypto.Sign(data, sb.privateKey) 277 } 278 279 // CheckSignature implements istanbul.Backend.CheckSignature 280 func (sb *Backend) CheckSignature(data []byte, address common.Address, sig []byte) error { 281 signer, err := istanbul.GetSignatureAddress(data, sig) 282 if err != nil { 283 return err 284 } 285 // Compare derived addresses 286 if signer != address { 287 return istanbulcommon.ErrInvalidSignature 288 } 289 290 return nil 291 } 292 293 // HasPropsal implements istanbul.Backend.HashBlock 294 func (sb *Backend) HasPropsal(hash common.Hash, number *big.Int) bool { 295 return sb.chain.GetHeader(hash, number.Uint64()) != nil 296 } 297 298 // GetProposer implements istanbul.Backend.GetProposer 299 func (sb *Backend) GetProposer(number uint64) common.Address { 300 if h := sb.chain.GetHeaderByNumber(number); h != nil { 301 a, _ := sb.Author(h) 302 return a 303 } 304 return common.Address{} 305 } 306 307 // ParentValidators implements istanbul.Backend.GetParentValidators 308 func (sb *Backend) ParentValidators(proposal istanbul.Proposal) istanbul.ValidatorSet { 309 if block, ok := proposal.(*types.Block); ok { 310 return sb.getValidators(block.Number().Uint64()-1, block.ParentHash()) 311 } 312 return validator.NewSet(nil, sb.config.ProposerPolicy) 313 } 314 315 func (sb *Backend) getValidators(number uint64, hash common.Hash) istanbul.ValidatorSet { 316 snap, err := sb.snapshot(sb.chain, number, hash, nil) 317 if err != nil { 318 return validator.NewSet(nil, sb.config.ProposerPolicy) 319 } 320 return snap.ValSet 321 } 322 323 func (sb *Backend) LastProposal() (istanbul.Proposal, common.Address) { 324 block := sb.currentBlock() 325 326 var proposer common.Address 327 if block.Number().Cmp(common.Big0) > 0 { 328 var err error 329 proposer, err = sb.Author(block.Header()) 330 if err != nil { 331 sb.logger.Error("IBFT: last block proposal invalid", "err", err) 332 return nil, common.Address{} 333 } 334 } 335 336 // Return header only block here since we don't need block body 337 return block, proposer 338 } 339 340 func (sb *Backend) HasBadProposal(hash common.Hash) bool { 341 if sb.hasBadBlock == nil { 342 return false 343 } 344 return sb.hasBadBlock(sb.db, hash) 345 } 346 347 func (sb *Backend) Close() error { 348 return nil 349 } 350 351 func (sb *Backend) startQBFT() error { 352 sb.logger.Info("IBFT: activate QBFT") 353 sb.logger.Trace("IBFT: set ProposerPolicy sorter to ValidatorSortByByteFunc") 354 sb.config.ProposerPolicy.Use(istanbul.ValidatorSortByByte()) 355 356 sb.core = qbftcore.New(sb, sb.config) 357 if err := sb.core.Start(); err != nil { 358 sb.logger.Error("IBFT: failed to activate QBFT", "err", err) 359 return err 360 } 361 362 return nil 363 } 364 365 func (sb *Backend) stop() error { 366 core := sb.core 367 sb.core = nil 368 369 if core != nil { 370 sb.logger.Info("IBFT: deactivate") 371 if err := core.Stop(); err != nil { 372 sb.logger.Error("IBFT: failed to deactivate", "err", err) 373 return err 374 } 375 } 376 377 return nil 378 } 379 380 // StartQBFTConsensus stops existing legacy ibft consensus and starts the new qbft consensus 381 func (sb *Backend) StartQBFTConsensus() error { 382 sb.logger.Info("IBFT: switch from IBFT to QBFT") 383 if err := sb.stop(); err != nil { 384 return err 385 } 386 387 return sb.startQBFT() 388 }