gitlab.com/jokerrs1/Sia@v1.3.2/modules/wallet/transactions.go (about)

     1  package wallet
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  	"sort"
     8  
     9  	"github.com/NebulousLabs/Sia/build"
    10  	"github.com/NebulousLabs/Sia/encoding"
    11  	"github.com/NebulousLabs/Sia/modules"
    12  	"github.com/NebulousLabs/Sia/types"
    13  )
    14  
    15  var (
    16  	errOutOfBounds = errors.New("requesting transactions at unknown confirmation heights")
    17  )
    18  
    19  // AddressTransactions returns all of the wallet transactions associated with a
    20  // single unlock hash.
    21  func (w *Wallet) AddressTransactions(uh types.UnlockHash) (pts []modules.ProcessedTransaction) {
    22  	// ensure durability of reported transactions
    23  	w.mu.Lock()
    24  	defer w.mu.Unlock()
    25  	w.syncDB()
    26  
    27  	txnIndices, _ := dbGetAddrTransactions(w.dbTx, uh)
    28  	for _, i := range txnIndices {
    29  		pt, err := dbGetProcessedTransaction(w.dbTx, i)
    30  		if err != nil {
    31  			continue
    32  		}
    33  		pts = append(pts, pt)
    34  	}
    35  	return pts
    36  }
    37  
    38  // AddressUnconfirmedTransactions returns all of the unconfirmed wallet transactions
    39  // related to a specific address.
    40  func (w *Wallet) AddressUnconfirmedTransactions(uh types.UnlockHash) (pts []modules.ProcessedTransaction) {
    41  	// ensure durability of reported transactions
    42  	w.mu.Lock()
    43  	defer w.mu.Unlock()
    44  	w.syncDB()
    45  
    46  	// Scan the full list of unconfirmed transactions to see if there are any
    47  	// related transactions.
    48  	for _, pt := range w.unconfirmedProcessedTransactions {
    49  		relevant := false
    50  		for _, input := range pt.Inputs {
    51  			if input.RelatedAddress == uh {
    52  				relevant = true
    53  				break
    54  			}
    55  		}
    56  		for _, output := range pt.Outputs {
    57  			if output.RelatedAddress == uh {
    58  				relevant = true
    59  				break
    60  			}
    61  		}
    62  		if relevant {
    63  			pts = append(pts, pt)
    64  		}
    65  	}
    66  	return pts
    67  }
    68  
    69  // Transaction returns the transaction with the given id. 'False' is returned
    70  // if the transaction does not exist.
    71  func (w *Wallet) Transaction(txid types.TransactionID) (pt modules.ProcessedTransaction, found bool) {
    72  	// ensure durability of reported transaction
    73  	w.mu.Lock()
    74  	defer w.mu.Unlock()
    75  	w.syncDB()
    76  
    77  	// Get the keyBytes for the given txid
    78  	keyBytes, err := dbGetTransactionIndex(w.dbTx, txid)
    79  	if err != nil {
    80  		return modules.ProcessedTransaction{}, false
    81  	}
    82  
    83  	// Retrieve the transaction
    84  	found = encoding.Unmarshal(w.dbTx.Bucket(bucketProcessedTransactions).Get(keyBytes), &pt) == nil
    85  	return
    86  }
    87  
    88  // Transactions returns all transactions relevant to the wallet that were
    89  // confirmed in the range [startHeight, endHeight].
    90  func (w *Wallet) Transactions(startHeight, endHeight types.BlockHeight) (pts []modules.ProcessedTransaction, err error) {
    91  	// ensure durability of reported transactions
    92  	w.mu.Lock()
    93  	defer w.mu.Unlock()
    94  	w.syncDB()
    95  
    96  	height, err := dbGetConsensusHeight(w.dbTx)
    97  	if err != nil {
    98  		return
    99  	} else if startHeight > height || startHeight > endHeight {
   100  		return nil, errOutOfBounds
   101  	}
   102  
   103  	// Get the bucket, the largest key in it and the cursor
   104  	bucket := w.dbTx.Bucket(bucketProcessedTransactions)
   105  	cursor := bucket.Cursor()
   106  	nextKey := bucket.Sequence() + 1
   107  
   108  	// Database is empty
   109  	if nextKey == 1 {
   110  		return
   111  	}
   112  
   113  	var pt modules.ProcessedTransaction
   114  	keyBytes := make([]byte, 8)
   115  	var result int
   116  	func() {
   117  		// Recover from possible panic during binary search
   118  		defer func() {
   119  			r := recover()
   120  			if r != nil {
   121  				err = fmt.Errorf("%v", r)
   122  			}
   123  		}()
   124  
   125  		// Start binary searching
   126  		result = sort.Search(int(nextKey), func(i int) bool {
   127  			// Create the key for the index
   128  			binary.BigEndian.PutUint64(keyBytes, uint64(i))
   129  
   130  			// Retrieve the processed transaction
   131  			key, ptBytes := cursor.Seek(keyBytes)
   132  			if build.DEBUG && key == nil {
   133  				panic("Failed to retrieve processed Transaction by key")
   134  			}
   135  
   136  			// Decode the transaction
   137  			if err = decodeProcessedTransaction(ptBytes, &pt); build.DEBUG && err != nil {
   138  				panic(err)
   139  			}
   140  
   141  			return pt.ConfirmationHeight >= startHeight
   142  		})
   143  	}()
   144  	if err != nil {
   145  		return
   146  	}
   147  
   148  	if uint64(result) == nextKey {
   149  		// No transaction was found
   150  		return
   151  	}
   152  
   153  	// Create the key that corresponds to the result of the search
   154  	binary.BigEndian.PutUint64(keyBytes, uint64(result))
   155  
   156  	// Get the processed transaction and decode it
   157  	key, ptBytes := cursor.Seek(keyBytes)
   158  	if build.DEBUG && key == nil {
   159  		build.Critical("Couldn't find the processed transaction from the search.")
   160  	}
   161  	if err = decodeProcessedTransaction(ptBytes, &pt); build.DEBUG && err != nil {
   162  		build.Critical(err)
   163  	}
   164  
   165  	// Gather all transactions until endHeight is reached
   166  	for pt.ConfirmationHeight <= endHeight {
   167  		if build.DEBUG && pt.ConfirmationHeight < startHeight {
   168  			build.Critical("wallet processed transactions are not sorted")
   169  		}
   170  		pts = append(pts, pt)
   171  
   172  		// Get next processed transaction
   173  		key, ptBytes := cursor.Next()
   174  		if key == nil {
   175  			break
   176  		}
   177  
   178  		// Decode the transaction
   179  		if err := decodeProcessedTransaction(ptBytes, &pt); build.DEBUG && err != nil {
   180  			panic("Failed to decode the processed transaction")
   181  		}
   182  	}
   183  	return
   184  }
   185  
   186  // UnconfirmedTransactions returns the set of unconfirmed transactions that are
   187  // relevant to the wallet.
   188  func (w *Wallet) UnconfirmedTransactions() []modules.ProcessedTransaction {
   189  	w.mu.RLock()
   190  	defer w.mu.RUnlock()
   191  	return w.unconfirmedProcessedTransactions
   192  }