// acceptTransactionSet verifies that a transaction set is allowed to be in the // transaction pool, and then adds it to the transaction pool. func (tp *TransactionPool) acceptTransactionSet(ts []types.Transaction) error { if len(ts) == 0 { return errEmptySet } // Remove all transactions that have been confirmed in the transaction set. err := tp.db.Update(func(tx *bolt.Tx) error { oldTS := ts ts = []types.Transaction{} for _, txn := range oldTS { if !tp.transactionConfirmed(tx, txn.ID()) { ts = append(ts, txn) } } return nil }) if err != nil { return err } // If no transactions remain, return a dublicate error. if len(ts) == 0 { return modules.ErrDuplicateTransactionSet } // Check the composition of the transaction set, including fees and // IsStandard rules. err = tp.checkTransactionSetComposition(ts) if err != nil { return err } // Check for conflicts with other transactions, which would indicate a // double-spend. Legal children of a transaction set will also trigger the // conflict-detector. oids := relatedObjectIDs(ts) var conflicts []TransactionSetID for _, oid := range oids { conflict, exists := tp.knownObjects[oid] if exists { conflicts = append(conflicts, conflict) } } if len(conflicts) > 0 { return tp.handleConflicts(ts, conflicts) } cc, err := tp.consensusSet.TryTransactionSet(ts) if err != nil { return modules.NewConsensusConflict(err.Error()) } // Add the transaction set to the pool. setID := TransactionSetID(crypto.HashObject(ts)) tp.transactionSets[setID] = ts for _, oid := range oids { tp.knownObjects[oid] = setID } tp.transactionSetDiffs[setID] = cc tp.transactionListSize += len(encoding.Marshal(ts)) return nil }
// acceptTransactionSet verifies that a transaction set is allowed to be in the // transaction pool, and then adds it to the transaction pool. func (tp *TransactionPool) acceptTransactionSet(ts []types.Transaction) error { if len(ts) == 0 { return errEmptySet } // Check the composition of the transaction set, including fees and // IsStandard rules. err := tp.checkTransactionSetComposition(ts) if err != nil { return err } // Check for conflicts with other transactions, which would indicate a // double-spend. Legal children of a transaction set will also trigger the // conflict-detector. oids := relatedObjectIDs(ts) var conflicts []TransactionSetID for _, oid := range oids { conflict, exists := tp.knownObjects[oid] if exists { conflicts = append(conflicts, conflict) } } if len(conflicts) > 0 { return tp.handleConflicts(ts, conflicts) } cc, err := tp.consensusSet.TryTransactionSet(ts) if err != nil { return modules.NewConsensusConflict(err.Error()) } // Add the transaction set to the pool. setID := TransactionSetID(crypto.HashObject(ts)) tp.transactionSets[setID] = ts for _, oid := range oids { tp.knownObjects[oid] = setID } tp.transactionSetDiffs[setID] = cc tp.transactionListSize += len(encoding.Marshal(ts)) return nil }
// handleConflicts detects whether the conflicts in the transaction pool are // legal children of the new transaction pool set or not. func (tp *TransactionPool) handleConflicts(ts []types.Transaction, conflicts []TransactionSetID) error { // Create a list of all the transaction ids that compose the set of // conflicts. conflictMap := make(map[types.TransactionID]TransactionSetID) for _, conflict := range conflicts { conflictSet := tp.transactionSets[conflict] for _, conflictTxn := range conflictSet { conflictMap[conflictTxn.ID()] = conflict } } // Discard all duplicate transactions from the input transaction set. var dedupSet []types.Transaction for _, t := range ts { _, exists := conflictMap[t.ID()] if exists { continue } dedupSet = append(dedupSet, t) } if len(dedupSet) == 0 { return modules.ErrDuplicateTransactionSet } // If transactions were pruned, it's possible that the set of // dependencies/conflicts has also reduced. To minimize computational load // on the consensus set, we want to prune out all of the conflicts that are // no longer relevant. As an example, consider the transaction set {A}, the // set {B}, and the new set {A, C}, where C is dependent on B. {A} and {B} // are both conflicts, but after deduplication {A} is no longer a conflict. // This is recursive, but it is guaranteed to run only once as the first // deduplication is guaranteed to be complete. if len(dedupSet) < len(ts) { oids := relatedObjectIDs(dedupSet) var conflicts []TransactionSetID for _, oid := range oids { conflict, exists := tp.knownObjects[oid] if exists { conflicts = append(conflicts, conflict) } } return tp.handleConflicts(dedupSet, conflicts) } // Merge all of the conflict sets with the input set (input set goes last // to preserve dependency ordering), and see if the set as a whole is both // small enough to be legal and valid as a set. If no, return an error. If // yes, add the new set to the pool, and eliminate the old set. The output // diff objects can be repeated, (no need to remove those). Just need to // remove the conflicts from tp.transactionSets. var superset []types.Transaction supersetMap := make(map[TransactionSetID]struct{}) for _, conflict := range conflictMap { supersetMap[conflict] = struct{}{} } for conflict := range supersetMap { superset = append(superset, tp.transactionSets[conflict]...) } superset = append(superset, dedupSet...) // Check the composition of the transaction set, including fees and // IsStandard rules (this is a new set, the rules must be rechecked). err := tp.checkTransactionSetComposition(superset) if err != nil { return err } // Check that the transaction set is valid. cc, err := tp.consensusSet.TryTransactionSet(superset) if err != nil { return modules.NewConsensusConflict(err.Error()) } // Remove the conflicts from the transaction pool. The diffs do not need to // be removed, they will be overwritten later in the function. for _, conflict := range conflictMap { conflictSet := tp.transactionSets[conflict] tp.transactionListSize -= len(encoding.Marshal(conflictSet)) delete(tp.transactionSets, conflict) delete(tp.transactionSetDiffs, conflict) } // Add the transaction set to the pool. setID := TransactionSetID(crypto.HashObject(superset)) tp.transactionSets[setID] = superset for _, diff := range cc.SiacoinOutputDiffs { tp.knownObjects[ObjectID(diff.ID)] = setID } for _, diff := range cc.FileContractDiffs { tp.knownObjects[ObjectID(diff.ID)] = setID } for _, diff := range cc.SiafundOutputDiffs { tp.knownObjects[ObjectID(diff.ID)] = setID } tp.transactionSetDiffs[setID] = cc tp.transactionListSize += len(encoding.Marshal(superset)) return nil }