gitlab.com/jokerrs1/Sia@v1.3.2/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/NebulousLabs/Sia/encoding" 13 "github.com/NebulousLabs/Sia/modules" 14 "github.com/NebulousLabs/Sia/persist" 15 "github.com/NebulousLabs/Sia/sync" 16 "github.com/NebulousLabs/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 // ChildTarget returns the target for the child of a block. 206 func (cs *ConsensusSet) ChildTarget(id types.BlockID) (target types.Target, exists bool) { 207 // A call to a closed database can cause undefined behavior. 208 err := cs.tg.Add() 209 if err != nil { 210 return types.Target{}, false 211 } 212 defer cs.tg.Done() 213 214 _ = cs.db.View(func(tx *bolt.Tx) error { 215 pb, err := getBlockMap(tx, id) 216 if err != nil { 217 return err 218 } 219 target = pb.ChildTarget 220 exists = true 221 return nil 222 }) 223 return target, exists 224 } 225 226 // Close safely closes the block database. 227 func (cs *ConsensusSet) Close() error { 228 return cs.tg.Stop() 229 } 230 231 // managedCurrentBlock returns the latest block in the heaviest known blockchain. 232 func (cs *ConsensusSet) managedCurrentBlock() (block types.Block) { 233 cs.mu.RLock() 234 defer cs.mu.RUnlock() 235 236 _ = cs.db.View(func(tx *bolt.Tx) error { 237 pb := currentProcessedBlock(tx) 238 block = pb.Block 239 return nil 240 }) 241 return block 242 } 243 244 // CurrentBlock returns the latest block in the heaviest known blockchain. 245 func (cs *ConsensusSet) CurrentBlock() (block types.Block) { 246 // A call to a closed database can cause undefined behavior. 247 err := cs.tg.Add() 248 if err != nil { 249 return types.Block{} 250 } 251 defer cs.tg.Done() 252 253 // Block until a lock can be grabbed on the consensus set, indicating that 254 // all modules have received the most recent block. The lock is held so that 255 // there are no race conditions when trying to synchronize nodes. 256 cs.mu.Lock() 257 defer cs.mu.Unlock() 258 259 _ = cs.db.View(func(tx *bolt.Tx) error { 260 pb := currentProcessedBlock(tx) 261 block = pb.Block 262 return nil 263 }) 264 return block 265 } 266 267 // Flush will block until the consensus set has finished all in-progress 268 // routines. 269 func (cs *ConsensusSet) Flush() error { 270 return cs.tg.Flush() 271 } 272 273 // Height returns the height of the consensus set. 274 func (cs *ConsensusSet) Height() (height types.BlockHeight) { 275 // A call to a closed database can cause undefined behavior. 276 err := cs.tg.Add() 277 if err != nil { 278 return 0 279 } 280 defer cs.tg.Done() 281 282 // Block until a lock can be grabbed on the consensus set, indicating that 283 // all modules have received the most recent block. The lock is held so that 284 // there are no race conditions when trying to synchronize nodes. 285 cs.mu.Lock() 286 defer cs.mu.Unlock() 287 288 _ = cs.db.View(func(tx *bolt.Tx) error { 289 height = blockHeight(tx) 290 return nil 291 }) 292 return height 293 } 294 295 // InCurrentPath returns true if the block presented is in the current path, 296 // false otherwise. 297 func (cs *ConsensusSet) InCurrentPath(id types.BlockID) (inPath bool) { 298 // A call to a closed database can cause undefined behavior. 299 err := cs.tg.Add() 300 if err != nil { 301 return false 302 } 303 defer cs.tg.Done() 304 305 _ = cs.db.View(func(tx *bolt.Tx) error { 306 pb, err := getBlockMap(tx, id) 307 if err != nil { 308 inPath = false 309 return nil 310 } 311 pathID, err := getPath(tx, pb.Height) 312 if err != nil { 313 inPath = false 314 return nil 315 } 316 inPath = pathID == id 317 return nil 318 }) 319 return inPath 320 } 321 322 // MinimumValidChildTimestamp returns the earliest timestamp that the next block 323 // can have in order for it to be considered valid. 324 func (cs *ConsensusSet) MinimumValidChildTimestamp(id types.BlockID) (timestamp types.Timestamp, exists bool) { 325 // A call to a closed database can cause undefined behavior. 326 err := cs.tg.Add() 327 if err != nil { 328 return 0, false 329 } 330 defer cs.tg.Done() 331 332 // Error is not checked because it does not matter. 333 _ = cs.db.View(func(tx *bolt.Tx) error { 334 pb, err := getBlockMap(tx, id) 335 if err != nil { 336 return err 337 } 338 timestamp = cs.blockRuleHelper.minimumValidChildTimestamp(tx.Bucket(BlockMap), pb) 339 exists = true 340 return nil 341 }) 342 return timestamp, exists 343 } 344 345 // StorageProofSegment returns the segment to be used in the storage proof for 346 // a given file contract. 347 func (cs *ConsensusSet) StorageProofSegment(fcid types.FileContractID) (index uint64, err error) { 348 // A call to a closed database can cause undefined behavior. 349 err = cs.tg.Add() 350 if err != nil { 351 return 0, err 352 } 353 defer cs.tg.Done() 354 355 _ = cs.db.View(func(tx *bolt.Tx) error { 356 index, err = storageProofSegment(tx, fcid) 357 return nil 358 }) 359 return index, err 360 }