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