From 390ab3ed5edca89e9ebbce5e2571f825f14ef00a Mon Sep 17 00:00:00 2001 From: mike76-dev Date: Fri, 26 Apr 2024 08:42:35 +0200 Subject: [PATCH] Fix merging errors --- modules/consensus/applytransaction.go | 385 -------------- modules/consensus/consistency.go | 185 ------- modules/consensus/database.go | 718 -------------------------- modules/consensus/maintenance.go | 259 ---------- modules/portal/dashboard.go | 4 +- modules/syncer/store.go | 4 +- modules/transactionpool/database.go | 169 ------ modules/wallet/unseeded.go | 96 ---- 8 files changed, 4 insertions(+), 1816 deletions(-) delete mode 100644 modules/consensus/applytransaction.go delete mode 100644 modules/consensus/consistency.go delete mode 100644 modules/consensus/database.go delete mode 100644 modules/consensus/maintenance.go delete mode 100644 modules/transactionpool/database.go delete mode 100644 modules/wallet/unseeded.go diff --git a/modules/consensus/applytransaction.go b/modules/consensus/applytransaction.go deleted file mode 100644 index ed0146c..0000000 --- a/modules/consensus/applytransaction.go +++ /dev/null @@ -1,385 +0,0 @@ -package consensus - -import ( - "bytes" - "database/sql" - "errors" - - "github.com/mike76-dev/sia-satellite/modules" - - "go.sia.tech/core/types" -) - -// applySiacoinInputs takes all of the siacoin inputs in a transaction and -// applies them to the state, updating the diffs in the processed block. -func applySiacoinInputs(tx *sql.Tx, pb *processedBlock, t types.Transaction) error { - // Remove all siacoin inputs from the unspent siacoin outputs list. - for _, sci := range t.SiacoinInputs { - sco, exists, err := findSiacoinOutput(tx, sci.ParentID) - if err != nil { - return err - } - if !exists { - return errors.New("no output found for the Siacoin input") - } - scod := modules.SiacoinOutputDiff{ - Direction: modules.DiffRevert, - ID: sci.ParentID, - SiacoinOutput: sco, - } - pb.SiacoinOutputDiffs = append(pb.SiacoinOutputDiffs, scod) - err = commitSiacoinOutputDiff(tx, scod, modules.DiffApply) - if err != nil { - return err - } - } - return nil -} - -// applySiacoinOutputs takes all of the siacoin outputs in a transaction and -// applies them to the state, updating the diffs in the processed block. -func applySiacoinOutputs(tx *sql.Tx, pb *processedBlock, t types.Transaction) error { - // Add all siacoin outputs to the unspent siacoin outputs list. - for i, sco := range t.SiacoinOutputs { - scoid := t.SiacoinOutputID(i) - scod := modules.SiacoinOutputDiff{ - Direction: modules.DiffApply, - ID: scoid, - SiacoinOutput: sco, - } - pb.SiacoinOutputDiffs = append(pb.SiacoinOutputDiffs, scod) - err := commitSiacoinOutputDiff(tx, scod, modules.DiffApply) - if err != nil { - return err - } - } - return nil -} - -// applyFileContracts iterates through all of the file contracts in a -// transaction and applies them to the state, updating the diffs in the proccesed -// block. -func applyFileContracts(tx *sql.Tx, pb *processedBlock, t types.Transaction) error { - for i, fc := range t.FileContracts { - fcid := t.FileContractID(i) - fcd := modules.FileContractDiff{ - Direction: modules.DiffApply, - ID: fcid, - FileContract: fc, - } - pb.FileContractDiffs = append(pb.FileContractDiffs, fcd) - err := commitFileContractDiff(tx, fcd, modules.DiffApply) - if err != nil { - return err - } - - // Get the portion of the contract that goes into the siafund pool and - // add it to the siafund pool. - sfp := getSiafundPool(tx) - sfpd := modules.SiafundPoolDiff{ - Direction: modules.DiffApply, - Previous: sfp, - Adjusted: sfp.Add(modules.Tax(blockHeight(tx), fc.Payout)), - } - pb.SiafundPoolDiffs = append(pb.SiafundPoolDiffs, sfpd) - err = commitSiafundPoolDiff(tx, sfpd, modules.DiffApply) - if err != nil { - return err - } - } - - return nil -} - -// applyFileContractRevisions iterates through all of the file contract -// revisions in a transaction and applies them to the state, updating the diffs -// in the processed block. -func applyFileContractRevisions(tx *sql.Tx, pb *processedBlock, t types.Transaction) error { - for _, fcr := range t.FileContractRevisions { - fc, exists, err := findFileContract(tx, fcr.ParentID) - if err != nil { - return err - } - if !exists { - return errors.New("no file contract found for the revision") - } - - // Add the diff to delete the old file contract. - fcd := modules.FileContractDiff{ - Direction: modules.DiffRevert, - ID: fcr.ParentID, - FileContract: fc, - } - pb.FileContractDiffs = append(pb.FileContractDiffs, fcd) - err = commitFileContractDiff(tx, fcd, modules.DiffApply) - if err != nil { - return err - } - - // Add the diff to add the revised file contract. - newFC := types.FileContract{ - Filesize: fcr.FileContract.Filesize, - FileMerkleRoot: fcr.FileContract.FileMerkleRoot, - WindowStart: fcr.FileContract.WindowStart, - WindowEnd: fcr.FileContract.WindowEnd, - Payout: fc.Payout, - ValidProofOutputs: fcr.FileContract.ValidProofOutputs, - MissedProofOutputs: fcr.FileContract.MissedProofOutputs, - UnlockHash: fcr.FileContract.UnlockHash, - RevisionNumber: fcr.FileContract.RevisionNumber, - } - fcd = modules.FileContractDiff{ - Direction: modules.DiffApply, - ID: fcr.ParentID, - FileContract: newFC, - } - pb.FileContractDiffs = append(pb.FileContractDiffs, fcd) - err = commitFileContractDiff(tx, fcd, modules.DiffApply) - if err != nil { - return err - } - } - - return nil -} - -// applyStorageProofs iterates through all of the storage proofs in a -// transaction and applies them to the state, updating the diffs in the processed -// block. -func applyStorageProofs(tx *sql.Tx, pb *processedBlock, t types.Transaction) error { - for _, sp := range t.StorageProofs { - fc, exists, err := findFileContract(tx, sp.ParentID) - if err != nil { - return err - } - if !exists { - return errors.New("no file contract found for the storage proof") - } - - // Add all of the outputs in the ValidProofOutputs of the contract. - for i, vpo := range fc.ValidProofOutputs { - spoid := modules.StorageProofOutputID(sp.ParentID, true, i) - dscod := modules.DelayedSiacoinOutputDiff{ - Direction: modules.DiffApply, - ID: spoid, - SiacoinOutput: vpo, - MaturityHeight: pb.Height + modules.MaturityDelay, - } - pb.DelayedSiacoinOutputDiffs = append(pb.DelayedSiacoinOutputDiffs, dscod) - err := commitDelayedSiacoinOutputDiff(tx, dscod, modules.DiffApply) - if err != nil { - return err - } - } - - fcd := modules.FileContractDiff{ - Direction: modules.DiffRevert, - ID: sp.ParentID, - FileContract: fc, - } - pb.FileContractDiffs = append(pb.FileContractDiffs, fcd) - err = commitFileContractDiff(tx, fcd, modules.DiffApply) - if err != nil { - return err - } - } - - return nil -} - -// applySiafundInputs takes all of the siafund inputs in a transaction and -// applies them to the state, updating the diffs in the processed block. -func applySiafundInputs(tx *sql.Tx, pb *processedBlock, t types.Transaction) error { - for _, sfi := range t.SiafundInputs { - // Calculate the volume of siacoins to put in the claim output. - sfo, claimStart, exists, err := findSiafundOutput(tx, sfi.ParentID) - if err != nil { - return err - } - if !exists { - return errors.New("no output found for the Siafund input") - } - claimPortion := getSiafundPool(tx).Sub(claimStart).Div64(modules.SiafundCount).Mul64(sfo.Value) - - // Add the claim output to the delayed set of outputs. - sco := types.SiacoinOutput{ - Value: claimPortion, - Address: sfi.ClaimAddress, - } - sfoid := sfi.ParentID.ClaimOutputID() - dscod := modules.DelayedSiacoinOutputDiff{ - Direction: modules.DiffApply, - ID: sfoid, - SiacoinOutput: sco, - MaturityHeight: pb.Height + modules.MaturityDelay, - } - pb.DelayedSiacoinOutputDiffs = append(pb.DelayedSiacoinOutputDiffs, dscod) - err = commitDelayedSiacoinOutputDiff(tx, dscod, modules.DiffApply) - if err != nil { - return err - } - - // Create the siafund output diff and remove the output from the - // consensus set. - sfod := modules.SiafundOutputDiff{ - Direction: modules.DiffRevert, - ID: sfi.ParentID, - SiafundOutput: sfo, - ClaimStart: claimStart, - } - pb.SiafundOutputDiffs = append(pb.SiafundOutputDiffs, sfod) - err = commitSiafundOutputDiff(tx, sfod, modules.DiffApply) - if err != nil { - return err - } - } - - return nil -} - -// applySiafundOutputs applies a siafund output to the consensus set. -func applySiafundOutputs(tx *sql.Tx, pb *processedBlock, t types.Transaction) error { - for i, sfo := range t.SiafundOutputs { - sfoid := t.SiafundOutputID(i) - sfod := modules.SiafundOutputDiff{ - Direction: modules.DiffApply, - ID: sfoid, - SiafundOutput: sfo, - ClaimStart: getSiafundPool(tx), - } - pb.SiafundOutputDiffs = append(pb.SiafundOutputDiffs, sfod) - err := commitSiafundOutputDiff(tx, sfod, modules.DiffApply) - if err != nil { - return err - } - } - - return nil -} - -// applyArbitraryData applies arbitrary data to the consensus set. ArbitraryData -// is a field of the Transaction type whose structure is not fixed. This means -// that, via hardfork, new types of transaction can be introduced with minimal -// breakage by updating consensus code to recognize and act upon values encoded -// within the ArbitraryData field. -// -// Accordingly, this function dispatches on the various ArbitraryData values -// that are recognized by consensus. Currently, types.FoundationUnlockHashUpdate -// is the only recognized value. -func applyArbitraryData(tx *sql.Tx, pb *processedBlock, t types.Transaction) error { - // No ArbitraryData values were recognized prior to the Foundation hardfork. - if pb.Height < modules.FoundationHardforkHeight { - return nil - } - for _, arb := range t.ArbitraryData { - if bytes.HasPrefix(arb, types.SpecifierFoundation[:]) { - var update types.FoundationAddressUpdate - d := types.NewBufDecoder(arb[16:]) - update.DecodeFrom(d) - if err := d.Err(); err != nil { - return err - } - - // Apply the update. First, save a copy of the old (i.e. current) - // unlock hashes, so that we can revert later. Then set the new - // unlock hashes. - // - // Importantly, we must only do this once per block; otherwise, for - // complicated reasons involving diffs, we would not be able to - // revert updates safely. So if we see that a copy has already been - // recorded, we simply ignore the update; i.e. only the first update - // in a block will be applied. - _, _, exists, err := getPriorFoundationUnlockHashes(tx, pb.Height) - if err != nil { - return err - } - if exists { - continue - } - err = setPriorFoundationUnlockHashes(tx, pb.Height) - if err != nil { - return err - } - err = setFoundationUnlockHashes(tx, update.NewPrimary, update.NewFailsafe) - if err != nil { - return err - } - err = transferFoundationOutputs(tx, pb.Height, update.NewPrimary) - if err != nil { - return err - } - } - } - - return nil -} - -// transferFoundationOutputs transfers all unspent subsidy outputs to -// newPrimary. This allows subsidies to be recovered in the event that the -// primary key is lost or unusable when a subsidy is created. -func transferFoundationOutputs(tx *sql.Tx, currentHeight uint64, newPrimary types.Address) error { - for height := modules.FoundationHardforkHeight; height < currentHeight; height += modules.FoundationSubsidyFrequency { - blockID, err := getBlockAtHeight(tx, height) - if err != nil { - continue - } - id := blockID.FoundationOutputID() - sco, exists, err := findSiacoinOutput(tx, id) - if err != nil { - return err - } - if !exists { - continue // Output has already been spent. - } - sco.Address = newPrimary - err = removeSiacoinOutput(tx, id) - if err != nil { - return err - } - err = addSiacoinOutput(tx, id, sco) - if err != nil { - return err - } - } - - return nil -} - -// applyTransaction applies the contents of a transaction to the ConsensusSet. -// This produces a set of diffs, which are stored in the blockNode containing -// the transaction. No verification is done by this function. -func applyTransaction(tx *sql.Tx, pb *processedBlock, t types.Transaction) error { - err := applySiacoinInputs(tx, pb, t) - if err != nil { - return err - } - err = applySiacoinOutputs(tx, pb, t) - if err != nil { - return err - } - err = applyFileContracts(tx, pb, t) - if err != nil { - return err - } - err = applyFileContractRevisions(tx, pb, t) - if err != nil { - return err - } - err = applyStorageProofs(tx, pb, t) - if err != nil { - return err - } - err = applySiafundInputs(tx, pb, t) - if err != nil { - return err - } - err = applySiafundOutputs(tx, pb, t) - if err != nil { - return err - } - err = applyArbitraryData(tx, pb, t) - if err != nil { - return err - } - return nil -} diff --git a/modules/consensus/consistency.go b/modules/consensus/consistency.go deleted file mode 100644 index 0762600..0000000 --- a/modules/consensus/consistency.go +++ /dev/null @@ -1,185 +0,0 @@ -package consensus - -import ( - "database/sql" - "errors" - "fmt" - - "github.com/mike76-dev/sia-satellite/modules" - - "go.sia.tech/core/types" - - "lukechampine.com/frand" -) - -// manageErr handles an error detected by the consistency checks. -func manageErr(tx *sql.Tx, err error) { - markInconsistency(tx) - fmt.Println(err) -} - -// checkSiacoinCount checks that the number of siacoins countable within the -// consensus set equal the expected number of siacoins for the block height. -func checkSiacoinCount(tx *sql.Tx) { - // Add all of the delayed Siacoin outputs. - dscoSiacoins, err := countDelayedSiacoins(tx) - if err != nil { - manageErr(tx, err) - } - - // Add all of the Siacoin outputs. - scoSiacoins, err := countSiacoins(tx) - if err != nil { - manageErr(tx, err) - } - - // Add all of the payouts from file contracts. - fcSiacoins, err := countFileContractPayouts(tx) - if err != nil { - manageErr(tx, err) - } - - // Add all of the siafund claims. - claimSiacoins, err := countSiafundClaims(tx) - if err != nil { - manageErr(tx, err) - } - - expectedSiacoins := modules.CalculateNumSiacoins(blockHeight(tx)) - totalSiacoins := dscoSiacoins.Add(scoSiacoins).Add(fcSiacoins).Add(claimSiacoins) - if !totalSiacoins.Equals(expectedSiacoins) { - diagnostics := fmt.Sprintf("Number of Siacoins\nDsco: %v\nSco: %v\nFc: %v\nClaim: %v\n", dscoSiacoins, scoSiacoins, fcSiacoins, claimSiacoins) - if totalSiacoins.Cmp(expectedSiacoins) < 0 { - diagnostics += fmt.Sprintf("total: %v\nexpected: %v\n expected is bigger: %v", totalSiacoins, expectedSiacoins, expectedSiacoins.Sub(totalSiacoins)) - } else { - diagnostics += fmt.Sprintf("total: %v\nexpected: %v\n expected is smaller: %v", totalSiacoins, expectedSiacoins, totalSiacoins.Sub(expectedSiacoins)) - } - manageErr(tx, errors.New(diagnostics)) - } -} - -// checkSiafundCount checks that the number of siafunds countable within the -// consensus set equal the expected number of siafunds for the block height. -func checkSiafundCount(tx *sql.Tx) { - total, err := countSiafunds(tx) - if err != nil { - manageErr(tx, err) - } - if total != modules.SiafundCount { - manageErr(tx, errors.New("wrong number of siafunds in the consensus set")) - } -} - -// checkDSCOs scans the sets of delayed siacoin outputs and checks for -// consistency. -func checkDSCOs(tx *sql.Tx) { - // Create a map to track which delayed siacoin output maps exist, - // a map to track which ids have appeared in the dsco set, and another - // map to count the values. - dscoTracker := make(map[uint64]struct{}) - idMap := make(map[types.SiacoinOutputID]struct{}) - total := make(map[uint64]types.Currency) - - // Iterate through all the delayed siacoin outputs, and check that they - // are for the correct heights. - rows, err := tx.Query("SELECT height, scoid, bytes FROM cs_dsco") - if err != nil { - manageErr(tx, err) - return - } - - for rows.Next() { - var height uint64 - var scoid types.SiacoinOutputID - id := make([]byte, 32) - var scoBytes []byte - if err := rows.Scan(&height, &id, &scoBytes); err != nil { - rows.Close() - manageErr(tx, err) - return - } - - dscoTracker[height] = struct{}{} - - // Check that the output id has not appeared in another dsco. - copy(scoid[:], id[:]) - _, exists := idMap[scoid] - if exists { - rows.Close() - manageErr(tx, errors.New("repeat delayed siacoin output")) - return - } - idMap[scoid] = struct{}{} - - // Sum the funds. - var sco types.SiacoinOutput - d := types.NewBufDecoder(scoBytes) - sco.DecodeFrom(d) - if err := d.Err(); err != nil { - rows.Close() - manageErr(tx, err) - return - } - if t, exists := total[height]; exists { - total[height] = t.Add(sco.Value) - } else { - total[height] = sco.Value - } - } - rows.Close() - - // Check that the minimum value has been achieved - the coinbase from - // an earlier block is guaranteed to be in the bucket. - for height, value := range total { - minimumValue := modules.CalculateCoinbase(height - modules.MaturityDelay) - if value.Cmp(minimumValue) < 0 { - manageErr(tx, errors.New("total number of coins in the delayed output bucket is incorrect")) - return - } - } - - // Check that all of the correct heights are represented. - currentHeight := blockHeight(tx) - expectedBuckets := 0 - for i := currentHeight + 1; i <= currentHeight+modules.MaturityDelay; i++ { - if i < modules.MaturityDelay { - continue - } - _, exists := dscoTracker[i] - if !exists { - manageErr(tx, errors.New("missing a dsco bucket")) - return - } - expectedBuckets++ - } - if len(dscoTracker) != expectedBuckets { - manageErr(tx, errors.New("too many dsco buckets")) - } -} - -// checkConsistency runs a series of checks to make sure that the consensus set -// is consistent with some rules that should always be true. -func (cs *ConsensusSet) checkConsistency(tx *sql.Tx) { - if cs.checkingConsistency { - return - } - - cs.checkingConsistency = true - checkDSCOs(tx) - checkSiacoinCount(tx) - checkSiafundCount(tx) - cs.checkingConsistency = false -} - -// maybeCheckConsistency runs a consistency check with a small probability. -// Useful for detecting database corruption in production without needing to go -// through the extremely slow process of running a consistency check every -// block. -func (cs *ConsensusSet) maybeCheckConsistency(tx *sql.Tx) { - if frand.Intn(1000) == 0 { - cs.checkConsistency(tx) - } -} - -// TODO: Check that every file contract has an expiration too, and that the -// number of file contracts + the number of expirations is equal. diff --git a/modules/consensus/database.go b/modules/consensus/database.go deleted file mode 100644 index 02591a5..0000000 --- a/modules/consensus/database.go +++ /dev/null @@ -1,718 +0,0 @@ -package consensus - -import ( - "bytes" - "database/sql" - "encoding/binary" - "errors" - "time" - - "github.com/mike76-dev/sia-satellite/modules" - - "go.sia.tech/core/types" -) - -// The address of the devs. -var ( - oldDevAddr = types.Address{125, 12, 68, 247, 102, 78, 45, 52, 229, 62, 253, 224, 102, 26, 111, 98, 142, 201, 38, 71, 133, 174, 142, 60, 215, 201, 115, 232, 209, 144, 195, 201} - newDevAddr = types.Address{243, 113, 199, 11, 206, 158, 184, 151, 156, 213, 9, 159, 89, 158, 196, 228, 252, 177, 78, 10, 252, 243, 31, 151, 145, 224, 62, 100, 150, 164, 192, 179} -) - -// errRepeatInsert is used when a duplicate field is found in the database. -var errRepeatInsert = errors.New("attempting to add an already existing item to the consensus set") - -// markInconsistency flags the database to indicate that inconsistency has been -// detected. -func markInconsistency(tx *sql.Tx) error { - // Place a 'true' in the consistency bucket to indicate that - // inconsistencies have been found. - _, err := tx.Exec("REPLACE INTO cs_consistency (id, inconsistency) VALUES (1, TRUE)") - if err != nil { - return err - } - - return nil -} - -// initDB creates a database with sane initial values. -func (cs *ConsensusSet) initDB(tx *sql.Tx) error { - // If the database has already been initialized, there is nothing to do. - // Initialization can be detected by looking for the presence of the Siafund - // pool bucket. (legacy design choice - ultimately probably not the best way - // to tell). - var count int - err := tx.QueryRow("SELECT COUNT(*) FROM cs_sfpool").Scan(&count) - if err != nil { - return err - } - if count > 0 { - return nil - } - - // Create the components of the database. - err = cs.createConsensusDB(tx) - if err != nil { - return err - } - err = cs.createChangeLog(tx) - if err != nil { - return err - } - - // Place a 'false' in the consistency bucket to indicate that no - // inconsistencies have been found. - _, err = tx.Exec("REPLACE INTO cs_consistency (id, inconsistency) VALUES (1, FALSE)") - if err != nil { - return err - } - - // Init Oak field. - _, err = tx.Exec("REPLACE INTO cs_oak_init (id, init) VALUES (1, FALSE)") - if err != nil { - return err - } - - return nil -} - -// createConsensusDB initializes the consensus portions of the database. -func (cs *ConsensusSet) createConsensusDB(tx *sql.Tx) error { - // Update the Siacoin output diffs map for the genesis block on disk. This - // needs to happen between the database being opened/initialized and the - // consensus set hash being calculated. - for _, scod := range cs.blockRoot.SiacoinOutputDiffs { - err := commitSiacoinOutputDiff(tx, scod, modules.DiffApply) - if err != nil { - return err - } - } - - // Set the Siafund pool to 0. - var buf bytes.Buffer - e := types.NewEncoder(&buf) - types.NewCurrency64(0).EncodeTo(e) - e.Flush() - _, err := tx.Exec("REPLACE INTO cs_sfpool (id, bytes) VALUES (1, ?)", buf.Bytes()) - if err != nil { - return err - } - - // Update the Siafund output diffs map for the genesis block on disk. This - // needs to happen between the database being opened/initialized and the - // consensus set hash being calculated. - for _, sfod := range cs.blockRoot.SiafundOutputDiffs { - err := commitSiafundOutputDiff(tx, sfod, modules.DiffApply) - if err != nil { - return err - } - } - - // Add the miner payout from the genesis block to the delayed siacoin - // outputs - unspendable, as the unlock hash is blank. - err = addDSCO(tx, modules.MaturityDelay, cs.blockRoot.Block.ID().MinerOutputID(0), types.SiacoinOutput{ - Value: modules.CalculateCoinbase(0), - Address: types.Address{}, - }) - if err != nil { - return err - } - - // Add the genesis block to the block structures. - err = pushPath(tx, cs.blockRoot.Block.ID()) - if err != nil { - return err - } - - return addBlock(tx, &cs.blockRoot) -} - -// blockHeight returns the height of the blockchain. -func blockHeight(tx *sql.Tx) uint64 { - var height uint64 - err := tx.QueryRow("SELECT height FROM cs_height WHERE id = 1").Scan(&height) - if err != nil { - return 0 - } - return height -} - -// currentBlockID returns the id of the most recent block in the consensus set. -func currentBlockID(tx *sql.Tx) types.BlockID { - id, err := getBlockAtHeight(tx, blockHeight(tx)) - if err != nil { - return types.BlockID{} - } - return id -} - -// currentProcessedBlock retrieves the most recent processed block. -func currentProcessedBlock(tx *sql.Tx) *processedBlock { - pb, exists, err := findBlockByID(tx, currentBlockID(tx)) - if err != nil || !exists { - return nil - } - return pb -} - -// findBlockByID tries to find a block with the given ID in the consensus set. -func findBlockByID(tx *sql.Tx, id types.BlockID) (*processedBlock, bool, error) { - var pbBytes []byte - err := tx.QueryRow("SELECT bytes FROM cs_map WHERE bid = ?", id[:]).Scan(&pbBytes) - if errors.Is(err, sql.ErrNoRows) { - return nil, false, nil - } - if err != nil { - return nil, false, err - } - pb := new(processedBlock) - d := types.NewBufDecoder(pbBytes) - pb.DecodeFrom(d) - return pb, true, d.Err() -} - -// getParentID is a convenience method for retrieving only some fields -// from the block. -func getParentID(tx *sql.Tx, id types.BlockID) (parentID types.BlockID, timestamp time.Time, err error) { - var pbBytes []byte - err = tx.QueryRow("SELECT bytes FROM cs_map WHERE bid = ?", id[:]).Scan(&pbBytes) - if err != nil { - return - } - copy(parentID[:], pbBytes[:32]) - ts := binary.LittleEndian.Uint64(pbBytes[40:48]) - return parentID, time.Unix(int64(ts), 0), nil -} - -// addBlock adds the processed block to the block map. -func addBlock(tx *sql.Tx, pb *processedBlock) error { - id := pb.Block.ID() - return saveBlock(tx, id, pb) -} - -// saveBlock adds the processed block under the given ID. -func saveBlock(tx *sql.Tx, id types.BlockID, pb *processedBlock) error { - var buf bytes.Buffer - e := types.NewEncoder(&buf) - pb.EncodeTo(e) - e.Flush() - _, err := tx.Exec(` - INSERT INTO cs_map (bid, bytes) VALUES (?, ?) AS new - ON DUPLICATE KEY UPDATE bytes = new.bytes - `, id[:], buf.Bytes()) - return err -} - -// getBlockAtHeight retrieves the ID of the block at the given height. -func getBlockAtHeight(tx *sql.Tx, height uint64) (bid types.BlockID, err error) { - id := make([]byte, 32) - err = tx.QueryRow("SELECT bid FROM cs_path WHERE height = ?", height).Scan(&id) - if err != nil { - return types.BlockID{}, err - } - - copy(bid[:], id[:]) - return -} - -// pushPath adds a block to the block path at current height + 1. -func pushPath(tx *sql.Tx, bid types.BlockID) error { - // Update the block height. - _, err := tx.Exec(` - INSERT INTO cs_height (id, height) VALUES (1, 0) - ON DUPLICATE KEY UPDATE height = height + 1 - `) - if err != nil { - return err - } - - // Add the block to the block path. - _, err = tx.Exec(` - INSERT INTO cs_path (height, bid) VALUES ( - (SELECT height FROM cs_height WHERE id = 1), ?) - `, bid[:]) - - return err -} - -// popPath removes a block from the "end" of the chain, i.e. the block -// with the largest height. -func popPath(tx *sql.Tx) error { - // Fetch and update the block height. - _, err := tx.Exec("UPDATE cs_height SET height = height - 1 WHERE id = 1") - if err != nil { - return err - } - - // Remove the block from the path. - _, err = tx.Exec(` - DELETE FROM cs_path WHERE height = ( - SELECT height FROM cs_height WHERE id = 1) + 1 - `) - return err -} - -// isSiacoinOutput returns true if there is a siacoin output of that id in the -// database. -func isSiacoinOutput(tx *sql.Tx, id types.SiacoinOutputID) bool { - var count int - err := tx.QueryRow("SELECT COUNT(*) FROM cs_sco WHERE scoid = ?", id[:]).Scan(&count) - if err != nil { - return false - } - return count > 0 -} - -// saveConsensusChange stores the consensus change node in the database. -func saveConsensusChange(tx *sql.Tx, ceid modules.ConsensusChangeID, cn changeNode) error { - var buf bytes.Buffer - e := types.NewEncoder(&buf) - cn.EncodeTo(e) - e.Flush() - _, err := tx.Exec(` - INSERT INTO cs_cl (ceid, bytes) VALUES (?, ?) AS new - ON DUPLICATE KEY UPDATE bytes = new.bytes - `, ceid[:], buf.Bytes()) - return err -} - -// loadConsensusChange retrieves the consensus change node from the database. -func loadConsensusChange(tx *sql.Tx, ceid modules.ConsensusChangeID) (cn changeNode, err error) { - var cnBytes []byte - err = tx.QueryRow("SELECT bytes FROM cs_cl WHERE ceid = ?", ceid[:]).Scan(&cnBytes) - if err != nil { - return - } - d := types.NewBufDecoder(cnBytes) - cn.DecodeFrom(d) - return cn, d.Err() -} - -// changeLogTailID retrieves the ID of the most recent consensus change. -func changeLogTailID(tx *sql.Tx) (tailID modules.ConsensusChangeID) { - id := make([]byte, 32) - err := tx.QueryRow("SELECT bytes FROM cs_changelog WHERE id = 1").Scan(&id) - if err != nil { - return - } - copy(tailID[:], id[:]) - return -} - -// setChangeLogTailID updates the ID of the most recent consensus change. -func setChangeLogTailID(tx *sql.Tx, ceid modules.ConsensusChangeID) (err error) { - _, err = tx.Exec("REPLACE INTO cs_changelog (id, bytes) VALUES (1, ?)", ceid[:]) - return -} - -// countDelayedSiacoins returns the sum of all delayed Siacoin outputs. -func countDelayedSiacoins(tx *sql.Tx) (total types.Currency, err error) { - rows, err := tx.Query("SELECT bytes FROM cs_dsco") - if err != nil { - return - } - - for rows.Next() { - var dsco types.SiacoinOutput - var dscoBytes []byte - if err = rows.Scan(&dscoBytes); err != nil { - rows.Close() - return types.ZeroCurrency, err - } - d := types.NewBufDecoder(dscoBytes) - dsco.DecodeFrom(d) - if err = d.Err(); err != nil { - rows.Close() - return types.ZeroCurrency, err - } - total = total.Add(dsco.Value) - } - rows.Close() - - return -} - -// countSiacoins returns the sum of all Siacoin outputs. -func countSiacoins(tx *sql.Tx) (total types.Currency, err error) { - rows, err := tx.Query("SELECT bytes FROM cs_sco") - if err != nil { - return - } - - for rows.Next() { - var sco types.SiacoinOutput - var scoBytes []byte - if err = rows.Scan(&scoBytes); err != nil { - rows.Close() - return types.ZeroCurrency, err - } - d := types.NewBufDecoder(scoBytes) - sco.DecodeFrom(d) - if err = d.Err(); err != nil { - rows.Close() - return types.ZeroCurrency, err - } - total = total.Add(sco.Value) - } - rows.Close() - - return -} - -// countFileContractPayouts returns the sum of all file contract payouts. -func countFileContractPayouts(tx *sql.Tx) (total types.Currency, err error) { - rows, err := tx.Query("SELECT bytes FROM cs_fc") - if err != nil { - return - } - - for rows.Next() { - var fc types.FileContract - var fcBytes []byte - if err = rows.Scan(&fcBytes); err != nil { - rows.Close() - return types.ZeroCurrency, err - } - d := types.NewBufDecoder(fcBytes) - fc.DecodeFrom(d) - if err = d.Err(); err != nil { - rows.Close() - return types.ZeroCurrency, err - } - var fcCoins types.Currency - for _, output := range fc.ValidProofOutputs { - fcCoins = fcCoins.Add(output.Value) - } - total = total.Add(fcCoins) - } - rows.Close() - - return -} - -// countSiafundClaims returns the sum of all Siacoins from Siafund claims. -func countSiafundClaims(tx *sql.Tx) (total types.Currency, err error) { - coinsPerFund := getSiafundPool(tx) - rows, err := tx.Query("SELECT bytes FROM cs_sfo") - if err != nil { - return - } - - for rows.Next() { - var sfoBytes []byte - if err = rows.Scan(&sfoBytes); err != nil { - rows.Close() - return types.ZeroCurrency, err - } - d := types.NewBufDecoder(sfoBytes) - var value, claimStart types.Currency - value.DecodeFrom(d) - (&types.Address{}).DecodeFrom(d) - claimStart.DecodeFrom(d) - if err = d.Err(); err != nil { - rows.Close() - return types.ZeroCurrency, err - } - claimCoins := coinsPerFund.Sub(claimStart).Mul(value).Div64(modules.SiafundCount) - total = total.Add(claimCoins) - } - rows.Close() - - return -} - -// countSiafunds returns the sum of all Siafund outputs. -func countSiafunds(tx *sql.Tx) (total uint64, err error) { - rows, err := tx.Query("SELECT bytes FROM cs_sfo") - if err != nil { - return - } - - for rows.Next() { - var sfo types.SiafundOutput - var sfoBytes []byte - if err = rows.Scan(&sfoBytes); err != nil { - rows.Close() - return 0, err - } - d := types.NewBufDecoder(sfoBytes) - sfo.DecodeFrom(d) - if err = d.Err(); err != nil { - rows.Close() - return 0, err - } - total = total + sfo.Value - } - rows.Close() - - return -} - -// findSiacoinOutput tries to find a Siacoin output with the given ID in the -// consensus set. -func findSiacoinOutput(tx *sql.Tx, scoid types.SiacoinOutputID) (sco types.SiacoinOutput, exists bool, err error) { - var scoBytes []byte - err = tx.QueryRow("SELECT bytes FROM cs_sco WHERE scoid = ?", scoid[:]).Scan(&scoBytes) - if errors.Is(err, sql.ErrNoRows) { - return types.SiacoinOutput{}, false, nil - } - if err != nil { - return types.SiacoinOutput{}, false, err - } - d := types.NewBufDecoder(scoBytes) - sco.DecodeFrom(d) - return sco, true, d.Err() -} - -// addSiacoinOutput adds a Siacoin output to the database. -func addSiacoinOutput(tx *sql.Tx, id types.SiacoinOutputID, sco types.SiacoinOutput) error { - var buf bytes.Buffer - e := types.NewEncoder(&buf) - sco.EncodeTo(e) - e.Flush() - _, err := tx.Exec("REPLACE INTO cs_sco (scoid, bytes) VALUES (?, ?)", id[:], buf.Bytes()) - return err -} - -// removeSiacoinOutput removes a Siacoin output from the database. -func removeSiacoinOutput(tx *sql.Tx, id types.SiacoinOutputID) error { - _, err := tx.Exec("DELETE FROM cs_sco WHERE scoid = ?", id[:]) - return err -} - -// findSiafundOutput tries to find a Siafund output with the given ID in the -// consensus set. -func findSiafundOutput(tx *sql.Tx, sfoid types.SiafundOutputID) (sfo types.SiafundOutput, claimStart types.Currency, exists bool, err error) { - var sfoBytes []byte - err = tx.QueryRow("SELECT bytes FROM cs_sfo WHERE sfoid = ?", sfoid[:]).Scan(&sfoBytes) - if errors.Is(err, sql.ErrNoRows) { - return types.SiafundOutput{}, types.Currency{}, false, nil - } - if err != nil { - return types.SiafundOutput{}, types.Currency{}, false, err - } - d := types.NewBufDecoder(sfoBytes) - var val types.Currency - val.DecodeFrom(d) - sfo.Value = val.Lo - sfo.Address.DecodeFrom(d) - claimStart.DecodeFrom(d) - err = d.Err() - if err == nil && blockHeight(tx) > modules.DevAddrHardforkHeight && sfo.Address == oldDevAddr { - sfo.Address = newDevAddr - } - return sfo, claimStart, true, err -} - -// addSiafundOutput adds a Siafund output to the database. -func addSiafundOutput(tx *sql.Tx, id types.SiafundOutputID, sfo types.SiafundOutput, claimStart types.Currency) error { - if sfo.Value == 0 { - return errors.New("zero value Siafund being added") - } - var buf bytes.Buffer - e := types.NewEncoder(&buf) - types.NewCurrency64(sfo.Value).EncodeTo(e) - sfo.Address.EncodeTo(e) - claimStart.EncodeTo(e) - e.Flush() - _, err := tx.Exec("REPLACE INTO cs_sfo (sfoid, bytes) VALUES (?, ?)", id[:], buf.Bytes()) - return err -} - -// removeSiafundOutput removes a Siafund output from the database. -func removeSiafundOutput(tx *sql.Tx, id types.SiafundOutputID) error { - _, err := tx.Exec("DELETE FROM cs_sfo WHERE sfoid = ?", id[:]) - return err -} - -// findFileContract tries to find a file contract with the given ID in the -// consensus set. -func findFileContract(tx *sql.Tx, fcid types.FileContractID) (fc types.FileContract, exists bool, err error) { - var fcBytes []byte - err = tx.QueryRow("SELECT bytes FROM cs_fc WHERE fcid = ?", fcid[:]).Scan(&fcBytes) - if errors.Is(err, sql.ErrNoRows) { - return types.FileContract{}, false, nil - } - if err != nil { - return types.FileContract{}, false, err - } - d := types.NewBufDecoder(fcBytes) - fc.DecodeFrom(d) - return fc, true, d.Err() -} - -// addFileContract adds a file contract to the database. -func addFileContract(tx *sql.Tx, id types.FileContractID, fc types.FileContract) error { - // Sanity check - should not be adding a zero-payout file contract. - if fc.Payout.IsZero() { - return errors.New("adding zero-payout file contract") - } - - // Add the file contract to the database. - var buf bytes.Buffer - e := types.NewEncoder(&buf) - fc.EncodeTo(e) - e.Flush() - _, err := tx.Exec("REPLACE INTO cs_fc (fcid, bytes) VALUES (?, ?)", id[:], buf.Bytes()) - if err != nil { - return err - } - - // Add an entry for when the file contract expires. - _, err = tx.Exec("REPLACE INTO cs_fcex (height, fcid, bytes) VALUES (?, ?, ?)", fc.WindowEnd, id[:], []byte{}) - return err -} - -// removeFileContract removes a file contract from the database. -func removeFileContract(tx *sql.Tx, id types.FileContractID) error { - // Delete the file contract entry. - _, err := tx.Exec("DELETE FROM cs_fc WHERE fcid = ?", id[:]) - if err != nil { - return err - } - - // Delete the entry for the file contract's expiration. - _, err = tx.Exec("DELETE FROM cs_fcex WHERE fcid = ?", id[:]) - return err -} - -// getSiafundPool returns the current value of the Siafund pool. No error is -// returned as the Siafund pool should always be available. -func getSiafundPool(tx *sql.Tx) (pool types.Currency) { - var poolBytes []byte - err := tx.QueryRow("SELECT bytes FROM cs_sfpool WHERE id = 1").Scan(&poolBytes) - if err != nil { - return types.ZeroCurrency - } - - d := types.NewBufDecoder(poolBytes) - pool.DecodeFrom(d) - if err := d.Err(); err != nil { - return types.ZeroCurrency - } - - return pool -} - -// setSiafundPool updates the saved Siafund pool in the database. -func setSiafundPool(tx *sql.Tx, c types.Currency) error { - var buf bytes.Buffer - e := types.NewEncoder(&buf) - c.EncodeTo(e) - e.Flush() - _, err := tx.Exec("REPLACE INTO cs_sfpool (id, bytes) VALUES (1, ?)", buf.Bytes()) - return err -} - -// getFoundationUnlockHashes returns the current primary and failsafe Foundation -// addresses. -func getFoundationUnlockHashes(tx *sql.Tx) (primary, failsafe types.Address, err error) { - fuhBytes := make([]byte, 64) - err = tx.QueryRow("SELECT bytes FROM cs_fuh_current WHERE id = 1").Scan(&fuhBytes) - if err != nil { - return types.Address{}, types.Address{}, err - } - copy(primary[:], fuhBytes[:32]) - copy(failsafe[:], fuhBytes[32:]) - return primary, failsafe, nil -} - -// setFoundationUnlockHashes updates the primary and failsafe Foundation -// addresses. -func setFoundationUnlockHashes(tx *sql.Tx, primary, failsafe types.Address) error { - fuhBytes := make([]byte, 64) - copy(fuhBytes[:32], primary[:]) - copy(fuhBytes[32:], failsafe[:]) - _, err := tx.Exec("REPLACE INTO cs_fuh_current (id, bytes) VALUES (1, ?)", fuhBytes) - return err -} - -// getPriorFoundationUnlockHashes returns the primary and failsafe Foundation -// addresses immediately prior to the application of the specified block. -func getPriorFoundationUnlockHashes(tx *sql.Tx, height uint64) (primary, failsafe types.Address, exists bool, err error) { - fuhBytes := make([]byte, 64) - err = tx.QueryRow("SELECT bytes FROM cs_fuh WHERE height = ?", height).Scan(&fuhBytes) - if errors.Is(err, sql.ErrNoRows) { - return types.Address{}, types.Address{}, false, nil - } - if err != nil { - return types.Address{}, types.Address{}, false, err - } - copy(primary[:], fuhBytes[:32]) - copy(failsafe[:], fuhBytes[32:]) - return primary, failsafe, true, nil -} - -// setPriorFoundationUnlockHashes sets the primary and failsafe Foundation -// addresses immediately prior to the application of the specified block. -func setPriorFoundationUnlockHashes(tx *sql.Tx, height uint64) error { - fuhBytes := make([]byte, 64) - err := tx.QueryRow("SELECT bytes FROM cs_fuh_current WHERE id = 1").Scan(&fuhBytes) - if err != nil { - return err - } - _, err = tx.Exec(` - REPLACE INTO cs_fuh (height, bytes) VALUES (?, ?) - `, height, fuhBytes) - return err -} - -// deletePriorFoundationUnlockHashes deletes the primary and failsafe Foundation -// addresses for the specified height. -func deletePriorFoundationUnlockHashes(tx *sql.Tx, height uint64) error { - _, err := tx.Exec("DELETE FROM cs_fuh WHERE height = ?", height) - return err -} - -// addDSCO adds a delayed Siacoin output to the consnesus set. -func addDSCO(tx *sql.Tx, bh uint64, id types.SiacoinOutputID, sco types.SiacoinOutput) error { - // Sanity check - output should not already be in the full set of outputs. - var count int - err := tx.QueryRow("SELECT COUNT(*) FROM cs_sco WHERE scoid = ?", id[:]).Scan(&count) - if err != nil { - return err - } - if count > 0 { - return errors.New("dsco already in output set") - } - - // Sanity check - should not be adding an item already in the db. - err = tx.QueryRow("SELECT COUNT(*) FROM cs_dsco WHERE scoid = ?", id[:]).Scan(&count) - if err != nil { - return err - } - if count > 0 { - return errRepeatInsert - } - - var buf bytes.Buffer - e := types.NewEncoder(&buf) - sco.EncodeTo(e) - e.Flush() - _, err = tx.Exec("REPLACE INTO cs_dsco (height, scoid, bytes) VALUES (?, ?, ?)", bh, id[:], buf.Bytes()) - - return err -} - -// removeDSCO removes a delayed siacoin output from the consensus set. -func removeDSCO(tx *sql.Tx, bh uint64, id types.SiacoinOutputID) error { - _, err := tx.Exec("DELETE FROM cs_dsco WHERE height = ? AND scoid = ?", bh, id[:]) - return err -} - -// checkDoSBlock checks if the block is a known DoS block. -func checkDoSBlock(tx *sql.Tx, id types.BlockID) (known bool, err error) { - var count int - err = tx.QueryRow("SELECT COUNT(*) FROM cs_dos WHERE bid = ?", id[:]).Scan(&count) - if err != nil { - return - } - return count > 0, err -} - -// addDoSBlock adds the block to known blocks list. -func addDoSBlock(tx *sql.Tx, id types.BlockID) error { - _, err := tx.Exec(` - REPLACE INTO cs_dos (bid) VALUES (?) - `, id[:]) - return err -} diff --git a/modules/consensus/maintenance.go b/modules/consensus/maintenance.go deleted file mode 100644 index ad85417..0000000 --- a/modules/consensus/maintenance.go +++ /dev/null @@ -1,259 +0,0 @@ -package consensus - -import ( - "database/sql" - "errors" - - "github.com/mike76-dev/sia-satellite/modules" - - "go.sia.tech/core/types" -) - -var ( - errOutputAlreadyMature = errors.New("delayed siacoin output is already in the matured outputs set") - errPayoutsAlreadyPaid = errors.New("payouts are already in the consensus set") -) - -// applyFoundationSubsidy adds a Foundation subsidy to the consensus set as a -// delayed siacoin output. If no subsidy is due on the given block, no output is -// added. -func applyFoundationSubsidy(tx *sql.Tx, pb *processedBlock) error { - // NOTE: this conditional is split up to better visualize test coverage. - if pb.Height < modules.FoundationHardforkHeight { - return nil - } else if (pb.Height-modules.FoundationHardforkHeight)%modules.FoundationSubsidyFrequency != 0 { - return nil - } - value := modules.FoundationSubsidyPerBlock.Mul64(modules.FoundationSubsidyFrequency) - if pb.Height == modules.FoundationHardforkHeight { - value = modules.InitialFoundationSubsidy - } - - // The subsidy is always sent to the primary address. - addr, _, err := getFoundationUnlockHashes(tx) - if err != nil { - return err - } - dscod := modules.DelayedSiacoinOutputDiff{ - Direction: modules.DiffApply, - ID: pb.Block.ID().FoundationOutputID(), - SiacoinOutput: types.SiacoinOutput{ - Value: value, - Address: addr, - }, - MaturityHeight: pb.Height + modules.MaturityDelay, - } - - pb.DelayedSiacoinOutputDiffs = append(pb.DelayedSiacoinOutputDiffs, dscod) - return commitDelayedSiacoinOutputDiff(tx, dscod, modules.DiffApply) -} - -// applyMinerPayouts adds a block's miner payouts to the consensus set as -// delayed siacoin outputs. -func applyMinerPayouts(tx *sql.Tx, pb *processedBlock) error { - for i := range pb.Block.MinerPayouts { - mpid := pb.Block.ID().MinerOutputID(i) - dscod := modules.DelayedSiacoinOutputDiff{ - Direction: modules.DiffApply, - ID: mpid, - SiacoinOutput: pb.Block.MinerPayouts[i], - MaturityHeight: pb.Height + modules.MaturityDelay, - } - pb.DelayedSiacoinOutputDiffs = append(pb.DelayedSiacoinOutputDiffs, dscod) - if err := commitDelayedSiacoinOutputDiff(tx, dscod, modules.DiffApply); err != nil { - return err - } - } - return nil -} - -// applyMaturedSiacoinOutputs goes through the list of siacoin outputs that -// have matured and adds them to the consensus set. This also updates the block -// node diff set. -func applyMaturedSiacoinOutputs(tx *sql.Tx, pb *processedBlock) error { - // Skip this step if the blockchain is not old enough to have maturing - // outputs. - if pb.Height < modules.MaturityDelay { - return nil - } - - // Iterate through the list of delayed siacoin outputs. - rows, err := tx.Query("SELECT scoid, bytes FROM cs_dsco WHERE height = ?", pb.Height) - if err != nil { - return err - } - - var scods []modules.SiacoinOutputDiff - var dscods []modules.DelayedSiacoinOutputDiff - for rows.Next() { - var scoid types.SiacoinOutputID - id := make([]byte, 32) - var scoBytes []byte - var sco types.SiacoinOutput - if err := rows.Scan(&id, &scoBytes); err != nil { - rows.Close() - return err - } - - copy(scoid[:], id[:]) - d := types.NewBufDecoder(scoBytes) - sco.DecodeFrom(d) - if err := d.Err(); err != nil { - rows.Close() - return err - } - - // Add the output to the ConsensusSet and record the diff in the - // blockNode. - scod := modules.SiacoinOutputDiff{ - Direction: modules.DiffApply, - ID: scoid, - SiacoinOutput: sco, - } - scods = append(scods, scod) - - // Create the dscod and add it to the list of dscods that should be - // deleted. - dscod := modules.DelayedSiacoinOutputDiff{ - Direction: modules.DiffRevert, - ID: scoid, - SiacoinOutput: sco, - MaturityHeight: pb.Height, - } - dscods = append(dscods, dscod) - } - rows.Close() - - // Sanity check - the output should not already be in siacoinOuptuts. - for _, scod := range scods { - if isSiacoinOutput(tx, scod.ID) { - return errOutputAlreadyMature - } - } - - for _, scod := range scods { - pb.SiacoinOutputDiffs = append(pb.SiacoinOutputDiffs, scod) - if err := commitSiacoinOutputDiff(tx, scod, modules.DiffApply); err != nil { - return err - } - } - for _, dscod := range dscods { - pb.DelayedSiacoinOutputDiffs = append(pb.DelayedSiacoinOutputDiffs, dscod) - if err := commitDelayedSiacoinOutputDiff(tx, dscod, modules.DiffApply); err != nil { - return err - } - } - - // Delete the delayed SC outputs. - _, err = tx.Exec("DELETE FROM cs_dsco WHERE height = ?", pb.Height) - return err -} - -// applyMissedStorageProof adds the outputs and diffs that result from a file -// contract expiring. -func applyMissedStorageProof(tx *sql.Tx, pb *processedBlock, fcid types.FileContractID) (dscods []modules.DelayedSiacoinOutputDiff, fcd modules.FileContractDiff, err error) { - // Sanity checks. - fc, exists, err := findFileContract(tx, fcid) - if err != nil { - return nil, modules.FileContractDiff{}, err - } - if !exists { - return nil, modules.FileContractDiff{}, errors.New("no file contract found") - } - - // Add all of the outputs in the missed proof outputs to the consensus set. - for i, mpo := range fc.MissedProofOutputs { - // Sanity check - output should not already exist. - spoid := modules.StorageProofOutputID(fcid, false, i) - if isSiacoinOutput(tx, spoid) { - return nil, modules.FileContractDiff{}, errPayoutsAlreadyPaid - } - - // Don't add the output if the value is zero. - dscod := modules.DelayedSiacoinOutputDiff{ - Direction: modules.DiffApply, - ID: spoid, - SiacoinOutput: mpo, - MaturityHeight: pb.Height + modules.MaturityDelay, - } - dscods = append(dscods, dscod) - } - - // Remove the file contract from the consensus set and record the diff in - // the blockNode. - fcd = modules.FileContractDiff{ - Direction: modules.DiffRevert, - ID: fcid, - FileContract: fc, - } - return dscods, fcd, nil -} - -// applyFileContractMaintenance looks for all of the file contracts that have -// expired without an appropriate storage proof, and calls 'applyMissedProof' -// for the file contract. -func applyFileContractMaintenance(tx *sql.Tx, pb *processedBlock) error { - // Get all of the expiring file contracts. - rows, err := tx.Query("SELECT fcid FROM cs_fcex WHERE height = ?", pb.Height) - if err != nil { - return err - } - - // Iterate through the expired contracts and add them to the list. - var dscods []modules.DelayedSiacoinOutputDiff - var fcds []modules.FileContractDiff - var fcids []types.FileContractID - for rows.Next() { - var fcid types.FileContractID - id := make([]byte, 32) - if err := rows.Scan(&id); err != nil { - rows.Close() - return err - } - copy(fcid[:], id[:]) - fcids = append(fcids, fcid) - } - rows.Close() - - for _, fcid := range fcids { - amspDSCODS, fcd, err := applyMissedStorageProof(tx, pb, fcid) - if err != nil { - return err - } - fcds = append(fcds, fcd) - dscods = append(dscods, amspDSCODS...) - } - - for _, dscod := range dscods { - pb.DelayedSiacoinOutputDiffs = append(pb.DelayedSiacoinOutputDiffs, dscod) - if err := commitDelayedSiacoinOutputDiff(tx, dscod, modules.DiffApply); err != nil { - return err - } - } - for _, fcd := range fcds { - pb.FileContractDiffs = append(pb.FileContractDiffs, fcd) - if err := commitFileContractDiff(tx, fcd, modules.DiffApply); err != nil { - return err - } - } - - // Delete expired file contracts. - _, err = tx.Exec("DELETE FROM cs_fcex WHERE height = ?", pb.Height) - return err -} - -// applyMaintenance applies block-level alterations to the consensus set. -// Maintenance is applied after all of the transactions for the block have been -// applied. -func applyMaintenance(tx *sql.Tx, pb *processedBlock) error { - if err := applyMinerPayouts(tx, pb); err != nil { - return err - } - if err := applyFoundationSubsidy(tx, pb); err != nil { - return err - } - if err := applyMaturedSiacoinOutputs(tx, pb); err != nil { - return err - } - return applyFileContractMaintenance(tx, pb) -} diff --git a/modules/portal/dashboard.go b/modules/portal/dashboard.go index 8900765..301b6cc 100644 --- a/modules/portal/dashboard.go +++ b/modules/portal/dashboard.go @@ -242,7 +242,7 @@ func (api *portalAPI) hostsHandlerPOST(w http.ResponseWriter, req *http.Request, return } if data.Estimation == 0 { - api.portal.log.Println("ERROR: zero estimation") + api.portal.log.Error("zero estimation") writeError(w, Error{ Code: httpErrorInternal, @@ -1013,7 +1013,7 @@ func (api *portalAPI) feesHandlerGET(w http.ResponseWriter, _ *http.Request, _ h } // announcementHandlerGET handles the GET /dashboard/announcement requests. -func (api *portalAPI) announcementHandlerGET(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { +func (api *portalAPI) announcementHandlerGET(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) { // Get the current announcement. text, _, err := api.portal.GetAnnouncement() if err != nil { diff --git a/modules/syncer/store.go b/modules/syncer/store.go index 33c1a8f..5058b6c 100644 --- a/modules/syncer/store.go +++ b/modules/syncer/store.go @@ -180,9 +180,9 @@ func (jps *JSONPeerStore) save() error { defer f.Close() if _, err = f.Write(js); err != nil { return err - } else if f.Sync(); err != nil { + } else if err = f.Sync(); err != nil { return err - } else if f.Close(); err != nil { + } else if err = f.Close(); err != nil { return err } else if err := os.Rename(jps.path+"_tmp", jps.path); err != nil { return err diff --git a/modules/transactionpool/database.go b/modules/transactionpool/database.go deleted file mode 100644 index bc182e4..0000000 --- a/modules/transactionpool/database.go +++ /dev/null @@ -1,169 +0,0 @@ -package transactionpool - -import ( - "bytes" - "database/sql" - "errors" - - "github.com/mike76-dev/sia-satellite/modules" - - "go.sia.tech/core/types" -) - -// Errors relating to the database. -var ( - // errNilConsensusChange is returned if there is no consensus change in the - // database. - errNilConsensusChange = errors.New("no consensus change found") - - // errNilFeeMedian is the message returned if a database does not find fee - // median persistence. - errNilFeeMedian = errors.New("no fee median found") - - // errNilRecentBlock is returned if there is no data stored in - // fieldRecentBlockID. - errNilRecentBlock = errors.New("no recent block found in the database") -) - -// Complex objects that get stored in database fields. -type ( - // medianPersist is the object that gets stored in the database so that - // the transaction pool can persist its block based fee estimations. - medianPersist struct { - RecentMedians []types.Currency - RecentMedianFee types.Currency - } -) - -// EncodeTo implements types.EncoderTo. -func (mp *medianPersist) EncodeTo(e *types.Encoder) { - e.WritePrefix(len(mp.RecentMedians)) - for _, rm := range mp.RecentMedians { - rm.EncodeTo(e) - } - mp.RecentMedianFee.EncodeTo(e) -} - -// DecodeFrom implements types.DecoderFrom. -func (mp *medianPersist) DecodeFrom(d *types.Decoder) { - mp.RecentMedians = make([]types.Currency, d.ReadPrefix()) - for i := 0; i < len(mp.RecentMedians); i++ { - mp.RecentMedians[i].DecodeFrom(d) - } - mp.RecentMedianFee.DecodeFrom(d) -} - -// deleteTransaction deletes a transaction from the list of confirmed -// transactions. -func (tp *TransactionPool) deleteTransaction(id types.TransactionID) error { - _, err := tp.dbTx.Exec("DELETE FROM tp_ctx WHERE txid = ?", id[:]) - return err -} - -// getBlockHeight returns the most recent block height from the database. -func (tp *TransactionPool) getBlockHeight() (bh uint64, err error) { - err = tp.dbTx.QueryRow("SELECT height FROM tp_height WHERE id = 1").Scan(&bh) - return -} - -// getFeeMedian will get the fee median struct stored in the database. -func (tp *TransactionPool) getFeeMedian() (medianPersist, error) { - var medianBytes []byte - err := tp.dbTx.QueryRow("SELECT bytes FROM tp_median WHERE id = 1").Scan(&medianBytes) - if errors.Is(err, sql.ErrNoRows) { - return medianPersist{}, errNilFeeMedian - } - if err != nil { - return medianPersist{}, err - } - - var mp medianPersist - d := types.NewBufDecoder(medianBytes) - mp.DecodeFrom(d) - if err := d.Err(); err != nil { - return medianPersist{}, modules.AddContext(err, "unable to unmarshal median data") - } - return mp, nil -} - -// getRecentBlockID will fetch the most recent block id and most recent parent -// id from the database. -func (tp *TransactionPool) getRecentBlockID() (recentID types.BlockID, err error) { - idBytes := make([]byte, 32) - err = tp.dbTx.QueryRow("SELECT bid FROM tp_recent WHERE id = 1").Scan(&idBytes) - if errors.Is(err, sql.ErrNoRows) { - return types.BlockID{}, errNilRecentBlock - } - if err != nil { - return types.BlockID{}, err - } - copy(recentID[:], idBytes[:]) - if recentID == (types.BlockID{}) { - return types.BlockID{}, errNilRecentBlock - } - return recentID, nil -} - -// getRecentConsensusChange returns the most recent consensus change from the -// database. -func (tp *TransactionPool) getRecentConsensusChange() (cc modules.ConsensusChangeID, err error) { - ccBytes := make([]byte, 32) - err = tp.dbTx.QueryRow("SELECT ceid FROM tp_cc WHERE id = 1").Scan(&ccBytes) - if errors.Is(err, sql.ErrNoRows) { - return modules.ConsensusChangeID{}, errNilConsensusChange - } - if err != nil { - return modules.ConsensusChangeID{}, err - } - copy(cc[:], ccBytes) - return cc, nil -} - -// putBlockHeight updates the transaction pool's block height. -func (tp *TransactionPool) putBlockHeight(height uint64) error { - tp.blockHeight = height - _, err := tp.dbTx.Exec(` - INSERT INTO tp_height (id, height) VALUES (1, ?) AS new - ON DUPLICATE KEY UPDATE height = new.height - `, height) - return err -} - -// putFeeMedian puts a median fees object into the database. -func (tp *TransactionPool) putFeeMedian(mp medianPersist) error { - var buf bytes.Buffer - e := types.NewEncoder(&buf) - mp.EncodeTo(e) - e.Flush() - _, err := tp.dbTx.Exec(` - INSERT INTO tp_median (id, bytes) VALUES (1, ?) AS new - ON DUPLICATE KEY UPDATE bytes = new.bytes - `, buf.Bytes()) - return err -} - -// putRecentBlockID will store the most recent block id and the parent id of -// that block in the database. -func (tp *TransactionPool) putRecentBlockID(recentID types.BlockID) error { - _, err := tp.dbTx.Exec(` - INSERT INTO tp_recent (id, bid) VALUES (1, ?) AS new - ON DUPLICATE KEY UPDATE bid = new.bid - `, recentID[:]) - return err -} - -// putRecentConsensusChange updates the most recent consensus change seen by -// the transaction pool. -func (tp *TransactionPool) putRecentConsensusChange(cc modules.ConsensusChangeID) error { - _, err := tp.dbTx.Exec(` - INSERT INTO tp_cc (id, ceid) VALUES (1, ?) AS new - ON DUPLICATE KEY UPDATE ceid = new.ceid - `, cc[:]) - return err -} - -// putTransaction adds a transaction to the list of confirmed transactions. -func (tp *TransactionPool) putTransaction(id types.TransactionID) error { - _, err := tp.dbTx.Exec("REPLACE INTO tp_ctx (txid) VALUES (?)", id[:]) - return err -} diff --git a/modules/wallet/unseeded.go b/modules/wallet/unseeded.go deleted file mode 100644 index baeed0f..0000000 --- a/modules/wallet/unseeded.go +++ /dev/null @@ -1,96 +0,0 @@ -package wallet - -import ( - "bytes" - "errors" - - "github.com/mike76-dev/sia-satellite/modules" - - "go.sia.tech/core/types" - - "lukechampine.com/frand" -) - -var ( - errDuplicateSpendableKey = errors.New("key has already been loaded into the wallet") - - // ErrInconsistentKeys is the error when keys provided are for different addresses. - ErrInconsistentKeys = errors.New("keyfiles provided that are for different addresses") - // ErrInsufficientKeys is the error when there's not enough keys provided to spend - // the Siafunds. - ErrInsufficientKeys = errors.New("not enough keys provided to spend the siafunds") -) - -// decryptSpendableKey decrypts an encryptedSpendableKey, returning a -// spendableKey. -func decryptSpendableKey(masterKey modules.WalletKey, uk encryptedSpendableKey) (sk spendableKey, err error) { - // Verify that the decryption key is correct. - decryptionKey := saltedEncryptionKey(masterKey, uk.Salt) - err = verifyEncryption(decryptionKey, uk.EncryptionVerification) - if err != nil { - return - } - - // Decrypt the spendable key and add it to the wallet. - encodedKey, err := modules.Decrypt(decryptionKey, uk.SpendableKey) - if err != nil { - return - } - d := types.NewBufDecoder(encodedKey) - sk.DecodeFrom(d) - err = d.Err() - - return -} - -// integrateSpendableKey loads a spendableKey into the wallet. -func (w *Wallet) integrateSpendableKey(masterKey modules.WalletKey, sk spendableKey) { - w.keys[sk.UnlockConditions.UnlockHash()] = sk -} - -// loadSpendableKey loads a spendable key into the wallet database. -func (w *Wallet) loadSpendableKey(masterKey modules.WalletKey, sk spendableKey) error { - // Duplication is detected by looking at the set of unlock conditions. If - // the wallet is locked, correct deduplication is uncertain. - if !w.unlocked { - return modules.ErrLockedWallet - } - - // Check for duplicates. - _, exists := w.keys[sk.UnlockConditions.UnlockHash()] - if exists { - return errDuplicateSpendableKey - } - - // TODO: Check that the key is actually spendable. - - // Create a UID and encryption verification. - var uk encryptedSpendableKey - var err error - frand.Read(uk.Salt[:]) - encryptionKey := saltedEncryptionKey(masterKey, uk.Salt) - uk.EncryptionVerification, err = modules.Encrypt(encryptionKey, verificationPlaintext) - if err != nil { - return err - } - - // Encrypt and save the key. - var buf bytes.Buffer - e := types.NewEncoder(&buf) - sk.EncodeTo(e) - e.Flush() - uk.SpendableKey, err = modules.Encrypt(encryptionKey, buf.Bytes()) - if err != nil { - return err - } - - err = checkMasterKey(w.dbTx, masterKey) - if err != nil { - return err - } - current, err := dbGetUnseededKeys(w.dbTx) - if err != nil { - return err - } - return dbPutUnseededKeys(w.dbTx, append(current, uk)) -}