github.com/elastos/Elastos.ELA.SideChain.ETH@v0.2.2/dpos/blockpool.go (about) 1 // Copyright (c) 2017-2019 The Elastos Foundation 2 // Use of this source code is governed by an MIT 3 // license that can be found in the LICENSE file. 4 // 5 6 package dpos 7 8 import ( 9 "errors" 10 "sync" 11 12 "github.com/elastos/Elastos.ELA.SideChain.ESC/log" 13 14 "github.com/elastos/Elastos.ELA/common" 15 "github.com/elastos/Elastos.ELA/core/types/payload" 16 ) 17 18 const cachedCount = 6 19 20 type DBlock interface { 21 GetHash() common.Uint256 22 GetHeight() uint64 23 Nonce() uint64 24 } 25 26 type ConfirmInfo struct { 27 Confirm *payload.Confirm 28 Height uint64 29 } 30 31 type BlockPool struct { 32 sync.RWMutex 33 blocks map[common.Uint256]DBlock 34 confirms map[common.Uint256]*payload.Confirm 35 heightConfirms map[uint64]*payload.Confirm 36 badBlocks map[common.Uint256]DBlock 37 38 VerifyConfirm func(confirm *payload.Confirm, elaHeight uint64) error 39 VerifyBlock func(block DBlock) error 40 SealHash func(block DBlock) (common.Uint256, error) 41 42 futureBlocks map[common.Uint256]DBlock 43 } 44 45 func NewBlockPool(verifyConfirm func(confirm *payload.Confirm, elaHeight uint64) error, 46 verifyBlock func(block DBlock) error, 47 sealHash func(block DBlock) (common.Uint256, error)) *BlockPool { 48 return &BlockPool{ 49 blocks: make(map[common.Uint256]DBlock), 50 confirms: make(map[common.Uint256]*payload.Confirm), 51 heightConfirms: make(map[uint64]*payload.Confirm), 52 badBlocks: make(map[common.Uint256]DBlock), 53 futureBlocks: make(map[common.Uint256]DBlock), 54 VerifyConfirm: verifyConfirm, 55 VerifyBlock: verifyBlock, 56 SealHash: sealHash, 57 } 58 } 59 60 func (bm *BlockPool) HandleParentBlock(parent DBlock) bool { 61 bm.Lock() 62 var handledBlock DBlock 63 for _, block := range bm.futureBlocks { 64 if block.GetHeight()-1 == parent.GetHeight() { 65 handledBlock = block 66 break 67 } 68 } 69 bm.Unlock() 70 if handledBlock != nil { 71 bm.AppendDposBlock(handledBlock) 72 return true 73 } 74 return false 75 } 76 77 func (bm *BlockPool) IsFutureBlock(hash common.Uint256) bool { 78 bm.Lock() 79 defer bm.Unlock() 80 81 for _, block := range bm.futureBlocks { 82 sealHash, err := bm.SealHash(block) 83 if err != nil { 84 return false 85 } 86 if sealHash.IsEqual(hash) { 87 return true 88 } 89 } 90 return false 91 } 92 93 func (bm *BlockPool) AddBadBlock(block DBlock) error { 94 bm.Lock() 95 defer bm.Unlock() 96 97 hash, err := bm.SealHash(block) 98 if err != nil { 99 return err 100 } 101 if _, ok := bm.badBlocks[hash]; ok { 102 return errors.New("duplicate badBlock in pool") 103 } 104 bm.badBlocks[hash] = block 105 return nil 106 } 107 108 func (bm *BlockPool) IsBadBlockProposal(proposal *payload.DPOSProposal) bool { 109 bm.Lock() 110 defer bm.Unlock() 111 hash := proposal.BlockHash 112 if b, ok := bm.badBlocks[hash]; ok { 113 log.Info("bad block propsoal", "height", b.GetHeight()) 114 return true 115 } 116 return false 117 } 118 119 func (bm *BlockPool) AppendFutureBlock(dposBlock DBlock) error { 120 bm.Lock() 121 defer bm.Unlock() 122 123 return bm.appendFutureBlock(dposBlock) 124 } 125 126 func (bm *BlockPool) appendFutureBlock(block DBlock) error { 127 hash, err := bm.SealHash(block) 128 if err != nil { 129 return err 130 } 131 if _, ok := bm.futureBlocks[hash]; ok { 132 return errors.New("duplicate futureBlocks in pool") 133 } 134 bm.futureBlocks[hash] = block 135 return nil 136 } 137 138 func (bm *BlockPool) AppendConfirm(confirm *payload.Confirm) error { 139 bm.Lock() 140 _, ok := bm.confirms[confirm.Proposal.BlockHash] 141 bm.Unlock() 142 if ok { 143 return errors.New("conformation is all ready in block pool") 144 } 145 return bm.appendConfirm(confirm) 146 } 147 148 func (bm *BlockPool) AppendDposBlock(dposBlock DBlock) error { 149 Info("[--AppendDposBlock--], height", dposBlock.GetHeight()) 150 return bm.appendBlock(dposBlock) 151 } 152 153 func (bm *BlockPool) appendBlock(block DBlock) error { 154 // add block 155 hash, err := bm.SealHash(block) 156 if err != nil { 157 return err 158 } 159 if bm.HasBlock(hash) { 160 return errors.New("duplicate block in pool") 161 } 162 // verify block 163 if err := bm.VerifyBlock(block); err != nil { 164 Info("[AppendBlock] check block sanity failed, ", err) 165 return err 166 } 167 bm.Lock() 168 bm.blocks[hash] = block 169 if _, ok := bm.futureBlocks[hash]; ok { 170 delete(bm.futureBlocks, hash) 171 } 172 bm.Unlock() 173 return nil 174 } 175 176 func (bm *BlockPool) appendConfirm(confirm *payload.Confirm) error { 177 log.Info("[appendConfirm] start") 178 defer Info("[appendConfirm] end") 179 // verify confirmation 180 dblock, ok := bm.GetBlock(confirm.Proposal.BlockHash) 181 if !ok { 182 return errors.New("appennd confirm error, not have DBlock") 183 } 184 if err := bm.VerifyConfirm(confirm, dblock.Nonce()); err != nil { 185 return err 186 } 187 bm.Lock() 188 bm.confirms[confirm.Proposal.BlockHash] = confirm 189 bm.Unlock() 190 err := bm.confirmBlock(confirm.Proposal.BlockHash) 191 if err != nil { 192 return err 193 } 194 195 return nil 196 } 197 198 func (bm *BlockPool) ConfirmBlock(hash common.Uint256) error { 199 bm.Lock() 200 err := bm.confirmBlock(hash) 201 bm.Unlock() 202 return err 203 } 204 205 func (bm *BlockPool) confirmBlock(hash common.Uint256) error { 206 Info("[ConfirmBlock] block hash:", hash) 207 bm.Lock() 208 209 block, ok := bm.blocks[hash] 210 if !ok { 211 bm.Unlock() 212 return errors.New("there is no block in pool when confirming block") 213 } 214 215 confirm, ok := bm.confirms[hash] 216 if !ok { 217 bm.Unlock() 218 return errors.New("there is no block confirmation in pool when confirming block") 219 } 220 221 bm.heightConfirms[block.GetHeight()] = confirm 222 bm.Unlock() 223 224 return nil 225 } 226 227 func (bm *BlockPool) AddToBlockMap(block DBlock) { 228 bm.Lock() 229 defer bm.Unlock() 230 231 hash, _ := bm.SealHash(block) 232 233 bm.blocks[hash] = block 234 } 235 236 func (bm *BlockPool) HasBlock(hash common.Uint256) bool { 237 _, ok := bm.GetBlock(hash) 238 return ok 239 } 240 241 func (bm *BlockPool) HashConfirmed(number uint64) bool { 242 bm.Lock() 243 var temp DBlock = nil 244 for _, block := range bm.blocks { 245 if block.GetHeight() == number { 246 temp = block 247 break 248 } 249 } 250 bm.Unlock() 251 if temp != nil { 252 hash, _ := bm.SealHash(temp) 253 _, has := bm.GetConfirm(hash) 254 return has 255 } 256 257 return false 258 } 259 260 func (bm *BlockPool) GetBlock(hash common.Uint256) (DBlock, bool) { 261 bm.Lock() 262 block, ok := bm.blocks[hash] 263 bm.Unlock() 264 return block, ok 265 } 266 267 func (bm *BlockPool) AddToConfirmMap(confirm *payload.Confirm) { 268 bm.Lock() 269 defer bm.Unlock() 270 271 bm.confirms[confirm.Proposal.BlockHash] = confirm 272 } 273 274 func (bm *BlockPool) CleanFinalConfirmedBlock(height uint64) { 275 bm.Lock() 276 defer bm.Unlock() 277 278 for _, block := range bm.blocks { 279 hash, _ := bm.SealHash(block) 280 if height > cachedCount && block.GetHeight() < height-cachedCount { 281 delete(bm.blocks, hash) 282 delete(bm.confirms, hash) 283 } 284 } 285 286 for _, block := range bm.badBlocks { 287 hash, _ := bm.SealHash(block) 288 if height > cachedCount && block.GetHeight() < height-cachedCount { 289 delete(bm.badBlocks, hash) 290 } 291 } 292 293 for cheight, _ := range bm.heightConfirms { 294 if height > cachedCount && cheight < height-cachedCount { 295 delete(bm.heightConfirms, cheight) 296 } 297 } 298 } 299 300 func (bm *BlockPool) GetConfirmByHeight(height uint64) (*payload.Confirm, bool) { 301 bm.Lock() 302 defer bm.Unlock() 303 304 confirm, ok := bm.heightConfirms[height] 305 return confirm, ok 306 } 307 308 func (bm *BlockPool) GetConfirm(hash common.Uint256) (*payload.Confirm, bool) { 309 bm.Lock() 310 defer bm.Unlock() 311 312 confirm, ok := bm.confirms[hash] 313 return confirm, ok 314 } 315 316 func (bm *BlockPool) RemoveConfirm(hash common.Uint256) { 317 bm.Lock() 318 defer bm.Unlock() 319 320 if _, ok := bm.confirms[hash]; ok { 321 delete(bm.confirms, hash) 322 } 323 }