github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/consensus/accept.go (about) 1 package consensus 2 3 import ( 4 "bytes" 5 "errors" 6 "time" 7 8 "github.com/NebulousLabs/Sia/build" 9 "github.com/NebulousLabs/Sia/modules" 10 "github.com/NebulousLabs/Sia/types" 11 12 "github.com/NebulousLabs/bolt" 13 ) 14 15 var ( 16 errDoSBlock = errors.New("block is known to be invalid") 17 errNoBlockMap = errors.New("block map is not in database") 18 errInconsistentSet = errors.New("consensus set is not in a consistent state") 19 errOrphan = errors.New("block has no known parent") 20 ) 21 22 // validateHeaderAndBlock does some early, low computation verification on the 23 // block. Callers should not assume that validation will happen in a particular 24 // order. 25 func (cs *ConsensusSet) validateHeaderAndBlock(tx dbTx, b types.Block) error { 26 // Check if the block is a DoS block - a known invalid block that is expensive 27 // to validate. 28 id := b.ID() 29 _, exists := cs.dosBlocks[id] 30 if exists { 31 return errDoSBlock 32 } 33 34 // Check if the block is already known. 35 blockMap := tx.Bucket(BlockMap) 36 if blockMap == nil { 37 return errNoBlockMap 38 } 39 if blockMap.Get(id[:]) != nil { 40 return modules.ErrBlockKnown 41 } 42 43 // Check for the parent. 44 parentID := b.ParentID 45 parentBytes := blockMap.Get(parentID[:]) 46 if parentBytes == nil { 47 return errOrphan 48 } 49 var parent processedBlock 50 err := cs.marshaler.Unmarshal(parentBytes, &parent) 51 if err != nil { 52 return err 53 } 54 // Check that the timestamp is not too far in the past to be acceptable. 55 minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent) 56 57 return cs.blockValidator.ValidateBlock(b, minTimestamp, parent.ChildTarget, parent.Height+1) 58 } 59 60 // checkHeaderTarget returns true if the header's ID meets the given target. 61 func checkHeaderTarget(h types.BlockHeader, target types.Target) bool { 62 blockHash := h.ID() 63 return bytes.Compare(target[:], blockHash[:]) >= 0 64 } 65 66 // validateHeader does some early, low computation verification on the header 67 // to determine if the block should be downloaded. Callers should not assume 68 // that validation will happen in a particular order. 69 func (cs *ConsensusSet) validateHeader(tx dbTx, h types.BlockHeader) error { 70 // Check if the block is a DoS block - a known invalid block that is expensive 71 // to validate. 72 id := h.ID() 73 _, exists := cs.dosBlocks[id] 74 if exists { 75 return errDoSBlock 76 } 77 78 // Check if the block is already known. 79 blockMap := tx.Bucket(BlockMap) 80 if blockMap == nil { 81 return errNoBlockMap 82 } 83 if blockMap.Get(id[:]) != nil { 84 return modules.ErrBlockKnown 85 } 86 87 // Check for the parent. 88 parentID := h.ParentID 89 parentBytes := blockMap.Get(parentID[:]) 90 if parentBytes == nil { 91 return errOrphan 92 } 93 var parent processedBlock 94 err := cs.marshaler.Unmarshal(parentBytes, &parent) 95 if err != nil { 96 return err 97 } 98 99 // Check that the target of the new block is sufficient. 100 if !checkHeaderTarget(h, parent.ChildTarget) { 101 return modules.ErrBlockUnsolved 102 } 103 104 // TODO: check if the block is a non extending block once headers-first 105 // downloads are implemented. 106 107 // Check that the timestamp is not too far in the past to be acceptable. 108 minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent) 109 if minTimestamp > h.Timestamp { 110 return errEarlyTimestamp 111 } 112 113 // Check if the block is in the extreme future. We make a distinction between 114 // future and extreme future because there is an assumption that by the time 115 // the extreme future arrives, this block will no longer be a part of the 116 // longest fork because it will have been ignored by all of the miners. 117 if h.Timestamp > types.CurrentTimestamp()+types.ExtremeFutureThreshold { 118 return errExtremeFutureTimestamp 119 } 120 121 // We do not check if the header is in the near future here, because we want 122 // to get the corresponding block as soon as possible, even if the block is in 123 // the near future. 124 125 return nil 126 } 127 128 // addBlockToTree inserts a block into the blockNode tree by adding it to its 129 // parent's list of children. If the new blockNode is heavier than the current 130 // node, the blockchain is forked to put the new block and its parents at the 131 // tip. An error will be returned if block verification fails or if the block 132 // does not extend the longest fork. 133 // 134 // addBlockToTree must use its own database update because it might need to 135 // modify the database while returning an error on the block. To prevent error 136 // tracking complexity, the error is handled inside the function so that 'nil' 137 // can be appropriately returned by the database and the transaction can be 138 // committed. Switching to a managed tx through bolt will make this complexity 139 // unneeded. 140 func (cs *ConsensusSet) addBlockToTree(b types.Block) (ce changeEntry, err error) { 141 var nonExtending bool 142 err = cs.db.Update(func(tx *bolt.Tx) error { 143 pb, err := getBlockMap(tx, b.ParentID) 144 if build.DEBUG && err != nil { 145 panic(err) 146 } 147 currentNode := currentProcessedBlock(tx) 148 newNode := cs.newChild(tx, pb, b) 149 150 // modules.ErrNonExtendingBlock should be returned if the block does 151 // not extend the current blockchain, however the changes from newChild 152 // should be committed (which means 'nil' must be returned). A flag is 153 // set to indicate that modules.ErrNonExtending should be returned. 154 nonExtending = !newNode.heavierThan(currentNode) 155 if nonExtending { 156 return nil 157 } 158 var revertedBlocks, appliedBlocks []*processedBlock 159 revertedBlocks, appliedBlocks, err = cs.forkBlockchain(tx, newNode) 160 if err != nil { 161 return err 162 } 163 for _, rn := range revertedBlocks { 164 ce.RevertedBlocks = append(ce.RevertedBlocks, rn.Block.ID()) 165 } 166 for _, an := range appliedBlocks { 167 ce.AppliedBlocks = append(ce.AppliedBlocks, an.Block.ID()) 168 } 169 // To have correct error handling, appendChangeLog must be called 170 // before appending to the in-memory changelog. If this call fails, the 171 // change is going to be reverted, but the in-memory changelog is not 172 // going to be reverted. 173 // 174 // Technically, if bolt fails for some other reason (such as a 175 // filesystem error), the in-memory changelog will be incorrect anyway. 176 // Restarting Sia will fix it. The in-memory changelog is being phased 177 // out. 178 err = appendChangeLog(tx, ce) 179 if err != nil { 180 return err 181 } 182 return nil 183 }) 184 if err != nil { 185 return changeEntry{}, err 186 } 187 if nonExtending { 188 return changeEntry{}, modules.ErrNonExtendingBlock 189 } 190 return ce, nil 191 } 192 193 // managedAcceptBlock will try to add a block to the consensus set. If the 194 // block does not extend the longest currently known chain, an error is 195 // returned but the block is still kept in memory. If the block extends a fork 196 // such that the fork becomes the longest currently known chain, the consensus 197 // set will reorganize itself to recognize the new longest fork. Accepted 198 // blocks are not relayed. 199 // 200 // Typically AcceptBlock should be used so that the accepted block is relayed. 201 // This method is typically only be used when there would otherwise be multiple 202 // consecutive calls to AcceptBlock with each successive call accepting the 203 // child block of the previous call. 204 func (cs *ConsensusSet) managedAcceptBlock(b types.Block) error { 205 // Grab a lock on the consensus set. Lock is demoted later in the function, 206 // failure to unlock before returning an error will cause a deadlock. 207 cs.mu.Lock() 208 209 // Start verification inside of a bolt View tx. 210 err := cs.db.View(func(tx *bolt.Tx) error { 211 // Do not accept a block if the database is inconsistent. 212 if inconsistencyDetected(tx) { 213 return errInconsistentSet 214 } 215 216 // Do some relatively inexpensive checks to validate the header and block. 217 // Validation generally occurs in the order of least expensive validation 218 // first. 219 err := cs.validateHeaderAndBlock(boltTxWrapper{tx}, b) 220 if err != nil { 221 // If the block is in the near future, but too far to be acceptable, then 222 // save the block and add it to the consensus set after it is no longer 223 // too far in the future. 224 // 225 // TODO: an attacker could mine many blocks off the genesis block all in the 226 // future and we would spawn a goroutine per each block. To fix this, either 227 // ban peers that send lots of future blocks and stop spawning goroutines 228 // after we are already waiting on a large number of future blocks. 229 // 230 // TODO: an attacker could broadcast a future block many times and we would 231 // spawn a goroutine for each broadcast. To fix this we should create a 232 // cache of future blocks, like we already do for DoS blocks, and only spawn 233 // a goroutine if we haven't already spawned one for that block. To limit 234 // the size of the cache of future blocks, make it a constant size (say 50) 235 // over which we would evict the block furthest in the future before adding 236 // a new block to the cache. 237 if err == errFutureTimestamp { 238 go func() { 239 time.Sleep(time.Duration(b.Timestamp-(types.CurrentTimestamp()+types.FutureThreshold)) * time.Second) 240 err := cs.AcceptBlock(b) 241 if err != nil { 242 cs.log.Debugln("WARN: failed to accept a future block:", err) 243 } 244 }() 245 } 246 return err 247 } 248 return nil 249 }) 250 if err != nil { 251 cs.mu.Unlock() 252 return err 253 } 254 255 // Try adding the block to the block tree. This call will perform 256 // verification on the block before adding the block to the block tree. An 257 // error is returned if verification fails or if the block does not extend 258 // the longest fork. 259 changeEntry, err := cs.addBlockToTree(b) 260 if err != nil { 261 cs.mu.Unlock() 262 return err 263 } 264 // If appliedBlocks is 0, revertedBlocks will also be 0. 265 if build.DEBUG && len(changeEntry.AppliedBlocks) == 0 && len(changeEntry.RevertedBlocks) != 0 { 266 panic("appliedBlocks and revertedBlocks are mismatched!") 267 } 268 269 // Updates complete, demote the lock. 270 cs.mu.Demote() 271 defer cs.mu.DemotedUnlock() 272 if len(changeEntry.AppliedBlocks) > 0 { 273 cs.readlockUpdateSubscribers(changeEntry) 274 } 275 return nil 276 } 277 278 // AcceptBlock will try to add a block to the consensus set. If the block does 279 // not extend the longest currently known chain, an error is returned but the 280 // block is still kept in memory. If the block extends a fork such that the 281 // fork becomes the longest currently known chain, the consensus set will 282 // reorganize itself to recognize the new longest fork. If a block is accepted 283 // without error, it will be relayed to all connected peers. This function 284 // should only be called for new blocks. 285 func (cs *ConsensusSet) AcceptBlock(b types.Block) error { 286 err := cs.managedAcceptBlock(b) 287 if err != nil { 288 return err 289 } 290 // COMPATv0.5.1 - broadcast the block to all peers <= v0.5.1 and block header to all peers > v0.5.1. 291 var relayBlockPeers, relayHeaderPeers []modules.Peer 292 for _, p := range cs.gateway.Peers() { 293 if build.VersionCmp(p.Version, "0.5.1") <= 0 { 294 relayBlockPeers = append(relayBlockPeers, p) 295 } else { 296 relayHeaderPeers = append(relayHeaderPeers, p) 297 } 298 } 299 go cs.gateway.Broadcast("RelayBlock", b, relayBlockPeers) 300 go cs.gateway.Broadcast("RelayHeader", b.Header(), relayHeaderPeers) 301 return nil 302 }