github.com/hyperion-hyn/go-ethereum@v2.4.0+incompatible/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/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/consensus" 27 "github.com/ethereum/go-ethereum/consensus/istanbul" 28 istanbulCore "github.com/ethereum/go-ethereum/consensus/istanbul/core" 29 "github.com/ethereum/go-ethereum/consensus/istanbul/validator" 30 "github.com/ethereum/go-ethereum/core" 31 "github.com/ethereum/go-ethereum/core/types" 32 "github.com/ethereum/go-ethereum/crypto" 33 "github.com/ethereum/go-ethereum/ethdb" 34 "github.com/ethereum/go-ethereum/event" 35 "github.com/ethereum/go-ethereum/log" 36 lru "github.com/hashicorp/golang-lru" 37 ) 38 39 const ( 40 // fetcherID is the ID indicates the block is from Istanbul engine 41 fetcherID = "istanbul" 42 ) 43 44 // New creates an Ethereum backend for Istanbul core engine. 45 func New(config *istanbul.Config, privateKey *ecdsa.PrivateKey, db ethdb.Database) consensus.Istanbul { 46 // Allocate the snapshot caches and create the engine 47 recents, _ := lru.NewARC(inmemorySnapshots) 48 recentMessages, _ := lru.NewARC(inmemoryPeers) 49 knownMessages, _ := lru.NewARC(inmemoryMessages) 50 backend := &backend{ 51 config: config, 52 istanbulEventMux: new(event.TypeMux), 53 privateKey: privateKey, 54 address: crypto.PubkeyToAddress(privateKey.PublicKey), 55 logger: log.New(), 56 db: db, 57 commitCh: make(chan *types.Block, 1), 58 recents: recents, 59 candidates: make(map[common.Address]bool), 60 coreStarted: false, 61 recentMessages: recentMessages, 62 knownMessages: knownMessages, 63 } 64 backend.core = istanbulCore.New(backend, backend.config) 65 return backend 66 } 67 68 // ---------------------------------------------------------------------------- 69 70 type backend struct { 71 config *istanbul.Config 72 istanbulEventMux *event.TypeMux 73 privateKey *ecdsa.PrivateKey 74 address common.Address 75 core istanbulCore.Engine 76 logger log.Logger 77 db ethdb.Database 78 chain consensus.ChainReader 79 currentBlock func() *types.Block 80 hasBadBlock func(hash common.Hash) bool 81 82 // the channels for istanbul engine notifications 83 commitCh chan *types.Block 84 proposedBlockHash common.Hash 85 sealMu sync.Mutex 86 coreStarted bool 87 coreMu sync.RWMutex 88 89 // Current list of candidates we are pushing 90 candidates map[common.Address]bool 91 // Protects the signer fields 92 candidatesLock sync.RWMutex 93 // Snapshots for recent block to speed up reorgs 94 recents *lru.ARCCache 95 96 // event subscription for ChainHeadEvent event 97 broadcaster consensus.Broadcaster 98 99 recentMessages *lru.ARCCache // the cache of peer's messages 100 knownMessages *lru.ARCCache // the cache of self messages 101 } 102 103 // zekun: HACK 104 func (sb *backend) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { 105 return new(big.Int) 106 } 107 108 // Address implements istanbul.Backend.Address 109 func (sb *backend) Address() common.Address { 110 return sb.address 111 } 112 113 // Validators implements istanbul.Backend.Validators 114 func (sb *backend) Validators(proposal istanbul.Proposal) istanbul.ValidatorSet { 115 return sb.getValidators(proposal.Number().Uint64(), proposal.Hash()) 116 } 117 118 // Broadcast implements istanbul.Backend.Broadcast 119 func (sb *backend) Broadcast(valSet istanbul.ValidatorSet, payload []byte) error { 120 // send to others 121 sb.Gossip(valSet, payload) 122 // send to self 123 msg := istanbul.MessageEvent{ 124 Payload: payload, 125 } 126 go sb.istanbulEventMux.Post(msg) 127 return nil 128 } 129 130 // Broadcast implements istanbul.Backend.Gossip 131 func (sb *backend) Gossip(valSet istanbul.ValidatorSet, payload []byte) error { 132 hash := istanbul.RLPHash(payload) 133 sb.knownMessages.Add(hash, true) 134 135 targets := make(map[common.Address]bool) 136 for _, val := range valSet.List() { 137 if val.Address() != sb.Address() { 138 targets[val.Address()] = true 139 } 140 } 141 142 if sb.broadcaster != nil && len(targets) > 0 { 143 ps := sb.broadcaster.FindPeers(targets) 144 for addr, p := range ps { 145 ms, ok := sb.recentMessages.Get(addr) 146 var m *lru.ARCCache 147 if ok { 148 m, _ = ms.(*lru.ARCCache) 149 if _, k := m.Get(hash); k { 150 // This peer had this event, skip it 151 continue 152 } 153 } else { 154 m, _ = lru.NewARC(inmemoryMessages) 155 } 156 157 m.Add(hash, true) 158 sb.recentMessages.Add(addr, m) 159 160 go p.Send(istanbulMsg, payload) 161 } 162 } 163 return nil 164 } 165 166 // Commit implements istanbul.Backend.Commit 167 func (sb *backend) Commit(proposal istanbul.Proposal, seals [][]byte) error { 168 // Check if the proposal is a valid block 169 block := &types.Block{} 170 block, ok := proposal.(*types.Block) 171 if !ok { 172 sb.logger.Error("Invalid proposal, %v", proposal) 173 return errInvalidProposal 174 } 175 176 h := block.Header() 177 // Append seals into extra-data 178 err := writeCommittedSeals(h, seals) 179 if err != nil { 180 return err 181 } 182 // update block's header 183 block = block.WithSeal(h) 184 185 sb.logger.Info("Committed", "address", sb.Address(), "hash", proposal.Hash(), "number", proposal.Number().Uint64()) 186 // - if the proposed and committed blocks are the same, send the proposed hash 187 // to commit channel, which is being watched inside the engine.Seal() function. 188 // - otherwise, we try to insert the block. 189 // -- if success, the ChainHeadEvent event will be broadcasted, try to build 190 // the next block and the previous Seal() will be stopped. 191 // -- otherwise, a error will be returned and a round change event will be fired. 192 if sb.proposedBlockHash == block.Hash() { 193 // feed block hash to Seal() and wait the Seal() result 194 sb.commitCh <- block 195 return nil 196 } 197 198 if sb.broadcaster != nil { 199 sb.broadcaster.Enqueue(fetcherID, block) 200 } 201 return nil 202 } 203 204 // EventMux implements istanbul.Backend.EventMux 205 func (sb *backend) EventMux() *event.TypeMux { 206 return sb.istanbulEventMux 207 } 208 209 // Verify implements istanbul.Backend.Verify 210 func (sb *backend) Verify(proposal istanbul.Proposal) (time.Duration, error) { 211 // Check if the proposal is a valid block 212 block := &types.Block{} 213 block, ok := proposal.(*types.Block) 214 if !ok { 215 sb.logger.Error("Invalid proposal, %v", proposal) 216 return 0, errInvalidProposal 217 } 218 219 // check bad block 220 if sb.HasBadProposal(block.Hash()) { 221 return 0, core.ErrBlacklistedHash 222 } 223 224 // check block body 225 txnHash := types.DeriveSha(block.Transactions()) 226 uncleHash := types.CalcUncleHash(block.Uncles()) 227 if txnHash != block.Header().TxHash { 228 return 0, errMismatchTxhashes 229 } 230 if uncleHash != nilUncleHash { 231 return 0, errInvalidUncleHash 232 } 233 234 // verify the header of proposed block 235 err := sb.VerifyHeader(sb.chain, block.Header(), false) 236 // ignore errEmptyCommittedSeals error because we don't have the committed seals yet 237 if err == nil || err == errEmptyCommittedSeals { 238 return 0, nil 239 } else if err == consensus.ErrFutureBlock { 240 return time.Unix(block.Header().Time.Int64(), 0).Sub(now()), consensus.ErrFutureBlock 241 } 242 return 0, err 243 } 244 245 // Sign implements istanbul.Backend.Sign 246 func (sb *backend) Sign(data []byte) ([]byte, error) { 247 hashData := crypto.Keccak256([]byte(data)) 248 return crypto.Sign(hashData, sb.privateKey) 249 } 250 251 // CheckSignature implements istanbul.Backend.CheckSignature 252 func (sb *backend) CheckSignature(data []byte, address common.Address, sig []byte) error { 253 signer, err := istanbul.GetSignatureAddress(data, sig) 254 if err != nil { 255 log.Error("Failed to get signer address", "err", err) 256 return err 257 } 258 // Compare derived addresses 259 if signer != address { 260 return errInvalidSignature 261 } 262 return nil 263 } 264 265 // HasPropsal implements istanbul.Backend.HashBlock 266 func (sb *backend) HasPropsal(hash common.Hash, number *big.Int) bool { 267 return sb.chain.GetHeader(hash, number.Uint64()) != nil 268 } 269 270 // GetProposer implements istanbul.Backend.GetProposer 271 func (sb *backend) GetProposer(number uint64) common.Address { 272 if h := sb.chain.GetHeaderByNumber(number); h != nil { 273 a, _ := sb.Author(h) 274 return a 275 } 276 return common.Address{} 277 } 278 279 // ParentValidators implements istanbul.Backend.GetParentValidators 280 func (sb *backend) ParentValidators(proposal istanbul.Proposal) istanbul.ValidatorSet { 281 if block, ok := proposal.(*types.Block); ok { 282 return sb.getValidators(block.Number().Uint64()-1, block.ParentHash()) 283 } 284 return validator.NewSet(nil, sb.config.ProposerPolicy) 285 } 286 287 func (sb *backend) getValidators(number uint64, hash common.Hash) istanbul.ValidatorSet { 288 snap, err := sb.snapshot(sb.chain, number, hash, nil) 289 if err != nil { 290 return validator.NewSet(nil, sb.config.ProposerPolicy) 291 } 292 return snap.ValSet 293 } 294 295 func (sb *backend) LastProposal() (istanbul.Proposal, common.Address) { 296 block := sb.currentBlock() 297 298 var proposer common.Address 299 if block.Number().Cmp(common.Big0) > 0 { 300 var err error 301 proposer, err = sb.Author(block.Header()) 302 if err != nil { 303 sb.logger.Error("Failed to get block proposer", "err", err) 304 return nil, common.Address{} 305 } 306 } 307 308 // Return header only block here since we don't need block body 309 return block, proposer 310 } 311 312 func (sb *backend) HasBadProposal(hash common.Hash) bool { 313 if sb.hasBadBlock == nil { 314 return false 315 } 316 return sb.hasBadBlock(hash) 317 } 318 319 func (sb *backend) Close() error { 320 return nil 321 }