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