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

     1  package transactionpool
     2  
     3  // TODO: It seems like the transaction pool is not properly detecting conflicts
     4  // between a file contract revision and a file contract.
     5  
     6  import (
     7  	"errors"
     8  
     9  	"github.com/NebulousLabs/Sia/crypto"
    10  	"github.com/NebulousLabs/Sia/encoding"
    11  	"github.com/NebulousLabs/Sia/modules"
    12  	"github.com/NebulousLabs/Sia/types"
    13  
    14  	"github.com/NebulousLabs/bolt"
    15  )
    16  
    17  const (
    18  	// The TransactionPoolSizeLimit is first checked, and then a transaction
    19  	// set is added. The current transaction pool does not do any priority
    20  	// ordering, so the size limit is such that the transaction pool will never
    21  	// exceed the size of a block.
    22  	//
    23  	// TODO: Add a priority structure that will allow the transaction pool to
    24  	// fill up beyond the size of a single block, without being subject to
    25  	// manipulation.
    26  	//
    27  	// The first ~1/4 of the transaction pool can be filled for free. This is
    28  	// mostly to preserve compatibility with clients that do not add fees.
    29  	TransactionPoolSizeLimit  = 2e6 - 5e3 - modules.TransactionSetSizeLimit
    30  	TransactionPoolSizeForFee = 500e3
    31  )
    32  
    33  var (
    34  	errObjectConflict      = errors.New("transaction set conflicts with an existing transaction set")
    35  	errFullTransactionPool = errors.New("transaction pool cannot accept more transactions")
    36  	errLowMinerFees        = errors.New("transaction set needs more miner fees to be accepted")
    37  	errEmptySet            = errors.New("transaction set is empty")
    38  
    39  	TransactionMinFee = types.SiacoinPrecision.Mul64(2)
    40  )
    41  
    42  // relatedObjectIDs determines all of the object ids related to a transaction.
    43  func relatedObjectIDs(ts []types.Transaction) []ObjectID {
    44  	oidMap := make(map[ObjectID]struct{})
    45  	for _, t := range ts {
    46  		for _, sci := range t.SiacoinInputs {
    47  			oidMap[ObjectID(sci.ParentID)] = struct{}{}
    48  		}
    49  		for i := range t.SiacoinOutputs {
    50  			oidMap[ObjectID(t.SiacoinOutputID(uint64(i)))] = struct{}{}
    51  		}
    52  		for i := range t.FileContracts {
    53  			oidMap[ObjectID(t.FileContractID(uint64(i)))] = struct{}{}
    54  		}
    55  		for _, fcr := range t.FileContractRevisions {
    56  			oidMap[ObjectID(fcr.ParentID)] = struct{}{}
    57  		}
    58  		for _, sp := range t.StorageProofs {
    59  			oidMap[ObjectID(sp.ParentID)] = struct{}{}
    60  		}
    61  		for _, sfi := range t.SiafundInputs {
    62  			oidMap[ObjectID(sfi.ParentID)] = struct{}{}
    63  		}
    64  		for i := range t.SiafundOutputs {
    65  			oidMap[ObjectID(t.SiafundOutputID(uint64(i)))] = struct{}{}
    66  		}
    67  	}
    68  
    69  	var oids []ObjectID
    70  	for oid := range oidMap {
    71  		oids = append(oids, oid)
    72  	}
    73  	return oids
    74  }
    75  
    76  // checkMinerFees checks that the total amount of transaction fees in the
    77  // transaction set is sufficient to earn a spot in the transaction pool.
    78  func (tp *TransactionPool) checkMinerFees(ts []types.Transaction) error {
    79  	// Transactions cannot be added after the TransactionPoolSizeLimit has been
    80  	// hit.
    81  	if tp.transactionListSize > TransactionPoolSizeLimit {
    82  		return errFullTransactionPool
    83  	}
    84  
    85  	// The first TransactionPoolSizeForFee transactions do not need fees.
    86  	if tp.transactionListSize > TransactionPoolSizeForFee {
    87  		// Currently required fees are set on a per-transaction basis. 2 coins
    88  		// are required per transaction if the free-fee limit has been reached,
    89  		// adding a larger fee is not useful.
    90  		var feeSum types.Currency
    91  		for i := range ts {
    92  			for _, fee := range ts[i].MinerFees {
    93  				feeSum = feeSum.Add(fee)
    94  			}
    95  		}
    96  		feeRequired := TransactionMinFee.Mul64(uint64(len(ts)))
    97  		if feeSum.Cmp(feeRequired) < 0 {
    98  			return errLowMinerFees
    99  		}
   100  	}
   101  	return nil
   102  }
   103  
   104  // checkTransactionSetComposition checks if the transaction set is valid given
   105  // the state of the pool. It does not check that each individual transaction
   106  // would be legal in the next block, but does check things like miner fees and
   107  // IsStandard.
   108  func (tp *TransactionPool) checkTransactionSetComposition(ts []types.Transaction) error {
   109  	// Check that the transaction set is not already known.
   110  	setID := TransactionSetID(crypto.HashObject(ts))
   111  	_, exists := tp.transactionSets[setID]
   112  	if exists {
   113  		return modules.ErrDuplicateTransactionSet
   114  	}
   115  
   116  	// Check that the transaction set has enough fees to justify adding it to
   117  	// the transaction list.
   118  	err := tp.checkMinerFees(ts)
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	// All checks after this are expensive.
   124  	//
   125  	// TODO: There is no DoS prevention mechanism in place to prevent repeated
   126  	// expensive verifications of invalid transactions that are created on the
   127  	// fly.
   128  
   129  	// Check that all transactions follow 'Standard.md' guidelines.
   130  	err = tp.IsStandardTransactionSet(ts)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	return nil
   135  }
   136  
   137  // handleConflicts detects whether the conflicts in the transaction pool are
   138  // legal children of the new transaction pool set or not.
   139  func (tp *TransactionPool) handleConflicts(ts []types.Transaction, conflicts []TransactionSetID) error {
   140  	// Create a list of all the transaction ids that compose the set of
   141  	// conflicts.
   142  	conflictMap := make(map[types.TransactionID]TransactionSetID)
   143  	for _, conflict := range conflicts {
   144  		conflictSet := tp.transactionSets[conflict]
   145  		for _, conflictTxn := range conflictSet {
   146  			conflictMap[conflictTxn.ID()] = conflict
   147  		}
   148  	}
   149  
   150  	// Discard all duplicate transactions from the input transaction set.
   151  	var dedupSet []types.Transaction
   152  	for _, t := range ts {
   153  		_, exists := conflictMap[t.ID()]
   154  		if exists {
   155  			continue
   156  		}
   157  		dedupSet = append(dedupSet, t)
   158  	}
   159  	if len(dedupSet) == 0 {
   160  		return modules.ErrDuplicateTransactionSet
   161  	}
   162  	// If transactions were pruned, it's possible that the set of
   163  	// dependencies/conflicts has also reduced. To minimize computational load
   164  	// on the consensus set, we want to prune out all of the conflicts that are
   165  	// no longer relevant. As an example, consider the transaction set {A}, the
   166  	// set {B}, and the new set {A, C}, where C is dependent on B. {A} and {B}
   167  	// are both conflicts, but after deduplication {A} is no longer a conflict.
   168  	// This is recursive, but it is guaranteed to run only once as the first
   169  	// deduplication is guaranteed to be complete.
   170  	if len(dedupSet) < len(ts) {
   171  		oids := relatedObjectIDs(dedupSet)
   172  		var conflicts []TransactionSetID
   173  		for _, oid := range oids {
   174  			conflict, exists := tp.knownObjects[oid]
   175  			if exists {
   176  				conflicts = append(conflicts, conflict)
   177  			}
   178  		}
   179  		return tp.handleConflicts(dedupSet, conflicts)
   180  	}
   181  
   182  	// Merge all of the conflict sets with the input set (input set goes last
   183  	// to preserve dependency ordering), and see if the set as a whole is both
   184  	// small enough to be legal and valid as a set. If no, return an error. If
   185  	// yes, add the new set to the pool, and eliminate the old set. The output
   186  	// diff objects can be repeated, (no need to remove those). Just need to
   187  	// remove the conflicts from tp.transactionSets.
   188  	var superset []types.Transaction
   189  	supersetMap := make(map[TransactionSetID]struct{})
   190  	for _, conflict := range conflictMap {
   191  		supersetMap[conflict] = struct{}{}
   192  	}
   193  	for conflict := range supersetMap {
   194  		superset = append(superset, tp.transactionSets[conflict]...)
   195  	}
   196  	superset = append(superset, dedupSet...)
   197  
   198  	// Check the composition of the transaction set, including fees and
   199  	// IsStandard rules (this is a new set, the rules must be rechecked).
   200  	err := tp.checkTransactionSetComposition(superset)
   201  	if err != nil {
   202  		return err
   203  	}
   204  
   205  	// Check that the transaction set is valid.
   206  	cc, err := tp.consensusSet.TryTransactionSet(superset)
   207  	if err != nil {
   208  		return modules.NewConsensusConflict(err.Error())
   209  	}
   210  
   211  	// Remove the conflicts from the transaction pool. The diffs do not need to
   212  	// be removed, they will be overwritten later in the function.
   213  	for _, conflict := range conflictMap {
   214  		conflictSet := tp.transactionSets[conflict]
   215  		tp.transactionListSize -= len(encoding.Marshal(conflictSet))
   216  		delete(tp.transactionSets, conflict)
   217  		delete(tp.transactionSetDiffs, conflict)
   218  	}
   219  
   220  	// Add the transaction set to the pool.
   221  	setID := TransactionSetID(crypto.HashObject(superset))
   222  	tp.transactionSets[setID] = superset
   223  	for _, diff := range cc.SiacoinOutputDiffs {
   224  		tp.knownObjects[ObjectID(diff.ID)] = setID
   225  	}
   226  	for _, diff := range cc.FileContractDiffs {
   227  		tp.knownObjects[ObjectID(diff.ID)] = setID
   228  	}
   229  	for _, diff := range cc.SiafundOutputDiffs {
   230  		tp.knownObjects[ObjectID(diff.ID)] = setID
   231  	}
   232  	tp.transactionSetDiffs[setID] = cc
   233  	tp.transactionListSize += len(encoding.Marshal(superset))
   234  	return nil
   235  }
   236  
   237  // acceptTransactionSet verifies that a transaction set is allowed to be in the
   238  // transaction pool, and then adds it to the transaction pool.
   239  func (tp *TransactionPool) acceptTransactionSet(ts []types.Transaction) error {
   240  	if len(ts) == 0 {
   241  		return errEmptySet
   242  	}
   243  
   244  	// Remove all transactions that have been confirmed in the transaction set.
   245  	err := tp.db.Update(func(tx *bolt.Tx) error {
   246  		oldTS := ts
   247  		ts = []types.Transaction{}
   248  		for _, txn := range oldTS {
   249  			if !tp.transactionConfirmed(tx, txn.ID()) {
   250  				ts = append(ts, txn)
   251  			}
   252  		}
   253  		return nil
   254  	})
   255  	if err != nil {
   256  		return err
   257  	}
   258  	// If no transactions remain, return a dublicate error.
   259  	if len(ts) == 0 {
   260  		return modules.ErrDuplicateTransactionSet
   261  	}
   262  
   263  	// Check the composition of the transaction set, including fees and
   264  	// IsStandard rules.
   265  	err = tp.checkTransactionSetComposition(ts)
   266  	if err != nil {
   267  		return err
   268  	}
   269  
   270  	// Check for conflicts with other transactions, which would indicate a
   271  	// double-spend. Legal children of a transaction set will also trigger the
   272  	// conflict-detector.
   273  	oids := relatedObjectIDs(ts)
   274  	var conflicts []TransactionSetID
   275  	for _, oid := range oids {
   276  		conflict, exists := tp.knownObjects[oid]
   277  		if exists {
   278  			conflicts = append(conflicts, conflict)
   279  		}
   280  	}
   281  	if len(conflicts) > 0 {
   282  		return tp.handleConflicts(ts, conflicts)
   283  	}
   284  	cc, err := tp.consensusSet.TryTransactionSet(ts)
   285  	if err != nil {
   286  		return modules.NewConsensusConflict(err.Error())
   287  	}
   288  
   289  	// Add the transaction set to the pool.
   290  	setID := TransactionSetID(crypto.HashObject(ts))
   291  	tp.transactionSets[setID] = ts
   292  	for _, oid := range oids {
   293  		tp.knownObjects[oid] = setID
   294  	}
   295  	tp.transactionSetDiffs[setID] = cc
   296  	tp.transactionListSize += len(encoding.Marshal(ts))
   297  	return nil
   298  }
   299  
   300  // AcceptTransaction adds a transaction to the unconfirmed set of
   301  // transactions. If the transaction is accepted, it will be relayed to
   302  // connected peers.
   303  func (tp *TransactionPool) AcceptTransactionSet(ts []types.Transaction) error {
   304  	tp.mu.Lock()
   305  	defer tp.mu.Unlock()
   306  
   307  	err := tp.acceptTransactionSet(ts)
   308  	if err != nil {
   309  		return err
   310  	}
   311  
   312  	// Notify subscribers and broadcast the transaction set.
   313  	go tp.gateway.Broadcast("RelayTransactionSet", ts, tp.gateway.Peers())
   314  	tp.updateSubscribersTransactions()
   315  	return nil
   316  }
   317  
   318  // relayTransactionSet is an RPC that accepts a transaction set from a peer. If
   319  // the accept is successful, the transaction will be relayed to the gateway's
   320  // other peers.
   321  func (tp *TransactionPool) relayTransactionSet(conn modules.PeerConn) error {
   322  	var ts []types.Transaction
   323  	err := encoding.ReadObject(conn, &ts, types.BlockSizeLimit)
   324  	if err != nil {
   325  		return err
   326  	}
   327  	return tp.AcceptTransactionSet(ts)
   328  }