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