github.com/ZuluSpl0it/Sia@v1.3.7/modules/transactionpool/transactionpool.go (about)

     1  package transactionpool
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/NebulousLabs/demotemutex"
     7  	"github.com/coreos/bbolt"
     8  
     9  	"github.com/NebulousLabs/Sia/crypto"
    10  	"github.com/NebulousLabs/Sia/modules"
    11  	"github.com/NebulousLabs/Sia/persist"
    12  	"github.com/NebulousLabs/Sia/sync"
    13  	"github.com/NebulousLabs/Sia/types"
    14  )
    15  
    16  var (
    17  	errNilCS      = errors.New("transaction pool cannot initialize with a nil consensus set")
    18  	errNilGateway = errors.New("transaction pool cannot initialize with a nil gateway")
    19  )
    20  
    21  type (
    22  	// ObjectID is the ID of an object such as siacoin output and file
    23  	// contracts, and is used to see if there is are conflicts or overlaps within
    24  	// the transaction pool.
    25  	ObjectID crypto.Hash
    26  	// TransactionSetID is the hash of a transaction set.
    27  	TransactionSetID crypto.Hash
    28  
    29  	// The TransactionPool tracks incoming transactions, accepting them or
    30  	// rejecting them based on internal criteria such as fees and unconfirmed
    31  	// double spends.
    32  	TransactionPool struct {
    33  		// Dependencies of the transaction pool.
    34  		consensusSet modules.ConsensusSet
    35  		gateway      modules.Gateway
    36  
    37  		// To prevent double spends in the unconfirmed transaction set, the
    38  		// transaction pool keeps a list of all objects that have either been
    39  		// created or consumed by the current unconfirmed transaction pool. All
    40  		// transactions with overlaps are rejected. This model is
    41  		// over-aggressive - one transaction set may create an object that
    42  		// another transaction set spends. This is done to minimize the
    43  		// computation and memory load on the transaction pool. Dependent
    44  		// transactions should be lumped into a single transaction set.
    45  		//
    46  		// transactionSetDiffs map form a transaction set id to the set of
    47  		// diffs that resulted from the transaction set.
    48  		knownObjects        map[ObjectID]TransactionSetID
    49  		subscriberSets      map[TransactionSetID]*modules.UnconfirmedTransactionSet
    50  		transactionHeights  map[types.TransactionID]types.BlockHeight
    51  		transactionSets     map[TransactionSetID][]types.Transaction
    52  		transactionSetDiffs map[TransactionSetID]*modules.ConsensusChange
    53  		transactionListSize int
    54  
    55  		// Variables related to the blockchain.
    56  		blockHeight     types.BlockHeight
    57  		recentMedians   []types.Currency
    58  		recentMedianFee types.Currency // SC per byte
    59  
    60  		// The consensus change index tracks how many consensus changes have
    61  		// been sent to the transaction pool. When a new subscriber joins the
    62  		// transaction pool, all prior consensus changes are sent to the new
    63  		// subscriber.
    64  		subscribers []modules.TransactionPoolSubscriber
    65  
    66  		// Utilities.
    67  		db         *persist.BoltDatabase
    68  		dbTx       *bolt.Tx
    69  		log        *persist.Logger
    70  		mu         demotemutex.DemoteMutex
    71  		tg         sync.ThreadGroup
    72  		persistDir string
    73  	}
    74  )
    75  
    76  // New creates a transaction pool that is ready to receive transactions.
    77  func New(cs modules.ConsensusSet, g modules.Gateway, persistDir string) (*TransactionPool, error) {
    78  	// Check that the input modules are non-nil.
    79  	if cs == nil {
    80  		return nil, errNilCS
    81  	}
    82  	if g == nil {
    83  		return nil, errNilGateway
    84  	}
    85  
    86  	// Initialize a transaction pool.
    87  	tp := &TransactionPool{
    88  		consensusSet: cs,
    89  		gateway:      g,
    90  
    91  		knownObjects:        make(map[ObjectID]TransactionSetID),
    92  		subscriberSets:      make(map[TransactionSetID]*modules.UnconfirmedTransactionSet),
    93  		transactionHeights:  make(map[types.TransactionID]types.BlockHeight),
    94  		transactionSets:     make(map[TransactionSetID][]types.Transaction),
    95  		transactionSetDiffs: make(map[TransactionSetID]*modules.ConsensusChange),
    96  
    97  		persistDir: persistDir,
    98  	}
    99  
   100  	// Open the tpool database.
   101  	err := tp.initPersist()
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	// Register RPCs
   107  	g.RegisterRPC("RelayTransactionSet", tp.relayTransactionSet)
   108  	tp.tg.OnStop(func() {
   109  		tp.gateway.UnregisterRPC("RelayTransactionSet")
   110  	})
   111  	return tp, nil
   112  }
   113  
   114  // Close releases any resources held by the transaction pool, stopping all of
   115  // its worker threads.
   116  func (tp *TransactionPool) Close() error {
   117  	return tp.tg.Stop()
   118  }
   119  
   120  // FeeEstimation returns an estimation for what fee should be applied to
   121  // transactions. It returns a minimum and maximum estimated fee per transaction
   122  // byte.
   123  func (tp *TransactionPool) FeeEstimation() (min, max types.Currency) {
   124  	err := tp.tg.Add()
   125  	if err != nil {
   126  		return
   127  	}
   128  	defer tp.tg.Done()
   129  	tp.mu.Lock()
   130  	defer tp.mu.Unlock()
   131  
   132  	// Use three methods to determine an acceptable fee, and then take the
   133  	// largest result of the two methods. The first method checks the historic
   134  	// blocks, to make sure that we don't under-estimate the number of fees
   135  	// needed in the event that we just purged the tpool.
   136  	//
   137  	// The second method looks at the existing tpool. Sudden congestion won't be
   138  	// represented on the blockchain right away, but should be immediately
   139  	// influencing how you set fees. Using the current tpool fullness will help
   140  	// pick appropriate fees in the event of sudden congestion.
   141  	//
   142  	// The third method just has hardcoded minimums as a sanity check. In the
   143  	// event of empty blocks, there should still be some fees being added to the
   144  	// chain.
   145  
   146  	// Set the minimum fee to the numbers recommended by the blockchain.
   147  	min = tp.recentMedianFee
   148  	max = tp.recentMedianFee.Mul64(maxMultiplier)
   149  
   150  	// Method two: use 'requiredFeesToExtendPool'.
   151  	required := tp.requiredFeesToExtendTpool()
   152  	requiredMin := required.MulFloat(minExtendMultiplier) // Clear the local requirement by a little bit.
   153  	requiredMax := requiredMin.MulFloat(maxMultiplier)    // Clear the local requirement by a lot.
   154  	if min.Cmp(requiredMin) < 0 {
   155  		min = requiredMin
   156  	}
   157  	if max.Cmp(requiredMax) < 0 {
   158  		max = requiredMax
   159  	}
   160  
   161  	// Method three: sane mimimums.
   162  	if min.Cmp(minEstimation) < 0 {
   163  		min = minEstimation
   164  	}
   165  	if max.Cmp(minEstimation.Mul64(maxMultiplier)) < 0 {
   166  		max = minEstimation.Mul64(maxMultiplier)
   167  	}
   168  
   169  	return
   170  }
   171  
   172  // TransactionList returns a list of all transactions in the transaction pool.
   173  // The transactions are provided in an order that can acceptably be put into a
   174  // block.
   175  func (tp *TransactionPool) TransactionList() []types.Transaction {
   176  	tp.mu.Lock()
   177  	defer tp.mu.Unlock()
   178  
   179  	var txns []types.Transaction
   180  	for _, tSet := range tp.transactionSets {
   181  		txns = append(txns, tSet...)
   182  	}
   183  	return txns
   184  }
   185  
   186  // Transaction returns the transaction with the provided txid, its parents, and
   187  // a bool indicating if it exists in the transaction pool.
   188  func (tp *TransactionPool) Transaction(id types.TransactionID) (types.Transaction, []types.Transaction, bool) {
   189  	tp.mu.Lock()
   190  	defer tp.mu.Unlock()
   191  
   192  	// find the transaction
   193  	exists := false
   194  	var txn types.Transaction
   195  	var allParents []types.Transaction
   196  	for _, tSet := range tp.transactionSets {
   197  		for i, t := range tSet {
   198  			if t.ID() == id {
   199  				txn = t
   200  				allParents = tSet[:i]
   201  				exists = true
   202  				break
   203  			}
   204  		}
   205  	}
   206  
   207  	// prune unneeded parents
   208  	parentIDs := make(map[types.OutputID]struct{})
   209  	addOutputIDs := func(txn types.Transaction) {
   210  		for _, input := range txn.SiacoinInputs {
   211  			parentIDs[types.OutputID(input.ParentID)] = struct{}{}
   212  		}
   213  		for _, fcr := range txn.FileContractRevisions {
   214  			parentIDs[types.OutputID(fcr.ParentID)] = struct{}{}
   215  		}
   216  		for _, input := range txn.SiafundInputs {
   217  			parentIDs[types.OutputID(input.ParentID)] = struct{}{}
   218  		}
   219  		for _, proof := range txn.StorageProofs {
   220  			parentIDs[types.OutputID(proof.ParentID)] = struct{}{}
   221  		}
   222  		for _, sig := range txn.TransactionSignatures {
   223  			parentIDs[types.OutputID(sig.ParentID)] = struct{}{}
   224  		}
   225  	}
   226  	isParent := func(t types.Transaction) bool {
   227  		for i := range t.SiacoinOutputs {
   228  			if _, exists := parentIDs[types.OutputID(t.SiacoinOutputID(uint64(i)))]; exists {
   229  				return true
   230  			}
   231  		}
   232  		for i := range t.FileContracts {
   233  			if _, exists := parentIDs[types.OutputID(t.SiacoinOutputID(uint64(i)))]; exists {
   234  				return true
   235  			}
   236  		}
   237  		for i := range t.SiafundOutputs {
   238  			if _, exists := parentIDs[types.OutputID(t.SiacoinOutputID(uint64(i)))]; exists {
   239  				return true
   240  			}
   241  		}
   242  		return false
   243  	}
   244  
   245  	addOutputIDs(txn)
   246  	var necessaryParents []types.Transaction
   247  	for i := len(allParents) - 1; i >= 0; i-- {
   248  		parent := allParents[i]
   249  
   250  		if isParent(parent) {
   251  			necessaryParents = append([]types.Transaction{parent}, necessaryParents...)
   252  			addOutputIDs(parent)
   253  		}
   254  	}
   255  
   256  	return txn, necessaryParents, exists
   257  }
   258  
   259  // TransactionSet returns the transaction set the provided object
   260  // appears in.
   261  func (tp *TransactionPool) TransactionSet(oid crypto.Hash) []types.Transaction {
   262  	tp.mu.RLock()
   263  	defer tp.mu.RUnlock()
   264  	var parents []types.Transaction
   265  	tSetID, exists := tp.knownObjects[ObjectID(oid)]
   266  	if !exists {
   267  		return nil
   268  	}
   269  	tSet, exists := tp.transactionSets[tSetID]
   270  	if !exists {
   271  		return nil
   272  	}
   273  	parents = append(parents, tSet...)
   274  	return parents
   275  }
   276  
   277  // Broadcast broadcasts a transaction set to all of the transaction pool's
   278  // peers.
   279  func (tp *TransactionPool) Broadcast(ts []types.Transaction) {
   280  	go tp.gateway.Broadcast("RelayTransactionSet", ts, tp.gateway.Peers())
   281  }