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  }