github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/wallet/update.go (about)

     1  package wallet
     2  
     3  import (
     4  	"math"
     5  
     6  	"github.com/NebulousLabs/Sia/build"
     7  	"github.com/NebulousLabs/Sia/modules"
     8  	"github.com/NebulousLabs/Sia/types"
     9  )
    10  
    11  // updateConfirmedSet uses a consensus change to update the confirmed set of
    12  // outputs as understood by the wallet.
    13  func (w *Wallet) updateConfirmedSet(cc modules.ConsensusChange) {
    14  	for _, diff := range cc.SiacoinOutputDiffs {
    15  		// Verify that the diff is relevant to the wallet.
    16  		_, exists := w.keys[diff.SiacoinOutput.UnlockHash]
    17  		if !exists {
    18  			continue
    19  		}
    20  
    21  		_, exists = w.siacoinOutputs[diff.ID]
    22  		if diff.Direction == modules.DiffApply {
    23  			if build.DEBUG && exists {
    24  				panic("adding an existing output to wallet")
    25  			}
    26  			w.siacoinOutputs[diff.ID] = diff.SiacoinOutput
    27  		} else {
    28  			if build.DEBUG && !exists {
    29  				panic("deleting nonexisting output from wallet")
    30  			}
    31  			delete(w.siacoinOutputs, diff.ID)
    32  		}
    33  	}
    34  	for _, diff := range cc.SiafundOutputDiffs {
    35  		// Verify that the diff is relevant to the wallet.
    36  		_, exists := w.keys[diff.SiafundOutput.UnlockHash]
    37  		if !exists {
    38  			continue
    39  		}
    40  
    41  		_, exists = w.siafundOutputs[diff.ID]
    42  		if diff.Direction == modules.DiffApply {
    43  			if build.DEBUG && exists {
    44  				panic("adding an existing output to wallet")
    45  			}
    46  			w.siafundOutputs[diff.ID] = diff.SiafundOutput
    47  		} else {
    48  			if build.DEBUG && !exists {
    49  				panic("deleting nonexisting output from wallet")
    50  			}
    51  			delete(w.siafundOutputs, diff.ID)
    52  		}
    53  	}
    54  	for _, diff := range cc.SiafundPoolDiffs {
    55  		if diff.Direction == modules.DiffApply {
    56  			w.siafundPool = diff.Adjusted
    57  		} else {
    58  			w.siafundPool = diff.Previous
    59  		}
    60  	}
    61  }
    62  
    63  // revertHistory reverts any transaction history that was destroyed by reverted
    64  // blocks in the consensus change.
    65  func (w *Wallet) revertHistory(cc modules.ConsensusChange) {
    66  	for _, block := range cc.RevertedBlocks {
    67  		// Remove any transactions that have been reverted.
    68  		for i := len(block.Transactions) - 1; i >= 0; i-- {
    69  			// If the transaction is relevant to the wallet, it will be the
    70  			// most recent transaction appended to w.processedTransactions.
    71  			// Relevance can be determined just by looking at the last element
    72  			// of w.processedTransactions.
    73  			txn := block.Transactions[i]
    74  			txid := txn.ID()
    75  			if len(w.processedTransactions) > 0 && txid == w.processedTransactions[len(w.processedTransactions)-1].TransactionID {
    76  				w.processedTransactions = w.processedTransactions[:len(w.processedTransactions)-1]
    77  				delete(w.processedTransactionMap, txid)
    78  			}
    79  		}
    80  
    81  		// Remove the miner payout transaction if applicable.
    82  		for _, mp := range block.MinerPayouts {
    83  			_, exists := w.keys[mp.UnlockHash]
    84  			if exists {
    85  				w.processedTransactions = w.processedTransactions[:len(w.processedTransactions)-1]
    86  				delete(w.processedTransactionMap, types.TransactionID(block.ID()))
    87  				break
    88  			}
    89  		}
    90  		w.consensusSetHeight--
    91  	}
    92  }
    93  
    94  // applyHistory applies any transaction history that was introduced by the
    95  // applied blocks.
    96  func (w *Wallet) applyHistory(cc modules.ConsensusChange) {
    97  	for _, block := range cc.AppliedBlocks {
    98  		w.consensusSetHeight++
    99  		// Apply the miner payout transaction if applicable.
   100  		minerPT := modules.ProcessedTransaction{
   101  			Transaction:           types.Transaction{},
   102  			TransactionID:         types.TransactionID(block.ID()),
   103  			ConfirmationHeight:    w.consensusSetHeight,
   104  			ConfirmationTimestamp: block.Timestamp,
   105  		}
   106  		relevant := false
   107  		for i, mp := range block.MinerPayouts {
   108  			_, exists := w.keys[mp.UnlockHash]
   109  			if exists {
   110  				relevant = true
   111  			}
   112  			minerPT.Outputs = append(minerPT.Outputs, modules.ProcessedOutput{
   113  				FundType:       types.SpecifierMinerPayout,
   114  				MaturityHeight: w.consensusSetHeight + types.MaturityDelay,
   115  				WalletAddress:  exists,
   116  				RelatedAddress: mp.UnlockHash,
   117  				Value:          mp.Value,
   118  			})
   119  			w.historicOutputs[types.OutputID(block.MinerPayoutID(uint64(i)))] = mp.Value
   120  		}
   121  		if relevant {
   122  			w.processedTransactions = append(w.processedTransactions, minerPT)
   123  			w.processedTransactionMap[minerPT.TransactionID] = &w.processedTransactions[len(w.processedTransactions)-1]
   124  		}
   125  		for _, txn := range block.Transactions {
   126  			relevant := false
   127  			pt := modules.ProcessedTransaction{
   128  				Transaction:           txn,
   129  				TransactionID:         txn.ID(),
   130  				ConfirmationHeight:    w.consensusSetHeight,
   131  				ConfirmationTimestamp: block.Timestamp,
   132  			}
   133  			for _, sci := range txn.SiacoinInputs {
   134  				_, exists := w.keys[sci.UnlockConditions.UnlockHash()]
   135  				if exists {
   136  					relevant = true
   137  				}
   138  				pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
   139  					FundType:       types.SpecifierSiacoinInput,
   140  					WalletAddress:  exists,
   141  					RelatedAddress: sci.UnlockConditions.UnlockHash(),
   142  					Value:          w.historicOutputs[types.OutputID(sci.ParentID)],
   143  				})
   144  			}
   145  			for i, sco := range txn.SiacoinOutputs {
   146  				_, exists := w.keys[sco.UnlockHash]
   147  				if exists {
   148  					relevant = true
   149  				}
   150  				pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
   151  					FundType:       types.SpecifierSiacoinOutput,
   152  					MaturityHeight: w.consensusSetHeight,
   153  					WalletAddress:  exists,
   154  					RelatedAddress: sco.UnlockHash,
   155  					Value:          sco.Value,
   156  				})
   157  				w.historicOutputs[types.OutputID(txn.SiacoinOutputID(uint64(i)))] = sco.Value
   158  			}
   159  			for _, sfi := range txn.SiafundInputs {
   160  				_, exists := w.keys[sfi.UnlockConditions.UnlockHash()]
   161  				if exists {
   162  					relevant = true
   163  				}
   164  				sfiValue := w.historicOutputs[types.OutputID(sfi.ParentID)]
   165  				pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
   166  					FundType:       types.SpecifierSiafundInput,
   167  					WalletAddress:  exists,
   168  					RelatedAddress: sfi.UnlockConditions.UnlockHash(),
   169  					Value:          sfiValue,
   170  				})
   171  				claimValue := w.siafundPool.Sub(w.historicClaimStarts[sfi.ParentID]).Mul(sfiValue)
   172  				pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
   173  					FundType:       types.SpecifierClaimOutput,
   174  					MaturityHeight: w.consensusSetHeight + types.MaturityDelay,
   175  					WalletAddress:  exists,
   176  					RelatedAddress: sfi.ClaimUnlockHash,
   177  					Value:          claimValue,
   178  				})
   179  			}
   180  			for i, sfo := range txn.SiafundOutputs {
   181  				_, exists := w.keys[sfo.UnlockHash]
   182  				if exists {
   183  					relevant = true
   184  				}
   185  				pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
   186  					FundType:       types.SpecifierSiafundOutput,
   187  					MaturityHeight: w.consensusSetHeight,
   188  					WalletAddress:  exists,
   189  					RelatedAddress: sfo.UnlockHash,
   190  					Value:          sfo.Value,
   191  				})
   192  				w.historicOutputs[types.OutputID(txn.SiafundOutputID(uint64(i)))] = sfo.Value
   193  				w.historicClaimStarts[txn.SiafundOutputID(uint64(i))] = sfo.ClaimStart
   194  			}
   195  			for _, fee := range txn.MinerFees {
   196  				pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
   197  					FundType: types.SpecifierMinerFee,
   198  					Value:    fee,
   199  				})
   200  			}
   201  			if relevant {
   202  				w.processedTransactions = append(w.processedTransactions, pt)
   203  				w.processedTransactionMap[pt.TransactionID] = &w.processedTransactions[len(w.processedTransactions)-1]
   204  			}
   205  		}
   206  	}
   207  }
   208  
   209  // ProcessConsensusChange parses a consensus change to update the set of
   210  // confirmed outputs known to the wallet.
   211  func (w *Wallet) ProcessConsensusChange(cc modules.ConsensusChange) {
   212  	w.mu.Lock()
   213  	defer w.mu.Unlock()
   214  	w.updateConfirmedSet(cc)
   215  	w.revertHistory(cc)
   216  	w.applyHistory(cc)
   217  }
   218  
   219  // ReceiveUpdatedUnconfirmedTransactions updates the wallet's unconfirmed
   220  // transaction set.
   221  func (w *Wallet) ReceiveUpdatedUnconfirmedTransactions(txns []types.Transaction, _ modules.ConsensusChange) {
   222  	w.mu.Lock()
   223  	defer w.mu.Unlock()
   224  
   225  	w.unconfirmedProcessedTransactions = nil
   226  	for _, txn := range txns {
   227  		// To save on  code complexity, relveancy is determined while building
   228  		// up the wallet transaction.
   229  		relevant := false
   230  		pt := modules.ProcessedTransaction{
   231  			Transaction:           txn,
   232  			TransactionID:         txn.ID(),
   233  			ConfirmationHeight:    types.BlockHeight(math.MaxUint64),
   234  			ConfirmationTimestamp: types.Timestamp(math.MaxUint64),
   235  		}
   236  		for _, sci := range txn.SiacoinInputs {
   237  			_, exists := w.keys[sci.UnlockConditions.UnlockHash()]
   238  			if exists {
   239  				relevant = true
   240  			}
   241  			pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
   242  				FundType:       types.SpecifierSiacoinInput,
   243  				WalletAddress:  exists,
   244  				RelatedAddress: sci.UnlockConditions.UnlockHash(),
   245  				Value:          w.historicOutputs[types.OutputID(sci.ParentID)],
   246  			})
   247  		}
   248  		for i, sco := range txn.SiacoinOutputs {
   249  			_, exists := w.keys[sco.UnlockHash]
   250  			if exists {
   251  				relevant = true
   252  			}
   253  			pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
   254  				FundType:       types.SpecifierSiacoinOutput,
   255  				MaturityHeight: types.BlockHeight(math.MaxUint64),
   256  				WalletAddress:  exists,
   257  				RelatedAddress: sco.UnlockHash,
   258  				Value:          sco.Value,
   259  			})
   260  			w.historicOutputs[types.OutputID(txn.SiacoinOutputID(uint64(i)))] = sco.Value
   261  		}
   262  		for _, fee := range txn.MinerFees {
   263  			pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
   264  				FundType: types.SpecifierMinerFee,
   265  				Value:    fee,
   266  			})
   267  		}
   268  		if relevant {
   269  			w.unconfirmedProcessedTransactions = append(w.unconfirmedProcessedTransactions, pt)
   270  		}
   271  	}
   272  }