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