github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/protocol/block.go (about) 1 package protocol 2 3 import ( 4 log "github.com/sirupsen/logrus" 5 6 "github.com/bytom/bytom/errors" 7 "github.com/bytom/bytom/protocol/bc" 8 "github.com/bytom/bytom/protocol/bc/types" 9 "github.com/bytom/bytom/protocol/state" 10 "github.com/bytom/bytom/protocol/validation" 11 ) 12 13 var ( 14 // ErrBadBlock is returned when a block is invalid. 15 ErrBadBlock = errors.New("invalid block") 16 // ErrBadStateRoot is returned when the computed assets merkle root 17 // disagrees with the one declared in a block header. 18 ErrBadStateRoot = errors.New("invalid state merkle root") 19 ) 20 21 // BlockExist check is a block in chain or orphan 22 func (c *Chain) BlockExist(hash *bc.Hash) bool { 23 if _, err := c.store.GetBlockHeader(hash); err == nil { 24 return true 25 } 26 27 return c.orphanManage.BlockExist(hash) 28 } 29 30 // GetBlockByHash return a block by given hash 31 func (c *Chain) GetBlockByHash(hash *bc.Hash) (*types.Block, error) { 32 return c.store.GetBlock(hash) 33 } 34 35 // GetBlockByHeight return a block header by given height 36 func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) { 37 hash, err := c.store.GetMainChainHash(height) 38 if err != nil { 39 return nil, errors.Wrap(err, "can't find block in given height") 40 } 41 42 return c.store.GetBlock(hash) 43 } 44 45 // GetHeaderByHash return a block header by given hash 46 func (c *Chain) GetHeaderByHash(hash *bc.Hash) (*types.BlockHeader, error) { 47 return c.store.GetBlockHeader(hash) 48 } 49 50 // GetHeaderByHeight return a block header by given height 51 func (c *Chain) GetHeaderByHeight(height uint64) (*types.BlockHeader, error) { 52 hash, err := c.store.GetMainChainHash(height) 53 if err != nil { 54 return nil, errors.Wrap(err, "can't find block header in given height") 55 } 56 57 return c.store.GetBlockHeader(hash) 58 } 59 60 func (c *Chain) calcReorganizeChain(beginAttach *types.BlockHeader, beginDetach *types.BlockHeader) ([]*types.BlockHeader, []*types.BlockHeader, error) { 61 var err error 62 var attachBlockHeaders []*types.BlockHeader 63 var detachBlockHeaders []*types.BlockHeader 64 65 for attachBlockHeader, detachBlockHeader := beginAttach, beginDetach; detachBlockHeader.Hash() != attachBlockHeader.Hash(); { 66 var attachRollback, detachRollBack bool 67 if attachRollback = attachBlockHeader.Height >= detachBlockHeader.Height; attachRollback { 68 attachBlockHeaders = append([]*types.BlockHeader{attachBlockHeader}, attachBlockHeaders...) 69 } 70 71 if detachRollBack = attachBlockHeader.Height <= detachBlockHeader.Height; detachRollBack { 72 detachBlockHeaders = append(detachBlockHeaders, detachBlockHeader) 73 } 74 75 if attachRollback { 76 attachBlockHeader, err = c.store.GetBlockHeader(&attachBlockHeader.PreviousBlockHash) 77 if err != nil { 78 return nil, nil, err 79 } 80 } 81 82 if detachRollBack { 83 detachBlockHeader, err = c.store.GetBlockHeader(&detachBlockHeader.PreviousBlockHash) 84 if err != nil { 85 return nil, nil, err 86 } 87 } 88 } 89 return attachBlockHeaders, detachBlockHeaders, nil 90 } 91 92 func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error { 93 attachNodes, detachNodes, err := c.calcReorganizeChain(blockHeader, c.bestBlockHeader) 94 if err != nil { 95 return err 96 } 97 98 utxoView := state.NewUtxoViewpoint() 99 contractView := state.NewContractViewpoint() 100 txsToRestore := map[bc.Hash]*types.Tx{} 101 for _, detachNode := range detachNodes { 102 hash := detachNode.Hash() 103 b, err := c.store.GetBlock(&hash) 104 if err != nil { 105 return err 106 } 107 108 detachBlock := types.MapBlock(b) 109 if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil { 110 return err 111 } 112 113 if err := utxoView.DetachBlock(detachBlock); err != nil { 114 return err 115 } 116 117 if err := contractView.DetachBlock(b); err != nil { 118 return err 119 } 120 121 for _, tx := range b.Transactions[1:] { 122 txsToRestore[tx.ID] = tx 123 } 124 log.WithFields(log.Fields{"module": logModule, "height": detachNode.Height, "hash": hash.String()}).Debug("detach from mainchain") 125 } 126 127 txsToRemove := map[bc.Hash]*types.Tx{} 128 for _, attachNode := range attachNodes { 129 hash := attachNode.Hash() 130 b, err := c.store.GetBlock(&hash) 131 if err != nil { 132 return err 133 } 134 135 attachBlock := types.MapBlock(b) 136 if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil { 137 return err 138 } 139 140 if err := utxoView.ApplyBlock(attachBlock); err != nil { 141 return err 142 } 143 144 if err := contractView.ApplyBlock(b); err != nil { 145 return err 146 } 147 148 for _, tx := range b.Transactions[1:] { 149 if _, ok := txsToRestore[tx.ID]; !ok { 150 txsToRemove[tx.ID] = tx 151 } else { 152 delete(txsToRestore, tx.ID) 153 } 154 } 155 156 log.WithFields(log.Fields{"module": logModule, "height": attachNode.Height, "hash": hash.String()}).Debug("attach from mainchain") 157 } 158 159 if err := c.setState(blockHeader, attachNodes, utxoView, contractView); err != nil { 160 return err 161 } 162 163 for txHash := range txsToRemove { 164 c.txPool.RemoveTransaction(&txHash) 165 } 166 167 for _, tx := range txsToRestore { 168 // the number of restored Tx should be very small or most of time ZERO 169 // Error returned from validation is ignored, tx could still be lost if validation fails. 170 // TODO: adjust tx timestamp so that it won't starve in pool. 171 if _, err := c.ValidateTx(tx); err != nil { 172 log.WithFields(log.Fields{"module": logModule, "tx_id": tx.Tx.ID.String(), "error": err}).Info("restore tx fail") 173 } 174 } 175 176 if len(txsToRestore) > 0 { 177 log.WithFields(log.Fields{"module": logModule, "num": len(txsToRestore)}).Debug("restore txs back to pool") 178 } 179 180 return nil 181 } 182 183 // SaveBlock will validate and save block into storage 184 func (c *Chain) saveBlock(block *types.Block) error { 185 parent, err := c.store.GetBlockHeader(&block.PreviousBlockHash) 186 if err != nil { 187 return err 188 } 189 190 checkpoint, err := c.PrevCheckpointByPrevHash(&block.PreviousBlockHash) 191 if err != nil { 192 return err 193 } 194 195 if err := validation.ValidateBlock(block, parent, checkpoint, c.ProgramConverter); err != nil { 196 return errors.Sub(ErrBadBlock, err) 197 } 198 199 if _, err := c.casper.ApplyBlock(block); err != nil { 200 return err 201 } 202 203 if err := c.store.SaveBlock(block); err != nil { 204 return err 205 } 206 207 blockHash := block.Hash() 208 c.orphanManage.Delete(&blockHash) 209 return nil 210 } 211 212 func (c *Chain) saveSubBlock(block *types.Block) { 213 blockHash := block.Hash() 214 prevOrphans, ok := c.orphanManage.GetPrevOrphans(&blockHash) 215 if !ok { 216 return 217 } 218 219 for _, prevOrphan := range prevOrphans { 220 orphanBlock, ok := c.orphanManage.Get(prevOrphan) 221 if !ok { 222 log.WithFields(log.Fields{"module": logModule, "hash": prevOrphan.String()}).Warning("saveSubBlock fail to get block from orphanManage") 223 continue 224 } 225 if err := c.saveBlock(orphanBlock); err != nil { 226 log.WithFields(log.Fields{"module": logModule, "hash": prevOrphan.String(), "height": orphanBlock.Height}).Warning("saveSubBlock fail to save block") 227 continue 228 } 229 230 c.saveSubBlock(orphanBlock) 231 } 232 } 233 234 type processBlockResponse struct { 235 isOrphan bool 236 err error 237 } 238 239 type processBlockMsg struct { 240 block *types.Block 241 reply chan processBlockResponse 242 } 243 244 // ProcessBlock is the entry for chain update 245 func (c *Chain) ProcessBlock(block *types.Block) (bool, error) { 246 reply := make(chan processBlockResponse, 1) 247 c.processBlockCh <- &processBlockMsg{block: block, reply: reply} 248 response := <-reply 249 return response.isOrphan, response.err 250 } 251 252 func (c *Chain) blockProcessor() { 253 for { 254 select { 255 case msg := <-c.processBlockCh: 256 isOrphan, err := c.processBlock(msg.block) 257 msg.reply <- processBlockResponse{isOrphan: isOrphan, err: err} 258 case msg := <-c.casper.RollbackCh(): 259 msg.Reply <- c.tryReorganize(msg.BestHash) 260 } 261 } 262 } 263 264 // ProcessBlock is the entry for handle block insert 265 func (c *Chain) processBlock(block *types.Block) (bool, error) { 266 blockHash := block.Hash() 267 if c.BlockExist(&blockHash) && c.bestBlockHeader.Height >= block.Height { 268 log.WithFields(log.Fields{"module": logModule, "hash": blockHash.String(), "height": block.Height}).Info("block has been processed") 269 return c.orphanManage.BlockExist(&blockHash), nil 270 } 271 272 if _, err := c.store.GetBlockHeader(&block.PreviousBlockHash); err != nil { 273 c.orphanManage.Add(block) 274 return true, nil 275 } 276 277 if err := c.saveBlock(block); err != nil { 278 return false, err 279 } 280 281 c.saveSubBlock(block) 282 bestHash := c.casper.BestChain() 283 return false, c.tryReorganize(bestHash) 284 } 285 286 func (c *Chain) tryReorganize(bestHash bc.Hash) error { 287 if c.bestBlockHeader.Hash() == bestHash { 288 return nil 289 } 290 291 blockHeader, err := c.GetHeaderByHash(&bestHash) 292 if err != nil { 293 return err 294 } 295 296 log.WithFields(log.Fields{"module": logModule, "bestHash": bestHash.String()}).Info("start to reorganize chain") 297 return c.reorganizeChain(blockHeader) 298 }