1313// See the License for the specific language governing permissions and
1414// limitations under the License.
1515
16+ use rstest:: rstest;
17+
1618use chainstate_test_framework:: { empty_witness, TransactionBuilder } ;
17- use common:: { chain:: signature:: inputsig:: InputWitness , primitives:: H256 } ;
19+ use common:: {
20+ chain:: { signature:: inputsig:: InputWitness , timelock:: OutputTimeLock , OrderData } ,
21+ primitives:: H256 ,
22+ } ;
1823use randomness:: Rng ;
19- use rstest:: rstest;
2024use test_utils:: random:: { make_seedable_rng, Seed } ;
2125
2226use super :: * ;
@@ -48,6 +52,10 @@ fn diamond_unconfirmed_descendants(#[case] seed: Seed) {
4852 OutputValue :: Coin ( Amount :: from_atoms ( rng. gen ( ) ) ) ,
4953 Destination :: AnyoneCanSpend ,
5054 ) )
55+ . add_output ( TxOutput :: Transfer (
56+ OutputValue :: Coin ( Amount :: from_atoms ( rng. gen ( ) ) ) ,
57+ Destination :: AnyoneCanSpend ,
58+ ) )
5159 . build ( ) ;
5260 let tx_a_id = tx_a. transaction ( ) . get_id ( ) ;
5361 output_cache
@@ -79,7 +87,7 @@ fn diamond_unconfirmed_descendants(#[case] seed: Seed) {
7987 // C
8088 let tx_c = TransactionBuilder :: new ( )
8189 . add_input (
82- TxInput :: from_utxo ( tx_a_id. into ( ) , 0 ) ,
90+ TxInput :: from_utxo ( tx_a_id. into ( ) , 1 ) ,
8391 empty_witness ( & mut rng) ,
8492 )
8593 . add_output ( TxOutput :: Transfer (
@@ -140,7 +148,7 @@ fn diamond_unconfirmed_descendants(#[case] seed: Seed) {
140148 assert ! ( output_cache. unconfirmed_descendants. is_empty( ) ) ;
141149}
142150
143- // Create 2 unconfirmed txs B and C that spends tokens:
151+ // Create 2 unconfirmed txs B and C that spend tokens:
144152//
145153// /-->B-->C
146154// A
@@ -151,7 +159,7 @@ fn diamond_unconfirmed_descendants(#[case] seed: Seed) {
151159#[ rstest]
152160#[ trace]
153161#[ case( Seed :: from_entropy( ) ) ]
154- fn conflict_parent_and_child ( #[ case] seed : Seed ) {
162+ fn update_conflicting_txs_parent_and_child ( #[ case] seed : Seed ) {
155163 let mut rng = make_seedable_rng ( seed) ;
156164
157165 let mut output_cache = OutputCache :: empty ( ) ;
@@ -245,3 +253,148 @@ fn conflict_parent_and_child(#[case] seed: Seed) {
245253 ]
246254 ) ;
247255}
256+
257+ // Create unconfirmed txs Bi that use a token in their outputs only:
258+ // a) by transferring zero amount of the token (a legit situation which doesn't require the token
259+ // to be present in the inputs);
260+ // b) creating an order that asks for the token.
261+ //
262+ // /-->Bi
263+ // A
264+ // \-->C
265+ //
266+ // Freeze token in C.
267+ // Check that Bi got marked as conflicted.
268+ #[ rstest]
269+ #[ trace]
270+ #[ case( Seed :: from_entropy( ) ) ]
271+ fn update_conflicting_txs_frozen_token_only_in_outputs ( #[ case] seed : Seed ) {
272+ let mut rng = make_seedable_rng ( seed) ;
273+
274+ let mut output_cache = OutputCache :: empty ( ) ;
275+ let token_id = TokenId :: random_using ( & mut rng) ;
276+
277+ let genesis_tx_id = Id :: < Transaction > :: new ( H256 :: random_using ( & mut rng) ) ;
278+ let tx_a = TransactionBuilder :: new ( )
279+ . add_input (
280+ TxInput :: from_utxo ( genesis_tx_id. into ( ) , 0 ) ,
281+ InputWitness :: NoSignature ( None ) ,
282+ )
283+ // Note: only coin outputs here
284+ . add_output ( TxOutput :: Transfer (
285+ OutputValue :: Coin ( Amount :: from_atoms ( rng. gen ( ) ) ) ,
286+ Destination :: AnyoneCanSpend ,
287+ ) )
288+ . add_output ( TxOutput :: Transfer (
289+ OutputValue :: Coin ( Amount :: from_atoms ( rng. gen ( ) ) ) ,
290+ Destination :: AnyoneCanSpend ,
291+ ) )
292+ . add_output ( TxOutput :: Transfer (
293+ OutputValue :: Coin ( Amount :: from_atoms ( rng. gen ( ) ) ) ,
294+ Destination :: AnyoneCanSpend ,
295+ ) )
296+ . build ( ) ;
297+ let tx_a_id = tx_a. transaction ( ) . get_id ( ) ;
298+ output_cache
299+ . add_tx (
300+ tx_a_id. into ( ) ,
301+ WalletTx :: Tx ( TxData :: new (
302+ tx_a,
303+ TxState :: Confirmed ( BlockHeight :: zero ( ) , BlockTimestamp :: from_int_seconds ( 0 ) , 0 ) ,
304+ ) ) ,
305+ )
306+ . unwrap ( ) ;
307+
308+ // Transfer zero amount
309+ let tx_b1 = TransactionBuilder :: new ( )
310+ . add_input (
311+ TxInput :: from_utxo ( tx_a_id. into ( ) , 0 ) ,
312+ empty_witness ( & mut rng) ,
313+ )
314+ . add_output ( TxOutput :: Transfer (
315+ OutputValue :: TokenV1 ( token_id, Amount :: ZERO ) ,
316+ Destination :: AnyoneCanSpend ,
317+ ) )
318+ . build ( ) ;
319+ let tx_b1_id = tx_b1. transaction ( ) . get_id ( ) ;
320+ output_cache
321+ . add_tx (
322+ tx_b1_id. into ( ) ,
323+ WalletTx :: Tx ( TxData :: new ( tx_b1. clone ( ) , TxState :: Inactive ( 0 ) ) ) ,
324+ )
325+ . unwrap ( ) ;
326+
327+ // LockThenTransfer zero amount
328+ let tx_b2 = TransactionBuilder :: new ( )
329+ . add_input (
330+ TxInput :: from_utxo ( tx_a_id. into ( ) , 1 ) ,
331+ empty_witness ( & mut rng) ,
332+ )
333+ . add_output ( TxOutput :: LockThenTransfer (
334+ OutputValue :: TokenV1 ( token_id, Amount :: ZERO ) ,
335+ Destination :: AnyoneCanSpend ,
336+ OutputTimeLock :: ForBlockCount ( 1 ) ,
337+ ) )
338+ . build ( ) ;
339+ let tx_b2_id = tx_b2. transaction ( ) . get_id ( ) ;
340+ output_cache
341+ . add_tx (
342+ tx_b2_id. into ( ) ,
343+ WalletTx :: Tx ( TxData :: new ( tx_b2. clone ( ) , TxState :: Inactive ( 0 ) ) ) ,
344+ )
345+ . unwrap ( ) ;
346+
347+ let tx_b3 = TransactionBuilder :: new ( )
348+ . add_input (
349+ TxInput :: from_utxo ( tx_a_id. into ( ) , 2 ) ,
350+ empty_witness ( & mut rng) ,
351+ )
352+ . add_output ( TxOutput :: CreateOrder ( Box :: new ( OrderData :: new (
353+ Destination :: AnyoneCanSpend ,
354+ OutputValue :: TokenV1 ( token_id, Amount :: from_atoms ( rng. gen ( ) ) ) ,
355+ OutputValue :: Coin ( Amount :: from_atoms ( rng. gen ( ) ) ) ,
356+ ) ) ) )
357+ . build ( ) ;
358+ let tx_b3_id = tx_b3. transaction ( ) . get_id ( ) ;
359+ output_cache
360+ . add_tx (
361+ tx_b3_id. into ( ) ,
362+ WalletTx :: Tx ( TxData :: new ( tx_b3. clone ( ) , TxState :: Inactive ( 0 ) ) ) ,
363+ )
364+ . unwrap ( ) ;
365+
366+ let tx_d = TransactionBuilder :: new ( )
367+ . add_input (
368+ TxInput :: AccountCommand (
369+ AccountNonce :: new ( 0 ) ,
370+ AccountCommand :: FreezeToken ( token_id, IsTokenUnfreezable :: No ) ,
371+ ) ,
372+ empty_witness ( & mut rng) ,
373+ )
374+ . build ( ) ;
375+
376+ let block_id = Id :: < GenBlock > :: new ( H256 :: random_using ( & mut rng) ) ;
377+ let result = output_cache
378+ . update_conflicting_txs ( tx_d. transaction ( ) , block_id)
379+ . unwrap ( )
380+ . into_iter ( )
381+ . collect :: < BTreeMap < _ , _ > > ( ) ;
382+
383+ assert_eq ! (
384+ result,
385+ BTreeMap :: from( [
386+ (
387+ tx_b1_id,
388+ WalletTx :: Tx ( TxData :: new( tx_b1, TxState :: Conflicted ( block_id) ) )
389+ ) ,
390+ (
391+ tx_b2_id,
392+ WalletTx :: Tx ( TxData :: new( tx_b2, TxState :: Conflicted ( block_id) ) )
393+ ) ,
394+ (
395+ tx_b3_id,
396+ WalletTx :: Tx ( TxData :: new( tx_b3, TxState :: Conflicted ( block_id) ) )
397+ ) ,
398+ ] )
399+ ) ;
400+ }
0 commit comments