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