github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/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 "fmt" 12 13 "github.com/NebulousLabs/Sia/build" 14 "github.com/NebulousLabs/Sia/encoding" 15 "github.com/NebulousLabs/Sia/modules" 16 "github.com/NebulousLabs/Sia/persist" 17 "github.com/NebulousLabs/Sia/types" 18 19 "github.com/NebulousLabs/bolt" 20 "github.com/NebulousLabs/demotemutex" 21 ) 22 23 var ( 24 errNilGateway = errors.New("cannot have a nil gateway as input") 25 ) 26 27 // The ConsensusSet is the object responsible for tracking the current status 28 // of the blockchain. Broadly speaking, it is responsible for maintaining 29 // consensus. It accepts blocks and constructs a blockchain, forking when 30 // necessary. 31 type ConsensusSet struct { 32 // The gateway manages peer connections and keeps the consensus set 33 // synchronized to the rest of the network. 34 gateway modules.Gateway 35 36 // The block root contains the genesis block. 37 blockRoot processedBlock 38 39 // Subscribers to the consensus set will receive a changelog every time 40 // there is an update to the consensus set. At initialization, they receive 41 // all changes that they are missing. 42 // 43 // Memory: A consensus set typically has fewer than 10 subscribers, and 44 // subscription typically happens entirely at startup. This slice is 45 // unlikely to grow beyond 1kb, and cannot by manipulated by an attacker as 46 // the function of adding a subscriber should not be exposed. 47 subscribers []modules.ConsensusSetSubscriber 48 49 // dosBlocks are blocks that are invalid, but the invalidity is only 50 // discoverable during an expensive step of validation. These blocks are 51 // recorded to eliminate a DoS vector where an expensive-to-validate block 52 // is submitted to the consensus set repeatedly. 53 // 54 // TODO: dosBlocks needs to be moved into the database, and if there's some 55 // reason it can't be in THE database, it should be in a separate database. 56 // dosBlocks is an unbounded map that an attacker can manipulate, though 57 // iirc manipulations are expensive, to the tune of creating a blockchain 58 // PoW per DoS block (though the attacker could conceivably build off of 59 // the genesis block, meaning the PoW is not very expensive. 60 dosBlocks map[types.BlockID]struct{} 61 62 // checkingConsistency is a bool indicating whether or not a consistency 63 // check is in progress. The consistency check logic call itself, resulting 64 // in infinite loops. This bool prevents that while still allowing for full 65 // granularity consistency checks. Previously, consistency checks were only 66 // performed after a full reorg, but now they are performed after every 67 // block. 68 checkingConsistency bool 69 70 // synced is true if initial blockchain download has finished. It indicates 71 // whether the consensus set is synced with the network. 72 synced bool 73 74 // Interfaces to abstract the dependencies of the ConsensusSet. 75 marshaler encoding.GenericMarshaler 76 blockRuleHelper blockRuleHelper 77 blockValidator blockValidator 78 79 // Utilities 80 db *persist.BoltDatabase 81 log *persist.Logger 82 mu demotemutex.DemoteMutex 83 persistDir string 84 } 85 86 // New returns a new ConsensusSet, containing at least the genesis block. If 87 // there is an existing block database present in the persist directory, it 88 // will be loaded. 89 func New(gateway modules.Gateway, persistDir string) (*ConsensusSet, error) { 90 // Check for nil dependencies. 91 if gateway == nil { 92 return nil, errNilGateway 93 } 94 95 // Create the ConsensusSet object. 96 cs := &ConsensusSet{ 97 gateway: gateway, 98 99 blockRoot: processedBlock{ 100 Block: types.GenesisBlock, 101 ChildTarget: types.RootTarget, 102 Depth: types.RootDepth, 103 104 DiffsGenerated: true, 105 }, 106 107 dosBlocks: make(map[types.BlockID]struct{}), 108 109 marshaler: encoding.StdGenericMarshaler{}, 110 blockRuleHelper: stdBlockRuleHelper{}, 111 blockValidator: NewBlockValidator(), 112 113 persistDir: persistDir, 114 } 115 116 // Create the diffs for the genesis siafund outputs. 117 for i, siafundOutput := range types.GenesisBlock.Transactions[0].SiafundOutputs { 118 sfid := types.GenesisBlock.Transactions[0].SiafundOutputID(uint64(i)) 119 sfod := modules.SiafundOutputDiff{ 120 Direction: modules.DiffApply, 121 ID: sfid, 122 SiafundOutput: siafundOutput, 123 } 124 cs.blockRoot.SiafundOutputDiffs = append(cs.blockRoot.SiafundOutputDiffs, sfod) 125 } 126 127 // Initialize the consensus persistence structures. 128 err := cs.initPersist() 129 if err != nil { 130 return nil, err 131 } 132 133 go func() { 134 // Sync with the network. Don't sync if we are testing because typically we 135 // don't have any mock peers to synchronize with in testing. 136 // TODO: figure out a better way to conditionally do IBD. Otherwise this block will never be tested. 137 if build.Release != "testing" { 138 cs.threadedInitialBlockchainDownload() 139 } 140 141 // Register RPCs 142 gateway.RegisterRPC("SendBlocks", cs.rpcSendBlocks) 143 gateway.RegisterRPC("RelayBlock", cs.rpcRelayBlock) // COMPATv0.5.1 144 gateway.RegisterRPC("RelayHeader", cs.rpcRelayHeader) 145 gateway.RegisterRPC("SendBlk", cs.rpcSendBlk) 146 gateway.RegisterConnectCall("SendBlocks", cs.threadedReceiveBlocks) 147 148 // Mark that we are synced with the network. 149 cs.mu.Lock() 150 cs.synced = true 151 cs.mu.Unlock() 152 }() 153 154 return cs, nil 155 } 156 157 // BlockAtHeight returns the block at a given height. 158 func (cs *ConsensusSet) BlockAtHeight(height types.BlockHeight) (block types.Block, exists bool) { 159 _ = cs.db.View(func(tx *bolt.Tx) error { 160 id, err := getPath(tx, height) 161 if err != nil { 162 return err 163 } 164 pb, err := getBlockMap(tx, id) 165 if err != nil { 166 return err 167 } 168 block = pb.Block 169 exists = true 170 return nil 171 }) 172 return block, exists 173 } 174 175 // ChildTarget returns the target for the child of a block. 176 func (cs *ConsensusSet) ChildTarget(id types.BlockID) (target types.Target, exists bool) { 177 _ = cs.db.View(func(tx *bolt.Tx) error { 178 pb, err := getBlockMap(tx, id) 179 if err != nil { 180 return err 181 } 182 target = pb.ChildTarget 183 exists = true 184 return nil 185 }) 186 return target, exists 187 } 188 189 // Close safely closes the block database. 190 func (cs *ConsensusSet) Close() error { 191 cs.mu.Lock() 192 defer cs.mu.Unlock() 193 194 var errs []error 195 if err := cs.db.Close(); err != nil { 196 errs = append(errs, fmt.Errorf("db.Close failed: %v", err)) 197 } 198 if err := cs.log.Close(); err != nil { 199 errs = append(errs, fmt.Errorf("log.Close failed: %v", err)) 200 } 201 return build.JoinErrors(errs, "; ") 202 } 203 204 // CurrentBlock returns the latest block in the heaviest known blockchain. 205 func (cs *ConsensusSet) CurrentBlock() (block types.Block) { 206 _ = cs.db.View(func(tx *bolt.Tx) error { 207 pb := currentProcessedBlock(tx) 208 block = pb.Block 209 return nil 210 }) 211 return block 212 } 213 214 // Height returns the height of the consensus set. 215 func (cs *ConsensusSet) Height() (height types.BlockHeight) { 216 _ = cs.db.View(func(tx *bolt.Tx) error { 217 height = blockHeight(tx) 218 return nil 219 }) 220 return height 221 } 222 223 // InCurrentPath returns true if the block presented is in the current path, 224 // false otherwise. 225 func (cs *ConsensusSet) InCurrentPath(id types.BlockID) (inPath bool) { 226 _ = cs.db.View(func(tx *bolt.Tx) error { 227 pb, err := getBlockMap(tx, id) 228 if err != nil { 229 inPath = false 230 return nil 231 } 232 pathID, err := getPath(tx, pb.Height) 233 if err != nil { 234 inPath = false 235 return nil 236 } 237 inPath = pathID == id 238 return nil 239 }) 240 return inPath 241 } 242 243 // MinimumValidChildTimestamp returns the earliest timestamp that the next block 244 // can have in order for it to be considered valid. 245 func (cs *ConsensusSet) MinimumValidChildTimestamp(id types.BlockID) (timestamp types.Timestamp, exists bool) { 246 // Error is not checked because it does not matter. 247 _ = cs.db.View(func(tx *bolt.Tx) error { 248 pb, err := getBlockMap(tx, id) 249 if err != nil { 250 return err 251 } 252 timestamp = cs.blockRuleHelper.minimumValidChildTimestamp(tx.Bucket(BlockMap), pb) 253 exists = true 254 return nil 255 }) 256 return timestamp, exists 257 } 258 259 // StorageProofSegment returns the segment to be used in the storage proof for 260 // a given file contract. 261 func (cs *ConsensusSet) StorageProofSegment(fcid types.FileContractID) (index uint64, err error) { 262 _ = cs.db.View(func(tx *bolt.Tx) error { 263 index, err = storageProofSegment(tx, fcid) 264 return nil 265 }) 266 return index, err 267 }