@@ -15,25 +15,45 @@ use crate::Anchor;
15
15
) )
16
16
) ]
17
17
pub enum ChainPosition < A > {
18
- /// The chain data is seen as confirmed, and in anchored by `A`.
19
- Confirmed ( A ) ,
20
- /// The chain data is not confirmed and last seen in the mempool at this timestamp.
21
- Unconfirmed ( u64 ) ,
18
+ /// The chain data is confirmed as it is anchored in the best chain by `A`.
19
+ Confirmed {
20
+ /// The [`Anchor`].
21
+ anchor : A ,
22
+ /// Whether the chain data is anchored transitively by a child transaction.
23
+ ///
24
+ /// If the value is `Some`, it means we have incomplete data. We can only deduce that the
25
+ /// chain data is confirmed at a block equal to or lower than the block referenced by `A`.
26
+ transitively : Option < Txid > ,
27
+ } ,
28
+ /// The chain data is not confirmed.
29
+ Unconfirmed {
30
+ /// When the chain data is last seen in the mempool.
31
+ ///
32
+ /// This value will be `None` if the chain data was never seen in the mempool and only seen
33
+ /// in a conflicting chain.
34
+ last_seen : Option < u64 > ,
35
+ } ,
22
36
}
23
37
24
38
impl < A > ChainPosition < A > {
25
39
/// Returns whether [`ChainPosition`] is confirmed or not.
26
40
pub fn is_confirmed ( & self ) -> bool {
27
- matches ! ( self , Self :: Confirmed ( _ ) )
41
+ matches ! ( self , Self :: Confirmed { .. } )
28
42
}
29
43
}
30
44
31
45
impl < A : Clone > ChainPosition < & A > {
32
46
/// Maps a [`ChainPosition<&A>`] into a [`ChainPosition<A>`] by cloning the contents.
33
47
pub fn cloned ( self ) -> ChainPosition < A > {
34
48
match self {
35
- ChainPosition :: Confirmed ( a) => ChainPosition :: Confirmed ( a. clone ( ) ) ,
36
- ChainPosition :: Unconfirmed ( last_seen) => ChainPosition :: Unconfirmed ( last_seen) ,
49
+ ChainPosition :: Confirmed {
50
+ anchor,
51
+ transitively,
52
+ } => ChainPosition :: Confirmed {
53
+ anchor : anchor. clone ( ) ,
54
+ transitively,
55
+ } ,
56
+ ChainPosition :: Unconfirmed { last_seen } => ChainPosition :: Unconfirmed { last_seen } ,
37
57
}
38
58
}
39
59
}
@@ -42,8 +62,10 @@ impl<A: Anchor> ChainPosition<A> {
42
62
/// Determines the upper bound of the confirmation height.
43
63
pub fn confirmation_height_upper_bound ( & self ) -> Option < u32 > {
44
64
match self {
45
- ChainPosition :: Confirmed ( a) => Some ( a. confirmation_height_upper_bound ( ) ) ,
46
- ChainPosition :: Unconfirmed ( _) => None ,
65
+ ChainPosition :: Confirmed { anchor, .. } => {
66
+ Some ( anchor. confirmation_height_upper_bound ( ) )
67
+ }
68
+ ChainPosition :: Unconfirmed { .. } => None ,
47
69
}
48
70
}
49
71
}
@@ -73,14 +95,14 @@ impl<A: Anchor> FullTxOut<A> {
73
95
/// [`confirmation_height_upper_bound`]: Anchor::confirmation_height_upper_bound
74
96
pub fn is_mature ( & self , tip : u32 ) -> bool {
75
97
if self . is_on_coinbase {
76
- let tx_height = match & self . chain_position {
77
- ChainPosition :: Confirmed ( anchor ) => anchor . confirmation_height_upper_bound ( ) ,
78
- ChainPosition :: Unconfirmed ( _ ) => {
98
+ let conf_height = match self . chain_position . confirmation_height_upper_bound ( ) {
99
+ Some ( height ) => height ,
100
+ None => {
79
101
debug_assert ! ( false , "coinbase tx can never be unconfirmed" ) ;
80
102
return false ;
81
103
}
82
104
} ;
83
- let age = tip. saturating_sub ( tx_height ) ;
105
+ let age = tip. saturating_sub ( conf_height ) ;
84
106
if age + 1 < COINBASE_MATURITY {
85
107
return false ;
86
108
}
@@ -103,17 +125,21 @@ impl<A: Anchor> FullTxOut<A> {
103
125
return false ;
104
126
}
105
127
106
- let confirmation_height = match & self . chain_position {
107
- ChainPosition :: Confirmed ( anchor ) => anchor . confirmation_height_upper_bound ( ) ,
108
- ChainPosition :: Unconfirmed ( _ ) => return false ,
128
+ let conf_height = match self . chain_position . confirmation_height_upper_bound ( ) {
129
+ Some ( height ) => height ,
130
+ None => return false ,
109
131
} ;
110
- if confirmation_height > tip {
132
+ if conf_height > tip {
111
133
return false ;
112
134
}
113
135
114
136
// if the spending tx is confirmed within tip height, the txout is no longer spendable
115
- if let Some ( ( ChainPosition :: Confirmed ( spending_anchor) , _) ) = & self . spent_by {
116
- if spending_anchor. anchor_block ( ) . height <= tip {
137
+ if let Some ( spend_height) = self
138
+ . spent_by
139
+ . as_ref ( )
140
+ . and_then ( |( pos, _) | pos. confirmation_height_upper_bound ( ) )
141
+ {
142
+ if spend_height <= tip {
117
143
return false ;
118
144
}
119
145
}
@@ -132,22 +158,32 @@ mod test {
132
158
133
159
#[ test]
134
160
fn chain_position_ord ( ) {
135
- let unconf1 = ChainPosition :: < ConfirmationBlockTime > :: Unconfirmed ( 10 ) ;
136
- let unconf2 = ChainPosition :: < ConfirmationBlockTime > :: Unconfirmed ( 20 ) ;
137
- let conf1 = ChainPosition :: Confirmed ( ConfirmationBlockTime {
138
- confirmation_time : 20 ,
139
- block_id : BlockId {
140
- height : 9 ,
141
- ..Default :: default ( )
161
+ let unconf1 = ChainPosition :: < ConfirmationBlockTime > :: Unconfirmed {
162
+ last_seen : Some ( 10 ) ,
163
+ } ;
164
+ let unconf2 = ChainPosition :: < ConfirmationBlockTime > :: Unconfirmed {
165
+ last_seen : Some ( 20 ) ,
166
+ } ;
167
+ let conf1 = ChainPosition :: Confirmed {
168
+ anchor : ConfirmationBlockTime {
169
+ confirmation_time : 20 ,
170
+ block_id : BlockId {
171
+ height : 9 ,
172
+ ..Default :: default ( )
173
+ } ,
142
174
} ,
143
- } ) ;
144
- let conf2 = ChainPosition :: Confirmed ( ConfirmationBlockTime {
145
- confirmation_time : 15 ,
146
- block_id : BlockId {
147
- height : 12 ,
148
- ..Default :: default ( )
175
+ transitively : None ,
176
+ } ;
177
+ let conf2 = ChainPosition :: Confirmed {
178
+ anchor : ConfirmationBlockTime {
179
+ confirmation_time : 15 ,
180
+ block_id : BlockId {
181
+ height : 12 ,
182
+ ..Default :: default ( )
183
+ } ,
149
184
} ,
150
- } ) ;
185
+ transitively : None ,
186
+ } ;
151
187
152
188
assert ! ( unconf2 > unconf1, "higher last_seen means higher ord" ) ;
153
189
assert ! ( unconf1 > conf1, "unconfirmed is higher ord than confirmed" ) ;
0 commit comments