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