github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/modules/consensus/consensusset.go (about) 1 package consensus 2 3 // All changes to the consenuss set are made via diffs, specifically by calling 4 // a commitDiff function. This means that future modifications (such as 5 // replacing in-memory versions of the utxo set with on-disk versions of the 6 // utxo set) should be relatively easy to verify for correctness. Modifying the 7 // commitDiff functions will be sufficient. 8 9 import ( 10 "errors" 11 12 "github.com/Synthesix/Sia/encoding" 13 "github.com/Synthesix/Sia/modules" 14 "github.com/Synthesix/Sia/persist" 15 "github.com/Synthesix/Sia/sync" 16 "github.com/Synthesix/Sia/types" 17 18 "github.com/NebulousLabs/demotemutex" 19 "github.com/coreos/bbolt" 20 ) 21 22 var ( 23 errNilGateway = errors.New("cannot have a nil gateway as input") 24 ) 25 26 // marshaler marshals objects into byte slices and unmarshals byte 27 // slices into objects. 28 type marshaler interface { 29 Marshal(interface{}) []byte 30 Unmarshal([]byte, interface{}) error 31 } 32 type stdMarshaler struct{} 33 34 func (stdMarshaler) Marshal(v interface{}) []byte { return encoding.Marshal(v) } 35 func (stdMarshaler) Unmarshal(b []byte, v interface{}) error { return encoding.Unmarshal(b, v) } 36 37 // The ConsensusSet is the object responsible for tracking the current status 38 // of the blockchain. Broadly speaking, it is responsible for maintaining 39 // consensus. It accepts blocks and constructs a blockchain, forking when 40 // necessary. 41 type ConsensusSet struct { 42 // The gateway manages peer connections and keeps the consensus set 43 // synchronized to the rest of the network. 44 gateway modules.Gateway 45 46 // The block root contains the genesis block. 47 blockRoot processedBlock 48 49 // Subscribers to the consensus set will receive a changelog every time 50 // there is an update to the consensus set. At initialization, they receive 51 // all changes that they are missing. 52 // 53 // Memory: A consensus set typically has fewer than 10 subscribers, and 54 // subscription typically happens entirely at startup. This slice is 55 // unlikely to grow beyond 1kb, and cannot by manipulated by an attacker as 56 // the function of adding a subscriber should not be exposed. 57 subscribers []modules.ConsensusSetSubscriber 58 59 // dosBlocks are blocks that are invalid, but the invalidity is only 60 // discoverable during an expensive step of validation. These blocks are 61 // recorded to eliminate a DoS vector where an expensive-to-validate block 62 // is submitted to the consensus set repeatedly. 63 // 64 // TODO: dosBlocks needs to be moved into the database, and if there's some 65 // reason it can't be in THE database, it should be in a separate database. 66 // dosBlocks is an unbounded map that an attacker can manipulate, though 67 // iirc manipulations are expensive, to the tune of creating a blockchain 68 // PoW per DoS block (though the attacker could conceivably build off of 69 // the genesis block, meaning the PoW is not very expensive. 70 dosBlocks map[types.BlockID]struct{} 71 72 // checkingConsistency is a bool indicating whether or not a consistency 73 // check is in progress. The consistency check logic call itself, resulting 74 // in infinite loops. This bool prevents that while still allowing for full 75 // granularity consistency checks. Previously, consistency checks were only 76 // performed after a full reorg, but now they are performed after every 77 // block. 78 checkingConsistency bool 79 80 // synced is true if initial blockchain download has finished. It indicates 81 // whether the consensus set is synced with the network. 82 synced bool 83 84 // Interfaces to abstract the dependencies of the ConsensusSet. 85 marshaler marshaler 86 blockRuleHelper blockRuleHelper 87 blockValidator blockValidator 88 89 // Utilities 90 db *persist.BoltDatabase 91 log *persist.Logger 92 mu demotemutex.DemoteMutex 93 persistDir string 94 tg sync.ThreadGroup 95 } 96 97 // New returns a new ConsensusSet, containing at least the genesis block. If 98 // there is an existing block database present in the persist directory, it 99 // will be loaded. 100 func New(gateway modules.Gateway, bootstrap bool, persistDir string) (*ConsensusSet, error) { 101 // Check for nil dependencies. 102 if gateway == nil { 103 return nil, errNilGateway 104 } 105 106 // Create the ConsensusSet object. 107 cs := &ConsensusSet{ 108 gateway: gateway, 109 110 blockRoot: processedBlock{ 111 Block: types.GenesisBlock, 112 ChildTarget: types.RootTarget, 113 Depth: types.RootDepth, 114 115 DiffsGenerated: true, 116 }, 117 118 dosBlocks: make(map[types.BlockID]struct{}), 119 120 marshaler: stdMarshaler{}, 121 blockRuleHelper: stdBlockRuleHelper{}, 122 blockValidator: NewBlockValidator(), 123 124 persistDir: persistDir, 125 } 126 127 // Create the diffs for the genesis siafund outputs. 128 for i, siafundOutput := range types.GenesisBlock.Transactions[0].SiafundOutputs { 129 sfid := types.GenesisBlock.Transactions[0].SiafundOutputID(uint64(i)) 130 sfod := modules.SiafundOutputDiff{ 131 Direction: modules.DiffApply, 132 ID: sfid, 133 SiafundOutput: siafundOutput, 134 } 135 cs.blockRoot.SiafundOutputDiffs = append(cs.blockRoot.SiafundOutputDiffs, sfod) 136 } 137 138 // Initialize the consensus persistence structures. 139 err := cs.initPersist() 140 if err != nil { 141 return nil, err 142 } 143 144 go func() { 145 // Sync with the network. Don't sync if we are testing because 146 // typically we don't have any mock peers to synchronize with in 147 // testing. 148 if bootstrap { 149 // We are in a virgin goroutine right now, so calling the threaded 150 // function without a goroutine is okay. 151 err = cs.threadedInitialBlockchainDownload() 152 if err != nil { 153 return 154 } 155 } 156 157 // threadedInitialBlockchainDownload will release the threadgroup 'Add' 158 // it was holding, so another needs to be grabbed to finish off this 159 // goroutine. 160 err = cs.tg.Add() 161 if err != nil { 162 return 163 } 164 defer cs.tg.Done() 165 166 // Register RPCs 167 gateway.RegisterRPC("SendBlocks", cs.rpcSendBlocks) 168 gateway.RegisterRPC("RelayHeader", cs.threadedRPCRelayHeader) 169 gateway.RegisterRPC("SendBlk", cs.rpcSendBlk) 170 gateway.RegisterConnectCall("SendBlocks", cs.threadedReceiveBlocks) 171 cs.tg.OnStop(func() { 172 cs.gateway.UnregisterRPC("SendBlocks") 173 cs.gateway.UnregisterRPC("RelayHeader") 174 cs.gateway.UnregisterRPC("SendBlk") 175 cs.gateway.UnregisterConnectCall("SendBlocks") 176 }) 177 178 // Mark that we are synced with the network. 179 cs.mu.Lock() 180 cs.synced = true 181 cs.mu.Unlock() 182 }() 183 184 return cs, nil 185 } 186 187 // BlockAtHeight returns the block at a given height. 188 func (cs *ConsensusSet) BlockAtHeight(height types.BlockHeight) (block types.Block, exists bool) { 189 _ = cs.db.View(func(tx *bolt.Tx) error { 190 id, err := getPath(tx, height) 191 if err != nil { 192 return err 193 } 194 pb, err := getBlockMap(tx, id) 195 if err != nil { 196 return err 197 } 198 block = pb.Block 199 exists = true 200 return nil 201 }) 202 return block, exists 203 } 204 205 // BlockByID returns the block for a given BlockID. 206 func (cs *ConsensusSet) BlockByID(id types.BlockID) (block types.Block, exists bool) { 207 _ = cs.db.View(func(tx *bolt.Tx) error { 208 pb, err := getBlockMap(tx, id) 209 if err != nil { 210 return err 211 } 212 block = pb.Block 213 exists = true 214 return nil 215 }) 216 return block, exists 217 } 218 219 // ChildTarget returns the target for the child of a block. 220 func (cs *ConsensusSet) ChildTarget(id types.BlockID) (target types.Target, exists bool) { 221 // A call to a closed database can cause undefined behavior. 222 err := cs.tg.Add() 223 if err != nil { 224 return types.Target{}, false 225 } 226 defer cs.tg.Done() 227 228 _ = cs.db.View(func(tx *bolt.Tx) error { 229 pb, err := getBlockMap(tx, id) 230 if err != nil { 231 return err 232 } 233 target = pb.ChildTarget 234 exists = true 235 return nil 236 }) 237 return target, exists 238 } 239 240 // Close safely closes the block database. 241 func (cs *ConsensusSet) Close() error { 242 return cs.tg.Stop() 243 } 244 245 // managedCurrentBlock returns the latest block in the heaviest known blockchain. 246 func (cs *ConsensusSet) managedCurrentBlock() (block types.Block) { 247 cs.mu.RLock() 248 defer cs.mu.RUnlock() 249 250 _ = cs.db.View(func(tx *bolt.Tx) error { 251 pb := currentProcessedBlock(tx) 252 block = pb.Block 253 return nil 254 }) 255 return block 256 } 257 258 // CurrentBlock returns the latest block in the heaviest known blockchain. 259 func (cs *ConsensusSet) CurrentBlock() (block types.Block) { 260 // A call to a closed database can cause undefined behavior. 261 err := cs.tg.Add() 262 if err != nil { 263 return types.Block{} 264 } 265 defer cs.tg.Done() 266 267 // Block until a lock can be grabbed on the consensus set, indicating that 268 // all modules have received the most recent block. The lock is held so that 269 // there are no race conditions when trying to synchronize nodes. 270 cs.mu.Lock() 271 defer cs.mu.Unlock() 272 273 _ = cs.db.View(func(tx *bolt.Tx) error { 274 pb := currentProcessedBlock(tx) 275 block = pb.Block 276 return nil 277 }) 278 return block 279 } 280 281 // Flush will block until the consensus set has finished all in-progress 282 // routines. 283 func (cs *ConsensusSet) Flush() error { 284 return cs.tg.Flush() 285 } 286 287 // Height returns the height of the consensus set. 288 func (cs *ConsensusSet) Height() (height types.BlockHeight) { 289 // A call to a closed database can cause undefined behavior. 290 err := cs.tg.Add() 291 if err != nil { 292 return 0 293 } 294 defer cs.tg.Done() 295 296 // Block until a lock can be grabbed on the consensus set, indicating that 297 // all modules have received the most recent block. The lock is held so that 298 // there are no race conditions when trying to synchronize nodes. 299 cs.mu.Lock() 300 defer cs.mu.Unlock() 301 302 _ = cs.db.View(func(tx *bolt.Tx) error { 303 height = blockHeight(tx) 304 return nil 305 }) 306 return height 307 } 308 309 // InCurrentPath returns true if the block presented is in the current path, 310 // false otherwise. 311 func (cs *ConsensusSet) InCurrentPath(id types.BlockID) (inPath bool) { 312 // A call to a closed database can cause undefined behavior. 313 err := cs.tg.Add() 314 if err != nil { 315 return false 316 } 317 defer cs.tg.Done() 318 319 _ = cs.db.View(func(tx *bolt.Tx) error { 320 pb, err := getBlockMap(tx, id) 321 if err != nil { 322 inPath = false 323 return nil 324 } 325 pathID, err := getPath(tx, pb.Height) 326 if err != nil { 327 inPath = false 328 return nil 329 } 330 inPath = pathID == id 331 return nil 332 }) 333 return inPath 334 } 335 336 // MinimumValidChildTimestamp returns the earliest timestamp that the next block 337 // can have in order for it to be considered valid. 338 func (cs *ConsensusSet) MinimumValidChildTimestamp(id types.BlockID) (timestamp types.Timestamp, exists bool) { 339 // A call to a closed database can cause undefined behavior. 340 err := cs.tg.Add() 341 if err != nil { 342 return 0, false 343 } 344 defer cs.tg.Done() 345 346 // Error is not checked because it does not matter. 347 _ = cs.db.View(func(tx *bolt.Tx) error { 348 pb, err := getBlockMap(tx, id) 349 if err != nil { 350 return err 351 } 352 timestamp = cs.blockRuleHelper.minimumValidChildTimestamp(tx.Bucket(BlockMap), pb) 353 exists = true 354 return nil 355 }) 356 return timestamp, exists 357 } 358 359 // StorageProofSegment returns the segment to be used in the storage proof for 360 // a given file contract. 361 func (cs *ConsensusSet) StorageProofSegment(fcid types.FileContractID) (index uint64, err error) { 362 // A call to a closed database can cause undefined behavior. 363 err = cs.tg.Add() 364 if err != nil { 365 return 0, err 366 } 367 defer cs.tg.Done() 368 369 _ = cs.db.View(func(tx *bolt.Tx) error { 370 index, err = storageProofSegment(tx, fcid) 371 return nil 372 }) 373 return index, err 374 }