@@ -4196,8 +4196,125 @@ fn test_transactions_sort_by() {
41964196 assert_eq ! ( [ None , Some ( 2000 ) , Some ( 1000 ) ] , conf_heights. as_slice( ) ) ;
41974197}
41984198
4199+ use std:: collections:: HashMap ;
4200+
4201+ use bdk_bitcoind_rpc:: Emitter ;
4202+ use bdk_testenv:: bitcoincore_rpc:: json:: CreateRawTransactionInput ;
4203+ use bdk_testenv:: bitcoincore_rpc:: { RawTx , RpcApi } ;
4204+ use bdk_testenv:: TestEnv ;
4205+ use bdk_wallet:: CreateParams ;
4206+
41994207#[ test]
42004208fn test_tx_builder_is_send_safe ( ) {
42014209 let ( mut wallet, _txid) = get_funded_wallet_wpkh ( ) ;
42024210 let _box: Box < dyn Send + Sync > = Box :: new ( wallet. build_tx ( ) ) ;
42034211}
4212+
4213+ // ErikDeSmedt's double spend test scenario
4214+ #[ test]
4215+ fn detect_double_spend ( ) -> anyhow:: Result < ( ) > {
4216+ // Create a test environment and mine initial funds
4217+ let env = TestEnv :: new ( ) . expect ( "Failed to launch bitcoind" ) ;
4218+ let client = env
4219+ . bitcoind
4220+ . create_wallet ( "" )
4221+ . expect ( "Failed to create wallet" ) ;
4222+ let address = client
4223+ . get_new_address ( None , None )
4224+ . expect ( "Failed to create address" ) ;
4225+ client
4226+ . generate_to_address ( 106 , address. assume_checked_ref ( ) )
4227+ . expect ( "Failed to mine block" ) ;
4228+
4229+ // Create the wallet under test
4230+ let mut alice = {
4231+ const DESC : & str ="tr(tprv8ZgxMBicQKsPdnxnfzsvrUZ58eNTq85PA6nhJALQiGy9GVhcvXmHX2r9znpyApMVNLdkPBp3WArLgU3UnA6npK9TtGoZDKdAjjkoYm3rY7F/84'/0'/0'/0/*)" ;
4232+ Wallet :: create_with_params (
4233+ CreateParams :: new_single ( DESC ) . network ( Network :: Regtest )
4234+ )
4235+ }
4236+ . expect ( "Wallet can be created" ) ;
4237+
4238+ // Sync it with the chain
4239+ let mut emitter = Emitter :: new ( & env. bitcoind . client , alice. latest_checkpoint ( ) , 0 ) ;
4240+ while let Some ( ev) = emitter. next_block ( ) . unwrap ( ) {
4241+ alice
4242+ . apply_block_connected_to ( & ev. block , ev. block_height ( ) , ev. connected_to ( ) )
4243+ . unwrap ( ) ;
4244+ }
4245+
4246+ // Creates some transactions
4247+ let unspent = client
4248+ . list_unspent ( None , None , None , Some ( false ) , None )
4249+ . unwrap ( ) ;
4250+
4251+ let input_amount = unspent[ 0 ] . amount ;
4252+ let destination_amount = Amount :: from_sat ( 100_000 ) ;
4253+ let fee_amount = Amount :: from_sat ( 2_000 ) ;
4254+ let change_amount = input_amount - destination_amount - fee_amount;
4255+
4256+ // Create a transaction that pays Alice
4257+ let address = alice. reveal_next_address ( KeychainKind :: External ) . address ;
4258+ let change_address = client. get_new_address ( None , None ) . unwrap ( ) . assume_checked ( ) ;
4259+ let inputs = [ CreateRawTransactionInput {
4260+ txid : unspent[ 0 ] . txid ,
4261+ vout : unspent[ 0 ] . vout ,
4262+ sequence : Some ( 0xFFFFFFFE ) ,
4263+ } ] ;
4264+ let mut outputs = HashMap :: new ( ) ;
4265+ outputs. insert ( address. to_string ( ) , destination_amount) ;
4266+ outputs. insert ( change_address. to_string ( ) , change_amount) ;
4267+ let tx1a = client
4268+ . create_raw_transaction ( & inputs, & outputs, None , None )
4269+ . unwrap ( ) ;
4270+ let tx1a = client
4271+ . sign_raw_transaction_with_wallet ( tx1a. raw_hex ( ) , None , None )
4272+ . unwrap ( )
4273+ . transaction ( )
4274+ . unwrap ( ) ;
4275+
4276+ // Create a double-spent of tx1a
4277+ let address = client. get_new_address ( None , None ) . unwrap ( ) . assume_checked ( ) ;
4278+ let mut outputs = HashMap :: new ( ) ;
4279+ outputs. insert ( address. to_string ( ) , destination_amount) ;
4280+ outputs. insert ( change_address. to_string ( ) , change_amount) ;
4281+ let tx1b = client
4282+ . create_raw_transaction ( & inputs, & outputs, None , None )
4283+ . unwrap ( ) ;
4284+ let tx1b: Transaction = client
4285+ . sign_raw_transaction_with_wallet ( tx1b. raw_hex ( ) , None , None )
4286+ . unwrap ( )
4287+ . transaction ( )
4288+ . unwrap ( ) ;
4289+
4290+ // Alice observes tx1a in the mempool
4291+ alice. apply_unconfirmed_txs ( vec ! [ ( tx1a. clone( ) , 100 ) ] ) ;
4292+
4293+ // A block is create
4294+ // In this block tx1a is doublespent by tx1b
4295+ client. send_raw_transaction ( tx1b. raw_hex ( ) ) . unwrap ( ) ;
4296+ let address = client
4297+ . get_new_address ( None , None )
4298+ . expect ( "Failed to create address" ) ;
4299+ client
4300+ . generate_to_address ( 6 , address. assume_checked_ref ( ) )
4301+ . expect ( "Failed to mine block" ) ;
4302+
4303+ // Apply the block to the w1
4304+ let mut emitter = Emitter :: new ( & env. bitcoind . client , alice. latest_checkpoint ( ) , 0 ) ;
4305+ while let Some ( ev) = emitter. next_block ( ) . unwrap ( ) {
4306+ alice
4307+ . apply_block_connected_to ( & ev. block , ev. block_height ( ) , ev. connected_to ( ) )
4308+ . unwrap ( ) ;
4309+ }
4310+
4311+ // We also add txb do the wallet
4312+ alice. apply_unconfirmed_txs ( [ ( tx1b. clone ( ) , 101 ) ] ) ;
4313+
4314+ // I expect list_unspent to be empty.
4315+ // (tx1a) was double-spent
4316+ assert_eq ! ( alice. list_unspent( ) . collect:: <Vec <_>>( ) , vec![ ] ) ;
4317+ //assert_eq!(alice.transactions().collect::<Vec<_>>(), vec![]);
4318+
4319+ Ok ( ( ) )
4320+ }
0 commit comments