gitlab.com/jokerrs1/Sia@v1.3.2/modules/consensus/accept.go (about) 1 package consensus 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "os" 8 "time" 9 10 "github.com/NebulousLabs/Sia/build" 11 "github.com/NebulousLabs/Sia/modules" 12 "github.com/NebulousLabs/Sia/types" 13 14 "github.com/coreos/bbolt" 15 ) 16 17 var ( 18 errDoSBlock = errors.New("block is known to be invalid") 19 errInconsistentSet = errors.New("consensus set is not in a consistent state") 20 errNoBlockMap = errors.New("block map is not in database") 21 errNonLinearChain = errors.New("block set is not a contiguous chain") 22 errOrphan = errors.New("block has no known parent") 23 ) 24 25 // managedBroadcastBlock will broadcast a block to the consensus set's peers. 26 func (cs *ConsensusSet) managedBroadcastBlock(b types.Block) { 27 // broadcast the block header to all peers 28 go cs.gateway.Broadcast("RelayHeader", b.Header(), cs.gateway.Peers()) 29 } 30 31 // validateHeaderAndBlock does some early, low computation verification on the 32 // block. Callers should not assume that validation will happen in a particular 33 // order. 34 func (cs *ConsensusSet) validateHeaderAndBlock(tx dbTx, b types.Block, id types.BlockID) (parent *processedBlock, err error) { 35 // Check if the block is a DoS block - a known invalid block that is expensive 36 // to validate. 37 _, exists := cs.dosBlocks[id] 38 if exists { 39 return nil, errDoSBlock 40 } 41 42 // Check if the block is already known. 43 blockMap := tx.Bucket(BlockMap) 44 if blockMap == nil { 45 return nil, errNoBlockMap 46 } 47 if blockMap.Get(id[:]) != nil { 48 return nil, modules.ErrBlockKnown 49 } 50 51 // Check for the parent. 52 parentID := b.ParentID 53 parentBytes := blockMap.Get(parentID[:]) 54 if parentBytes == nil { 55 return nil, errOrphan 56 } 57 parent = new(processedBlock) 58 err = cs.marshaler.Unmarshal(parentBytes, parent) 59 if err != nil { 60 return nil, err 61 } 62 // Check that the timestamp is not too far in the past to be acceptable. 63 minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, parent) 64 65 err = cs.blockValidator.ValidateBlock(b, id, minTimestamp, parent.ChildTarget, parent.Height+1, cs.log) 66 if err != nil { 67 return nil, err 68 } 69 return parent, nil 70 } 71 72 // checkHeaderTarget returns true if the header's ID meets the given target. 73 func checkHeaderTarget(h types.BlockHeader, target types.Target) bool { 74 blockHash := h.ID() 75 return bytes.Compare(target[:], blockHash[:]) >= 0 76 } 77 78 // validateHeader does some early, low computation verification on the header 79 // to determine if the block should be downloaded. Callers should not assume 80 // that validation will happen in a particular order. 81 func (cs *ConsensusSet) validateHeader(tx dbTx, h types.BlockHeader) error { 82 // Check if the block is a DoS block - a known invalid block that is expensive 83 // to validate. 84 id := h.ID() 85 _, exists := cs.dosBlocks[id] 86 if exists { 87 return errDoSBlock 88 } 89 90 // Check if the block is already known. 91 blockMap := tx.Bucket(BlockMap) 92 if blockMap == nil { 93 return errNoBlockMap 94 } 95 if blockMap.Get(id[:]) != nil { 96 return modules.ErrBlockKnown 97 } 98 99 // Check for the parent. 100 parentID := h.ParentID 101 parentBytes := blockMap.Get(parentID[:]) 102 if parentBytes == nil { 103 return errOrphan 104 } 105 var parent processedBlock 106 err := cs.marshaler.Unmarshal(parentBytes, &parent) 107 if err != nil { 108 return err 109 } 110 111 // Check that the target of the new block is sufficient. 112 if !checkHeaderTarget(h, parent.ChildTarget) { 113 return modules.ErrBlockUnsolved 114 } 115 116 // TODO: check if the block is a non extending block once headers-first 117 // downloads are implemented. 118 119 // Check that the timestamp is not too far in the past to be acceptable. 120 minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent) 121 if minTimestamp > h.Timestamp { 122 return errEarlyTimestamp 123 } 124 125 // Check if the block is in the extreme future. We make a distinction between 126 // future and extreme future because there is an assumption that by the time 127 // the extreme future arrives, this block will no longer be a part of the 128 // longest fork because it will have been ignored by all of the miners. 129 if h.Timestamp > types.CurrentTimestamp()+types.ExtremeFutureThreshold { 130 return errExtremeFutureTimestamp 131 } 132 133 // We do not check if the header is in the near future here, because we want 134 // to get the corresponding block as soon as possible, even if the block is in 135 // the near future. 136 137 return nil 138 } 139 140 // addBlockToTree inserts a block into the blockNode tree by adding it to its 141 // parent's list of children. If the new blockNode is heavier than the current 142 // node, the blockchain is forked to put the new block and its parents at the 143 // tip. An error will be returned if block verification fails or if the block 144 // does not extend the longest fork. 145 // 146 // addBlockToTree might need to modify the database while returning an error 147 // on the block. Such errors are handled outside of the transaction by the 148 // caller. Switching to a managed tx through bolt will make this complexity 149 // unneeded. 150 func (cs *ConsensusSet) addBlockToTree(tx *bolt.Tx, b types.Block, parent *processedBlock) (ce changeEntry, err error) { 151 // Prepare the child processed block associated with the parent block. 152 newNode := cs.newChild(tx, parent, b) 153 154 // Check whether the new node is part of a chain that is heavier than the 155 // current node. If not, return ErrNonExtending and don't fork the 156 // blockchain. 157 currentNode := currentProcessedBlock(tx) 158 if !newNode.heavierThan(currentNode) { 159 return changeEntry{}, modules.ErrNonExtendingBlock 160 } 161 162 // Fork the blockchain and put the new heaviest block at the tip of the 163 // chain. 164 var revertedBlocks, appliedBlocks []*processedBlock 165 revertedBlocks, appliedBlocks, err = cs.forkBlockchain(tx, newNode) 166 if err != nil { 167 return changeEntry{}, err 168 } 169 for _, rn := range revertedBlocks { 170 ce.RevertedBlocks = append(ce.RevertedBlocks, rn.Block.ID()) 171 } 172 for _, an := range appliedBlocks { 173 ce.AppliedBlocks = append(ce.AppliedBlocks, an.Block.ID()) 174 } 175 err = appendChangeLog(tx, ce) 176 if err != nil { 177 return changeEntry{}, err 178 } 179 return ce, nil 180 } 181 182 // threadedSleepOnFutureBlock will sleep until the timestamp of a future block 183 // has arrived. 184 // 185 // TODO: An attacker can broadcast a future block multiple times, resulting in a 186 // goroutine spinup for each future block. Need to prevent that. 187 // 188 // TODO: An attacker could produce a very large number of future blocks, 189 // consuming memory. Need to prevent that. 190 func (cs *ConsensusSet) threadedSleepOnFutureBlock(b types.Block) { 191 // Add this thread to the threadgroup. 192 err := cs.tg.Add() 193 if err != nil { 194 return 195 } 196 defer cs.tg.Done() 197 198 // Perform a soft-sleep while we wait for the block to become valid. 199 select { 200 case <-cs.tg.StopChan(): 201 return 202 case <-time.After(time.Duration(b.Timestamp-(types.CurrentTimestamp()+types.FutureThreshold)) * time.Second): 203 _, err := cs.managedAcceptBlocks([]types.Block{b}) 204 if err != nil { 205 cs.log.Debugln("WARN: failed to accept a future block:", err) 206 } 207 cs.managedBroadcastBlock(b) 208 } 209 } 210 211 // managedAcceptBlocks will try to add blocks to the consensus set. If the 212 // blocks do not extend the longest currently known chain, an error is 213 // returned but the blocks are still kept in memory. If the blocks extend a fork 214 // such that the fork becomes the longest currently known chain, the consensus 215 // set will reorganize itself to recognize the new longest fork. Accepted 216 // blocks are not relayed. 217 // 218 // Typically AcceptBlock should be used so that the accepted block is relayed. 219 // This method is typically only be used when there would otherwise be multiple 220 // consecutive calls to AcceptBlock with each successive call accepting the 221 // child block of the previous call. 222 func (cs *ConsensusSet) managedAcceptBlocks(blocks []types.Block) (blockchainExtended bool, err error) { 223 // Grab a lock on the consensus set. 224 cs.mu.Lock() 225 defer cs.mu.Unlock() 226 227 // Make sure that blocks are consecutive. Though this isn't a strict 228 // requirement, if blocks are not consecutive then it becomes a lot harder 229 // to maintain correcetness when adding multiple blocks in a single tx. 230 // 231 // This is the first time that IDs on the blocks have been computed. 232 blockIDs := make([]types.BlockID, 0, len(blocks)) 233 for i := 0; i < len(blocks); i++ { 234 blockIDs = append(blockIDs, blocks[i].ID()) 235 if i > 0 && blocks[i].ParentID != blockIDs[i-1] { 236 return false, errNonLinearChain 237 } 238 } 239 240 // Verify the headers for every block, throw out known blocks, and the 241 // invalid blocks (which includes the children of invalid blocks). 242 chainExtended := false 243 changes := make([]changeEntry, 0, len(blocks)) 244 setErr := cs.db.Update(func(tx *bolt.Tx) error { 245 cs.log.Printf("accept: starting block processing loop (%v blocks, height %v)", len(blocks), blockHeight(tx)) 246 for i := 0; i < len(blocks); i++ { 247 // Start by checking the header of the block. 248 parent, err := cs.validateHeaderAndBlock(boltTxWrapper{tx}, blocks[i], blockIDs[i]) 249 if err == modules.ErrBlockKnown { 250 // Skip over known blocks. 251 continue 252 } 253 if err == errFutureTimestamp { 254 // Queue the block to be tried again if it is a future block. 255 go cs.threadedSleepOnFutureBlock(blocks[i]) 256 } 257 if err != nil { 258 return err 259 } 260 261 // Try adding the block to consnesus. 262 changeEntry, err := cs.addBlockToTree(tx, blocks[i], parent) 263 if err == nil { 264 changes = append(changes, changeEntry) 265 chainExtended = true 266 var applied, reverted []string 267 for _, b := range changeEntry.AppliedBlocks { 268 applied = append(applied, b.String()[:6]) 269 } 270 for _, b := range changeEntry.RevertedBlocks { 271 reverted = append(reverted, b.String()[:6]) 272 } 273 cs.log.Printf("accept: added change %v, applying blocks %v, reverting blocks %v (height now %v)", changeEntry.ID(), applied, reverted, blockHeight(tx)) 274 } 275 if err == modules.ErrNonExtendingBlock { 276 err = nil 277 } 278 if err != nil { 279 return err 280 } 281 // Sanity check - If reverted blocks is zero, applied blocks should also 282 // be zero. 283 if build.DEBUG && len(changeEntry.AppliedBlocks) == 0 && len(changeEntry.RevertedBlocks) != 0 { 284 panic("after adding a change entry, there are no applied blocks but there are reverted blocks") 285 } 286 } 287 return nil 288 }) 289 cs.log.Printf("accept: finished block processing loop") 290 if _, ok := setErr.(bolt.MmapError); ok { 291 cs.log.Println("ERROR: Bolt mmap failed:", setErr) 292 fmt.Println("Blockchain database has run out of disk space!") 293 os.Exit(1) 294 } 295 if setErr != nil { 296 if len(changes) == 0 { 297 fmt.Println("Received an invalid block set.") 298 cs.log.Println("Consensus received an invalid block:", setErr) 299 } else { 300 fmt.Println("Received a partially valid block set.") 301 cs.log.Println("Consensus received a chain of blocks, where one was valid, but others were not:", setErr) 302 } 303 return false, setErr 304 } 305 // Stop here if the blocks did not extend the longest blockchain. 306 if !chainExtended { 307 return false, modules.ErrNonExtendingBlock 308 } 309 // Send any changes to subscribers. 310 for i := 0; i < len(changes); i++ { 311 cs.updateSubscribers(changes[i]) 312 } 313 return chainExtended, nil 314 } 315 316 // AcceptBlock will try to add a block to the consensus set. If the block does 317 // not extend the longest currently known chain, an error is returned but the 318 // block is still kept in memory. If the block extends a fork such that the 319 // fork becomes the longest currently known chain, the consensus set will 320 // reorganize itself to recognize the new longest fork. If a block is accepted 321 // without error, it will be relayed to all connected peers. This function 322 // should only be called for new blocks. 323 func (cs *ConsensusSet) AcceptBlock(b types.Block) error { 324 err := cs.tg.Add() 325 if err != nil { 326 return err 327 } 328 defer cs.tg.Done() 329 330 chainExtended, err := cs.managedAcceptBlocks([]types.Block{b}) 331 if err != nil { 332 return err 333 } 334 if chainExtended { 335 cs.managedBroadcastBlock(b) 336 } 337 return nil 338 }