gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/explorer/update.go (about) 1 package explorer 2 3 import ( 4 "fmt" 5 6 bolt "github.com/coreos/bbolt" 7 8 "gitlab.com/SiaPrime/SiaPrime/build" 9 "gitlab.com/SiaPrime/SiaPrime/encoding" 10 "gitlab.com/SiaPrime/SiaPrime/modules" 11 "gitlab.com/SiaPrime/SiaPrime/types" 12 ) 13 14 // ProcessConsensusChange follows the most recent changes to the consensus set, 15 // including parsing new blocks and updating the utxo sets. 16 func (e *Explorer) ProcessConsensusChange(cc modules.ConsensusChange) { 17 if len(cc.AppliedBlocks) == 0 { 18 build.Critical("Explorer.ProcessConsensusChange called with a ConsensusChange that has no AppliedBlocks") 19 } 20 21 err := e.db.Update(func(tx *bolt.Tx) (err error) { 22 // use exception-style error handling to enable more concise update code 23 defer func() { 24 if r := recover(); r != nil { 25 err = fmt.Errorf("%v", r) 26 } 27 }() 28 29 // get starting block height 30 var blockheight types.BlockHeight 31 err = dbGetInternal(internalBlockHeight, &blockheight)(tx) 32 if err != nil { 33 return err 34 } 35 36 // Update cumulative stats for reverted blocks. 37 for _, block := range cc.RevertedBlocks { 38 bid := block.ID() 39 tbid := types.TransactionID(bid) 40 41 blockheight-- 42 dbRemoveBlockID(tx, bid) 43 dbRemoveTransactionID(tx, tbid) // Miner payouts are a transaction 44 45 target, exists := e.cs.ChildTarget(block.ParentID) 46 if !exists { 47 target = types.RootTarget 48 } 49 dbRemoveBlockTarget(tx, bid, target) 50 51 // Remove miner payouts 52 for j, payout := range block.MinerPayouts { 53 scoid := block.MinerPayoutID(uint64(j)) 54 dbRemoveSiacoinOutputID(tx, scoid, tbid) 55 dbRemoveUnlockHash(tx, payout.UnlockHash, tbid) 56 } 57 58 // Remove transactions 59 for _, txn := range block.Transactions { 60 txid := txn.ID() 61 dbRemoveTransactionID(tx, txid) 62 63 for _, sci := range txn.SiacoinInputs { 64 dbRemoveSiacoinOutputID(tx, sci.ParentID, txid) 65 dbRemoveUnlockHash(tx, sci.UnlockConditions.UnlockHash(), txid) 66 } 67 for k, sco := range txn.SiacoinOutputs { 68 scoid := txn.SiacoinOutputID(uint64(k)) 69 dbRemoveSiacoinOutputID(tx, scoid, txid) 70 dbRemoveUnlockHash(tx, sco.UnlockHash, txid) 71 dbRemoveSiacoinOutput(tx, scoid) 72 } 73 for k, fc := range txn.FileContracts { 74 fcid := txn.FileContractID(uint64(k)) 75 dbRemoveFileContractID(tx, fcid, txid) 76 dbRemoveUnlockHash(tx, fc.UnlockHash, txid) 77 for l, sco := range fc.ValidProofOutputs { 78 scoid := fcid.StorageProofOutputID(types.ProofValid, uint64(l)) 79 dbRemoveSiacoinOutputID(tx, scoid, txid) 80 dbRemoveUnlockHash(tx, sco.UnlockHash, txid) 81 } 82 for l, sco := range fc.MissedProofOutputs { 83 scoid := fcid.StorageProofOutputID(types.ProofMissed, uint64(l)) 84 dbRemoveSiacoinOutputID(tx, scoid, txid) 85 dbRemoveUnlockHash(tx, sco.UnlockHash, txid) 86 } 87 dbRemoveFileContract(tx, fcid) 88 } 89 for _, fcr := range txn.FileContractRevisions { 90 dbRemoveFileContractID(tx, fcr.ParentID, txid) 91 dbRemoveUnlockHash(tx, fcr.UnlockConditions.UnlockHash(), txid) 92 dbRemoveUnlockHash(tx, fcr.NewUnlockHash, txid) 93 for l, sco := range fcr.NewValidProofOutputs { 94 scoid := fcr.ParentID.StorageProofOutputID(types.ProofValid, uint64(l)) 95 dbRemoveSiacoinOutputID(tx, scoid, txid) 96 dbRemoveUnlockHash(tx, sco.UnlockHash, txid) 97 } 98 for l, sco := range fcr.NewMissedProofOutputs { 99 scoid := fcr.ParentID.StorageProofOutputID(types.ProofMissed, uint64(l)) 100 dbRemoveSiacoinOutputID(tx, scoid, txid) 101 dbRemoveUnlockHash(tx, sco.UnlockHash, txid) 102 } 103 // Remove the file contract revision from the revision chain. 104 dbRemoveFileContractRevision(tx, fcr.ParentID) 105 } 106 for _, sp := range txn.StorageProofs { 107 dbRemoveStorageProof(tx, sp.ParentID) 108 } 109 for _, sfi := range txn.SiafundInputs { 110 dbRemoveSiafundOutputID(tx, sfi.ParentID, txid) 111 dbRemoveUnlockHash(tx, sfi.UnlockConditions.UnlockHash(), txid) 112 dbRemoveUnlockHash(tx, sfi.ClaimUnlockHash, txid) 113 } 114 for k, sfo := range txn.SiafundOutputs { 115 sfoid := txn.SiafundOutputID(uint64(k)) 116 dbRemoveSiafundOutputID(tx, sfoid, txid) 117 dbRemoveUnlockHash(tx, sfo.UnlockHash, txid) 118 } 119 } 120 121 // remove the associated block facts 122 dbRemoveBlockFacts(tx, bid) 123 } 124 125 // Update cumulative stats for applied blocks. 126 for _, block := range cc.AppliedBlocks { 127 bid := block.ID() 128 tbid := types.TransactionID(bid) 129 130 // special handling for genesis block 131 if bid == types.GenesisID { 132 dbAddGenesisBlock(tx) 133 continue 134 } 135 136 blockheight++ 137 dbAddBlockID(tx, bid, blockheight) 138 dbAddTransactionID(tx, tbid, blockheight) // Miner payouts are a transaction 139 140 target, exists := e.cs.ChildTarget(block.ParentID) 141 if !exists { 142 target = types.RootTarget 143 } 144 dbAddBlockTarget(tx, bid, target) 145 146 // Catalog the new miner payouts. 147 for j, payout := range block.MinerPayouts { 148 scoid := block.MinerPayoutID(uint64(j)) 149 dbAddSiacoinOutputID(tx, scoid, tbid) 150 dbAddUnlockHash(tx, payout.UnlockHash, tbid) 151 } 152 153 // Update cumulative stats for applied transactions. 154 for _, txn := range block.Transactions { 155 // Add the transaction to the list of active transactions. 156 txid := txn.ID() 157 dbAddTransactionID(tx, txid, blockheight) 158 159 for _, sci := range txn.SiacoinInputs { 160 dbAddSiacoinOutputID(tx, sci.ParentID, txid) 161 dbAddUnlockHash(tx, sci.UnlockConditions.UnlockHash(), txid) 162 } 163 for j, sco := range txn.SiacoinOutputs { 164 scoid := txn.SiacoinOutputID(uint64(j)) 165 dbAddSiacoinOutputID(tx, scoid, txid) 166 dbAddUnlockHash(tx, sco.UnlockHash, txid) 167 } 168 for k, fc := range txn.FileContracts { 169 fcid := txn.FileContractID(uint64(k)) 170 dbAddFileContractID(tx, fcid, txid) 171 dbAddUnlockHash(tx, fc.UnlockHash, txid) 172 dbAddFileContract(tx, fcid, fc) 173 for l, sco := range fc.ValidProofOutputs { 174 scoid := fcid.StorageProofOutputID(types.ProofValid, uint64(l)) 175 dbAddSiacoinOutputID(tx, scoid, txid) 176 dbAddUnlockHash(tx, sco.UnlockHash, txid) 177 } 178 for l, sco := range fc.MissedProofOutputs { 179 scoid := fcid.StorageProofOutputID(types.ProofMissed, uint64(l)) 180 dbAddSiacoinOutputID(tx, scoid, txid) 181 dbAddUnlockHash(tx, sco.UnlockHash, txid) 182 } 183 } 184 for _, fcr := range txn.FileContractRevisions { 185 dbAddFileContractID(tx, fcr.ParentID, txid) 186 dbAddUnlockHash(tx, fcr.UnlockConditions.UnlockHash(), txid) 187 dbAddUnlockHash(tx, fcr.NewUnlockHash, txid) 188 for l, sco := range fcr.NewValidProofOutputs { 189 scoid := fcr.ParentID.StorageProofOutputID(types.ProofValid, uint64(l)) 190 dbAddSiacoinOutputID(tx, scoid, txid) 191 dbAddUnlockHash(tx, sco.UnlockHash, txid) 192 } 193 for l, sco := range fcr.NewMissedProofOutputs { 194 scoid := fcr.ParentID.StorageProofOutputID(types.ProofMissed, uint64(l)) 195 dbAddSiacoinOutputID(tx, scoid, txid) 196 dbAddUnlockHash(tx, sco.UnlockHash, txid) 197 } 198 dbAddFileContractRevision(tx, fcr.ParentID, fcr) 199 } 200 for _, sp := range txn.StorageProofs { 201 dbAddFileContractID(tx, sp.ParentID, txid) 202 dbAddStorageProof(tx, sp.ParentID, sp) 203 } 204 for _, sfi := range txn.SiafundInputs { 205 dbAddSiafundOutputID(tx, sfi.ParentID, txid) 206 dbAddUnlockHash(tx, sfi.UnlockConditions.UnlockHash(), txid) 207 dbAddUnlockHash(tx, sfi.ClaimUnlockHash, txid) 208 } 209 for k, sfo := range txn.SiafundOutputs { 210 sfoid := txn.SiafundOutputID(uint64(k)) 211 dbAddSiafundOutputID(tx, sfoid, txid) 212 dbAddUnlockHash(tx, sfo.UnlockHash, txid) 213 } 214 } 215 216 // calculate and add new block facts, if possible 217 if tx.Bucket(bucketBlockFacts).Get(encoding.Marshal(block.ParentID)) != nil { 218 facts := dbCalculateBlockFacts(tx, e.cs, block) 219 dbAddBlockFacts(tx, facts) 220 } 221 } 222 223 // Update stats according to SiacoinOutputDiffs 224 for _, scod := range cc.SiacoinOutputDiffs { 225 if scod.Direction == modules.DiffApply { 226 dbAddSiacoinOutput(tx, scod.ID, scod.SiacoinOutput) 227 } 228 } 229 230 // Update stats according to SiafundOutputDiffs 231 for _, sfod := range cc.SiafundOutputDiffs { 232 if sfod.Direction == modules.DiffApply { 233 dbAddSiafundOutput(tx, sfod.ID, sfod.SiafundOutput) 234 } 235 } 236 237 // Compute the changes in the active set. Note, because this is calculated 238 // at the end instead of in a loop, the historic facts may contain 239 // inaccuracies about the active set. This should not be a problem except 240 // for large reorgs. 241 // TODO: improve this 242 currentBlock, exists := e.cs.BlockAtHeight(blockheight) 243 if !exists { 244 build.Critical("consensus is missing block", blockheight) 245 } 246 currentID := currentBlock.ID() 247 var facts blockFacts 248 err = dbGetAndDecode(bucketBlockFacts, currentID, &facts)(tx) 249 if err == nil { 250 for _, diff := range cc.FileContractDiffs { 251 if diff.Direction == modules.DiffApply { 252 facts.ActiveContractCount++ 253 facts.ActiveContractCost = facts.ActiveContractCost.Add(diff.FileContract.Payout) 254 facts.ActiveContractSize = facts.ActiveContractSize.Add(types.NewCurrency64(diff.FileContract.FileSize)) 255 } else { 256 facts.ActiveContractCount-- 257 facts.ActiveContractCost = facts.ActiveContractCost.Sub(diff.FileContract.Payout) 258 facts.ActiveContractSize = facts.ActiveContractSize.Sub(types.NewCurrency64(diff.FileContract.FileSize)) 259 } 260 } 261 err = tx.Bucket(bucketBlockFacts).Put(encoding.Marshal(currentID), encoding.Marshal(facts)) 262 if err != nil { 263 return err 264 } 265 } 266 267 // set final blockheight 268 err = dbSetInternal(internalBlockHeight, blockheight)(tx) 269 if err != nil { 270 return err 271 } 272 273 // set change ID 274 err = dbSetInternal(internalRecentChange, cc.ID)(tx) 275 if err != nil { 276 return err 277 } 278 279 return nil 280 }) 281 if err != nil { 282 build.Critical("explorer update failed:", err) 283 } 284 } 285 286 // helper functions 287 func assertNil(err error) { 288 if err != nil { 289 panic(err) 290 } 291 } 292 func mustPut(bucket *bolt.Bucket, key, val interface{}) { 293 assertNil(bucket.Put(encoding.Marshal(key), encoding.Marshal(val))) 294 } 295 func mustPutSet(bucket *bolt.Bucket, key interface{}) { 296 assertNil(bucket.Put(encoding.Marshal(key), nil)) 297 } 298 func mustDelete(bucket *bolt.Bucket, key interface{}) { 299 assertNil(bucket.Delete(encoding.Marshal(key))) 300 } 301 func bucketIsEmpty(bucket *bolt.Bucket) bool { 302 k, _ := bucket.Cursor().First() 303 return k == nil 304 } 305 306 // These functions panic on error. The panic will be caught by 307 // ProcessConsensusChange. 308 309 // Add/Remove block ID 310 func dbAddBlockID(tx *bolt.Tx, id types.BlockID, height types.BlockHeight) { 311 mustPut(tx.Bucket(bucketBlockIDs), id, height) 312 } 313 func dbRemoveBlockID(tx *bolt.Tx, id types.BlockID) { 314 mustDelete(tx.Bucket(bucketBlockIDs), id) 315 } 316 317 // Add/Remove block facts 318 func dbAddBlockFacts(tx *bolt.Tx, facts blockFacts) { 319 mustPut(tx.Bucket(bucketBlockFacts), facts.BlockID, facts) 320 } 321 func dbRemoveBlockFacts(tx *bolt.Tx, id types.BlockID) { 322 mustDelete(tx.Bucket(bucketBlockFacts), id) 323 } 324 325 // Add/Remove block target 326 func dbAddBlockTarget(tx *bolt.Tx, id types.BlockID, target types.Target) { 327 mustPut(tx.Bucket(bucketBlockTargets), id, target) 328 } 329 func dbRemoveBlockTarget(tx *bolt.Tx, id types.BlockID, target types.Target) { 330 mustDelete(tx.Bucket(bucketBlockTargets), id) 331 } 332 333 // Add/Remove file contract 334 func dbAddFileContract(tx *bolt.Tx, id types.FileContractID, fc types.FileContract) { 335 history := fileContractHistory{Contract: fc} 336 mustPut(tx.Bucket(bucketFileContractHistories), id, history) 337 } 338 func dbRemoveFileContract(tx *bolt.Tx, id types.FileContractID) { 339 mustDelete(tx.Bucket(bucketFileContractHistories), id) 340 } 341 342 // Add/Remove txid from file contract ID bucket 343 func dbAddFileContractID(tx *bolt.Tx, id types.FileContractID, txid types.TransactionID) { 344 b, err := tx.Bucket(bucketFileContractIDs).CreateBucketIfNotExists(encoding.Marshal(id)) 345 assertNil(err) 346 mustPutSet(b, txid) 347 } 348 func dbRemoveFileContractID(tx *bolt.Tx, id types.FileContractID, txid types.TransactionID) { 349 bucket := tx.Bucket(bucketFileContractIDs).Bucket(encoding.Marshal(id)) 350 mustDelete(bucket, txid) 351 if bucketIsEmpty(bucket) { 352 tx.Bucket(bucketFileContractIDs).DeleteBucket(encoding.Marshal(id)) 353 } 354 } 355 356 func dbAddFileContractRevision(tx *bolt.Tx, fcid types.FileContractID, fcr types.FileContractRevision) { 357 var history fileContractHistory 358 assertNil(dbGetAndDecode(bucketFileContractHistories, fcid, &history)(tx)) 359 history.Revisions = append(history.Revisions, fcr) 360 mustPut(tx.Bucket(bucketFileContractHistories), fcid, history) 361 } 362 func dbRemoveFileContractRevision(tx *bolt.Tx, fcid types.FileContractID) { 363 var history fileContractHistory 364 assertNil(dbGetAndDecode(bucketFileContractHistories, fcid, &history)(tx)) 365 // TODO: could be more rigorous 366 history.Revisions = history.Revisions[:len(history.Revisions)-1] 367 mustPut(tx.Bucket(bucketFileContractHistories), fcid, history) 368 } 369 370 // Add/Remove siacoin output 371 func dbAddSiacoinOutput(tx *bolt.Tx, id types.SiacoinOutputID, output types.SiacoinOutput) { 372 mustPut(tx.Bucket(bucketSiacoinOutputs), id, output) 373 } 374 func dbRemoveSiacoinOutput(tx *bolt.Tx, id types.SiacoinOutputID) { 375 mustDelete(tx.Bucket(bucketSiacoinOutputs), id) 376 } 377 378 // Add/Remove txid from siacoin output ID bucket 379 func dbAddSiacoinOutputID(tx *bolt.Tx, id types.SiacoinOutputID, txid types.TransactionID) { 380 b, err := tx.Bucket(bucketSiacoinOutputIDs).CreateBucketIfNotExists(encoding.Marshal(id)) 381 assertNil(err) 382 mustPutSet(b, txid) 383 } 384 func dbRemoveSiacoinOutputID(tx *bolt.Tx, id types.SiacoinOutputID, txid types.TransactionID) { 385 bucket := tx.Bucket(bucketSiacoinOutputIDs).Bucket(encoding.Marshal(id)) 386 mustDelete(bucket, txid) 387 if bucketIsEmpty(bucket) { 388 tx.Bucket(bucketSiacoinOutputIDs).DeleteBucket(encoding.Marshal(id)) 389 } 390 } 391 392 // Add/Remove siafund output 393 func dbAddSiafundOutput(tx *bolt.Tx, id types.SiafundOutputID, output types.SiafundOutput) { 394 mustPut(tx.Bucket(bucketSiafundOutputs), id, output) 395 } 396 func dbRemoveSiafundOutput(tx *bolt.Tx, id types.SiafundOutputID) { 397 mustDelete(tx.Bucket(bucketSiafundOutputs), id) 398 } 399 400 // Add/Remove txid from siafund output ID bucket 401 func dbAddSiafundOutputID(tx *bolt.Tx, id types.SiafundOutputID, txid types.TransactionID) { 402 b, err := tx.Bucket(bucketSiafundOutputIDs).CreateBucketIfNotExists(encoding.Marshal(id)) 403 assertNil(err) 404 mustPutSet(b, txid) 405 } 406 func dbRemoveSiafundOutputID(tx *bolt.Tx, id types.SiafundOutputID, txid types.TransactionID) { 407 bucket := tx.Bucket(bucketSiafundOutputIDs).Bucket(encoding.Marshal(id)) 408 mustDelete(bucket, txid) 409 if bucketIsEmpty(bucket) { 410 tx.Bucket(bucketSiafundOutputIDs).DeleteBucket(encoding.Marshal(id)) 411 } 412 } 413 414 // Add/Remove storage proof 415 func dbAddStorageProof(tx *bolt.Tx, fcid types.FileContractID, sp types.StorageProof) { 416 var history fileContractHistory 417 assertNil(dbGetAndDecode(bucketFileContractHistories, fcid, &history)(tx)) 418 history.StorageProof = sp 419 mustPut(tx.Bucket(bucketFileContractHistories), fcid, history) 420 } 421 func dbRemoveStorageProof(tx *bolt.Tx, fcid types.FileContractID) { 422 dbAddStorageProof(tx, fcid, types.StorageProof{}) 423 } 424 425 // Add/Remove transaction ID 426 func dbAddTransactionID(tx *bolt.Tx, id types.TransactionID, height types.BlockHeight) { 427 mustPut(tx.Bucket(bucketTransactionIDs), id, height) 428 } 429 func dbRemoveTransactionID(tx *bolt.Tx, id types.TransactionID) { 430 mustDelete(tx.Bucket(bucketTransactionIDs), id) 431 } 432 433 // Add/Remove txid from unlock hash bucket 434 func dbAddUnlockHash(tx *bolt.Tx, uh types.UnlockHash, txid types.TransactionID) { 435 b, err := tx.Bucket(bucketUnlockHashes).CreateBucketIfNotExists(encoding.Marshal(uh)) 436 assertNil(err) 437 mustPutSet(b, txid) 438 } 439 func dbRemoveUnlockHash(tx *bolt.Tx, uh types.UnlockHash, txid types.TransactionID) { 440 bucket := tx.Bucket(bucketUnlockHashes).Bucket(encoding.Marshal(uh)) 441 mustDelete(bucket, txid) 442 if bucketIsEmpty(bucket) { 443 tx.Bucket(bucketUnlockHashes).DeleteBucket(encoding.Marshal(uh)) 444 } 445 } 446 447 func dbCalculateBlockFacts(tx *bolt.Tx, cs modules.ConsensusSet, block types.Block) blockFacts { 448 // get the parent block facts 449 var bf blockFacts 450 err := dbGetAndDecode(bucketBlockFacts, block.ParentID, &bf)(tx) 451 assertNil(err) 452 453 // get target 454 target, exists := cs.ChildTarget(block.ParentID) 455 if !exists { 456 panic(fmt.Sprint("ConsensusSet is missing target of known block", block.ParentID)) 457 } 458 459 // update fields 460 bf.BlockID = block.ID() 461 bf.Height++ 462 bf.Difficulty = target.Difficulty() 463 bf.Target = target 464 bf.Timestamp = block.Timestamp 465 bf.TotalCoins = types.CalculateNumSiacoins(bf.Height) 466 467 // calculate maturity timestamp 468 var maturityTimestamp types.Timestamp 469 if bf.Height > types.MaturityDelay { 470 oldBlock, exists := cs.BlockAtHeight(bf.Height - types.MaturityDelay) 471 if !exists { 472 panic(fmt.Sprint("ConsensusSet is missing block at height", bf.Height-types.MaturityDelay)) 473 } 474 maturityTimestamp = oldBlock.Timestamp 475 } 476 bf.MaturityTimestamp = maturityTimestamp 477 478 // calculate hashrate by averaging last 'hashrateEstimationBlocks' blocks 479 var estimatedHashrate types.Currency 480 if bf.Height > hashrateEstimationBlocks { 481 var totalDifficulty = bf.Target 482 var oldestTimestamp types.Timestamp 483 for i := types.BlockHeight(1); i < hashrateEstimationBlocks; i++ { 484 b, exists := cs.BlockAtHeight(bf.Height - i) 485 if !exists { 486 panic(fmt.Sprint("ConsensusSet is missing block at height", bf.Height-hashrateEstimationBlocks)) 487 } 488 target, exists := cs.ChildTarget(b.ParentID) 489 if !exists { 490 panic(fmt.Sprint("ConsensusSet is missing target of known block", b.ParentID)) 491 } 492 totalDifficulty = totalDifficulty.AddDifficulties(target) 493 oldestTimestamp = b.Timestamp 494 } 495 secondsPassed := bf.Timestamp - oldestTimestamp 496 estimatedHashrate = totalDifficulty.Difficulty().Div64(uint64(secondsPassed)) 497 } 498 bf.EstimatedHashrate = estimatedHashrate 499 500 bf.MinerPayoutCount += uint64(len(block.MinerPayouts)) 501 bf.TransactionCount += uint64(len(block.Transactions)) 502 for _, txn := range block.Transactions { 503 bf.SiacoinInputCount += uint64(len(txn.SiacoinInputs)) 504 bf.SiacoinOutputCount += uint64(len(txn.SiacoinOutputs)) 505 bf.FileContractCount += uint64(len(txn.FileContracts)) 506 bf.FileContractRevisionCount += uint64(len(txn.FileContractRevisions)) 507 bf.StorageProofCount += uint64(len(txn.StorageProofs)) 508 bf.SiafundInputCount += uint64(len(txn.SiafundInputs)) 509 bf.SiafundOutputCount += uint64(len(txn.SiafundOutputs)) 510 bf.MinerFeeCount += uint64(len(txn.MinerFees)) 511 bf.ArbitraryDataCount += uint64(len(txn.ArbitraryData)) 512 bf.TransactionSignatureCount += uint64(len(txn.TransactionSignatures)) 513 514 for _, fc := range txn.FileContracts { 515 bf.TotalContractCost = bf.TotalContractCost.Add(fc.Payout) 516 bf.TotalContractSize = bf.TotalContractSize.Add(types.NewCurrency64(fc.FileSize)) 517 } 518 for _, fcr := range txn.FileContractRevisions { 519 bf.TotalContractSize = bf.TotalContractSize.Add(types.NewCurrency64(fcr.NewFileSize)) 520 bf.TotalRevisionVolume = bf.TotalRevisionVolume.Add(types.NewCurrency64(fcr.NewFileSize)) 521 } 522 } 523 524 return bf 525 } 526 527 // Special handling for the genesis block. No other functions are called on it. 528 func dbAddGenesisBlock(tx *bolt.Tx) { 529 id := types.GenesisID 530 dbAddBlockID(tx, id, 0) 531 532 txid := types.GenesisBlock.Transactions[0].ID() 533 dbAddTransactionID(tx, txid, 0) 534 for i, sco := range types.GenesisAirdropAllocation { 535 scoid := types.GenesisBlock.Transactions[0].SiacoinOutputID(uint64(i)) 536 dbAddSiacoinOutputID(tx, scoid, txid) 537 dbAddUnlockHash(tx, sco.UnlockHash, txid) 538 dbAddSiacoinOutput(tx, scoid, sco) 539 } 540 541 txid = types.GenesisBlock.Transactions[1].ID() 542 dbAddTransactionID(tx, txid, 0) 543 for i, sfo := range types.GenesisSiafundAllocation { 544 sfoid := types.GenesisBlock.Transactions[1].SiafundOutputID(uint64(i)) 545 dbAddSiafundOutputID(tx, sfoid, txid) 546 dbAddUnlockHash(tx, sfo.UnlockHash, txid) 547 dbAddSiafundOutput(tx, sfoid, sfo) 548 } 549 550 dbAddBlockFacts(tx, blockFacts{ 551 BlockFacts: modules.BlockFacts{ 552 BlockID: id, 553 Height: 0, 554 Difficulty: types.RootTarget.Difficulty(), 555 Target: types.RootTarget, 556 TotalCoins: types.CalculateCoinbase(0), 557 TransactionCount: uint64(len(types.GenesisBlock.Transactions)), 558 SiacoinOutputCount: uint64(len(types.GenesisAirdropAllocation)), 559 SiafundOutputCount: uint64(len(types.GenesisSiafundAllocation)), 560 }, 561 Timestamp: types.GenesisBlock.Timestamp, 562 }) 563 }