|
20 | 20 | //! identifying and traversing conflicts and descendants of a given transaction. Some [`TxGraph`]
|
21 | 21 | //! methods only consider transactions that are "canonical" (i.e., in the best chain or in mempool).
|
22 | 22 | //! We decide which transactions are canonical based on the transaction's anchors and the
|
23 |
| -//! `last_seen` (as unconfirmed) timestamp; see the [`try_get_chain_position`] documentation for |
24 |
| -//! more details. |
| 23 | +//! `last_seen` (as unconfirmed) timestamp. |
25 | 24 | //!
|
26 | 25 | //! The [`ChangeSet`] reports changes made to a [`TxGraph`]; it can be used to either save to
|
27 | 26 | //! persistent storage, or to be applied to another [`TxGraph`].
|
|
89 | 88 | //! let changeset = graph.apply_update(update);
|
90 | 89 | //! assert!(changeset.is_empty());
|
91 | 90 | //! ```
|
92 |
| -//! [`try_get_chain_position`]: TxGraph::try_get_chain_position |
93 | 91 | //! [`insert_txout`]: TxGraph::insert_txout
|
94 | 92 |
|
95 | 93 | use crate::collections::*;
|
@@ -739,217 +737,6 @@ impl<A: Clone + Ord> TxGraph<A> {
|
739 | 737 | }
|
740 | 738 |
|
741 | 739 | impl<A: Anchor> TxGraph<A> {
|
742 |
| - /// Get the position of the transaction in `chain` with tip `chain_tip`. |
743 |
| - /// |
744 |
| - /// Chain data is fetched from `chain`, a [`ChainOracle`] implementation. |
745 |
| - /// |
746 |
| - /// This method returns `Ok(None)` if the transaction is not found in the chain, and no longer |
747 |
| - /// belongs in the mempool. The following factors are used to approximate whether an |
748 |
| - /// unconfirmed transaction exists in the mempool (not evicted): |
749 |
| - /// |
750 |
| - /// 1. Unconfirmed transactions that conflict with confirmed transactions are evicted. |
751 |
| - /// 2. Unconfirmed transactions that spend from transactions that are evicted, are also |
752 |
| - /// evicted. |
753 |
| - /// 3. Given two conflicting unconfirmed transactions, the transaction with the lower |
754 |
| - /// `last_seen_unconfirmed` parameter is evicted. A transaction's `last_seen_unconfirmed` |
755 |
| - /// parameter is the max of all it's descendants' `last_seen_unconfirmed` parameters. If the |
756 |
| - /// final `last_seen_unconfirmed`s are the same, the transaction with the lower `txid` (by |
757 |
| - /// lexicographical order) is evicted. |
758 |
| - /// |
759 |
| - /// # Error |
760 |
| - /// |
761 |
| - /// An error will occur if the [`ChainOracle`] implementation (`chain`) fails. If the |
762 |
| - /// [`ChainOracle`] is infallible, [`get_chain_position`] can be used instead. |
763 |
| - /// |
764 |
| - /// [`get_chain_position`]: Self::get_chain_position |
765 |
| - pub fn try_get_chain_position<C: ChainOracle>( |
766 |
| - &self, |
767 |
| - chain: &C, |
768 |
| - chain_tip: BlockId, |
769 |
| - txid: Txid, |
770 |
| - ) -> Result<Option<ChainPosition<&A>>, C::Error> { |
771 |
| - let tx_node = match self.txs.get(&txid) { |
772 |
| - Some(v) => v, |
773 |
| - None => return Ok(None), |
774 |
| - }; |
775 |
| - |
776 |
| - for anchor in self.anchors.get(&txid).unwrap_or(&self.empty_anchors) { |
777 |
| - match chain.is_block_in_chain(anchor.anchor_block(), chain_tip)? { |
778 |
| - Some(true) => return Ok(Some(ChainPosition::Confirmed(anchor))), |
779 |
| - _ => continue, |
780 |
| - } |
781 |
| - } |
782 |
| - |
783 |
| - // If no anchors are in best chain and we don't have a last_seen, we can return |
784 |
| - // early because by definition the tx doesn't have a chain position. |
785 |
| - let last_seen = match self.last_seen.get(&txid) { |
786 |
| - Some(t) => *t, |
787 |
| - None => return Ok(None), |
788 |
| - }; |
789 |
| - |
790 |
| - // The tx is not anchored to a block in the best chain, which means that it |
791 |
| - // might be in mempool, or it might have been dropped already. |
792 |
| - // Let's check conflicts to find out! |
793 |
| - let tx = match tx_node { |
794 |
| - TxNodeInternal::Whole(tx) => { |
795 |
| - // A coinbase tx that is not anchored in the best chain cannot be unconfirmed and |
796 |
| - // should always be filtered out. |
797 |
| - if tx.is_coinbase() { |
798 |
| - return Ok(None); |
799 |
| - } |
800 |
| - tx.clone() |
801 |
| - } |
802 |
| - TxNodeInternal::Partial(_) => { |
803 |
| - // Partial transactions (outputs only) cannot have conflicts. |
804 |
| - return Ok(None); |
805 |
| - } |
806 |
| - }; |
807 |
| - |
808 |
| - // We want to retrieve all the transactions that conflict with us, plus all the |
809 |
| - // transactions that conflict with our unconfirmed ancestors, since they conflict with us |
810 |
| - // as well. |
811 |
| - // We only traverse unconfirmed ancestors since conflicts of confirmed transactions |
812 |
| - // cannot be in the best chain. |
813 |
| - |
814 |
| - // First of all, we retrieve all our ancestors. Since we're using `new_include_root`, the |
815 |
| - // resulting array will also include `tx` |
816 |
| - let unconfirmed_ancestor_txs = |
817 |
| - TxAncestors::new_include_root(self, tx.clone(), |_, ancestor_tx: Arc<Transaction>| { |
818 |
| - let tx_node = self.get_tx_node(ancestor_tx.as_ref().compute_txid())?; |
819 |
| - // We're filtering the ancestors to keep only the unconfirmed ones (= no anchors in |
820 |
| - // the best chain) |
821 |
| - for block in tx_node.anchors { |
822 |
| - match chain.is_block_in_chain(block.anchor_block(), chain_tip) { |
823 |
| - Ok(Some(true)) => return None, |
824 |
| - Err(e) => return Some(Err(e)), |
825 |
| - _ => continue, |
826 |
| - } |
827 |
| - } |
828 |
| - Some(Ok(tx_node)) |
829 |
| - }) |
830 |
| - .collect::<Result<Vec<_>, C::Error>>()?; |
831 |
| - |
832 |
| - // We determine our tx's last seen, which is the max between our last seen, |
833 |
| - // and our unconf descendants' last seen. |
834 |
| - let unconfirmed_descendants_txs = TxDescendants::new_include_root( |
835 |
| - self, |
836 |
| - tx.as_ref().compute_txid(), |
837 |
| - |_, descendant_txid: Txid| { |
838 |
| - let tx_node = self.get_tx_node(descendant_txid)?; |
839 |
| - // We're filtering the ancestors to keep only the unconfirmed ones (= no anchors in |
840 |
| - // the best chain) |
841 |
| - for block in tx_node.anchors { |
842 |
| - match chain.is_block_in_chain(block.anchor_block(), chain_tip) { |
843 |
| - Ok(Some(true)) => return None, |
844 |
| - Err(e) => return Some(Err(e)), |
845 |
| - _ => continue, |
846 |
| - } |
847 |
| - } |
848 |
| - Some(Ok(tx_node)) |
849 |
| - }, |
850 |
| - ) |
851 |
| - .collect::<Result<Vec<_>, C::Error>>()?; |
852 |
| - |
853 |
| - let tx_last_seen = unconfirmed_descendants_txs |
854 |
| - .iter() |
855 |
| - .max_by_key(|tx| tx.last_seen_unconfirmed) |
856 |
| - .map(|tx| tx.last_seen_unconfirmed) |
857 |
| - .expect("descendants always includes at least one transaction (the root tx"); |
858 |
| - |
859 |
| - // Now we traverse our ancestors and consider all their conflicts |
860 |
| - for tx_node in unconfirmed_ancestor_txs { |
861 |
| - // We retrieve all the transactions conflicting with this specific ancestor |
862 |
| - let conflicting_txs = |
863 |
| - self.walk_conflicts(tx_node.tx.as_ref(), |_, txid| self.get_tx_node(txid)); |
864 |
| - |
865 |
| - // If a conflicting tx is in the best chain, or has `last_seen` higher than this ancestor, then |
866 |
| - // this tx cannot exist in the best chain |
867 |
| - for conflicting_tx in conflicting_txs { |
868 |
| - for block in conflicting_tx.anchors { |
869 |
| - if chain.is_block_in_chain(block.anchor_block(), chain_tip)? == Some(true) { |
870 |
| - return Ok(None); |
871 |
| - } |
872 |
| - } |
873 |
| - if conflicting_tx.last_seen_unconfirmed > tx_last_seen { |
874 |
| - return Ok(None); |
875 |
| - } |
876 |
| - if conflicting_tx.last_seen_unconfirmed == Some(last_seen) |
877 |
| - && conflicting_tx.as_ref().compute_txid() > tx.as_ref().compute_txid() |
878 |
| - { |
879 |
| - // Conflicting tx has priority if txid of conflicting tx > txid of original tx |
880 |
| - return Ok(None); |
881 |
| - } |
882 |
| - } |
883 |
| - } |
884 |
| - |
885 |
| - Ok(Some(ChainPosition::Unconfirmed(last_seen))) |
886 |
| - } |
887 |
| - |
888 |
| - /// Get the position of the transaction in `chain` with tip `chain_tip`. |
889 |
| - /// |
890 |
| - /// This is the infallible version of [`try_get_chain_position`]. |
891 |
| - /// |
892 |
| - /// [`try_get_chain_position`]: Self::try_get_chain_position |
893 |
| - pub fn get_chain_position<C: ChainOracle<Error = Infallible>>( |
894 |
| - &self, |
895 |
| - chain: &C, |
896 |
| - chain_tip: BlockId, |
897 |
| - txid: Txid, |
898 |
| - ) -> Option<ChainPosition<&A>> { |
899 |
| - self.try_get_chain_position(chain, chain_tip, txid) |
900 |
| - .expect("error is infallible") |
901 |
| - } |
902 |
| - |
903 |
| - /// Get the txid of the spending transaction and where the spending transaction is observed in |
904 |
| - /// the `chain` of `chain_tip`. |
905 |
| - /// |
906 |
| - /// If no in-chain transaction spends `outpoint`, `None` will be returned. |
907 |
| - /// |
908 |
| - /// # Error |
909 |
| - /// |
910 |
| - /// An error will occur only if the [`ChainOracle`] implementation (`chain`) fails. |
911 |
| - /// |
912 |
| - /// If the [`ChainOracle`] is infallible, [`get_chain_spend`] can be used instead. |
913 |
| - /// |
914 |
| - /// [`get_chain_spend`]: Self::get_chain_spend |
915 |
| - pub fn try_get_chain_spend<C: ChainOracle>( |
916 |
| - &self, |
917 |
| - chain: &C, |
918 |
| - chain_tip: BlockId, |
919 |
| - outpoint: OutPoint, |
920 |
| - ) -> Result<Option<(ChainPosition<&A>, Txid)>, C::Error> { |
921 |
| - if self |
922 |
| - .try_get_chain_position(chain, chain_tip, outpoint.txid)? |
923 |
| - .is_none() |
924 |
| - { |
925 |
| - return Ok(None); |
926 |
| - } |
927 |
| - if let Some(spends) = self.spends.get(&outpoint) { |
928 |
| - for &txid in spends { |
929 |
| - if let Some(observed_at) = self.try_get_chain_position(chain, chain_tip, txid)? { |
930 |
| - return Ok(Some((observed_at, txid))); |
931 |
| - } |
932 |
| - } |
933 |
| - } |
934 |
| - Ok(None) |
935 |
| - } |
936 |
| - |
937 |
| - /// Get the txid of the spending transaction and where the spending transaction is observed in |
938 |
| - /// the `chain` of `chain_tip`. |
939 |
| - /// |
940 |
| - /// This is the infallible version of [`try_get_chain_spend`] |
941 |
| - /// |
942 |
| - /// [`try_get_chain_spend`]: Self::try_get_chain_spend |
943 |
| - pub fn get_chain_spend<C: ChainOracle<Error = Infallible>>( |
944 |
| - &self, |
945 |
| - chain: &C, |
946 |
| - static_block: BlockId, |
947 |
| - outpoint: OutPoint, |
948 |
| - ) -> Option<(ChainPosition<&A>, Txid)> { |
949 |
| - self.try_get_chain_spend(chain, static_block, outpoint) |
950 |
| - .expect("error is infallible") |
951 |
| - } |
952 |
| - |
953 | 740 | /// List graph transactions that are in `chain` with `chain_tip`.
|
954 | 741 | ///
|
955 | 742 | /// Each transaction is represented as a [`CanonicalTx`] that contains where the transaction is
|
|
0 commit comments