@@ -4269,5 +4269,127 @@ public async Task IsTrackedTests()
4269
4269
group = new GroupTrackedSource ( ( await tester . Client . CreateGroupAsync ( Cancel ) ) . GroupId ) ;
4270
4270
Assert . True ( await tester . Client . IsTrackedAsync ( group , Cancel ) ) ;
4271
4271
}
4272
+ [ Fact ]
4273
+ public async Task CanImportUTXOs ( )
4274
+ {
4275
+ using var tester = ServerTester . Create ( ) ;
4276
+
4277
+ var wallet1 = await tester . Client . CreateGroupAsync ( ) ;
4278
+ var wallet1TS = new GroupTrackedSource ( wallet1 . GroupId ) ;
4279
+
4280
+ var k = new Key ( ) ;
4281
+ var kAddress = k . GetAddress ( ScriptPubKeyType . Segwit , tester . Network ) ;
4282
+
4283
+ // We use this one because it allows us to use WaitForTransaction later
4284
+ var legacy = new AddressTrackedSource ( new Key ( ) . GetAddress ( ScriptPubKeyType . Legacy , tester . Network ) ) ;
4285
+ await tester . Client . TrackAsync ( legacy ) ;
4286
+
4287
+ var kScript = kAddress . ScriptPubKey ;
4288
+
4289
+ // test 1: create a script and send 2 utxos to it(from diff txs), without confirming
4290
+ // import the first one, verify it is unconfirmed, confirm, then the second one and see it is confirmed
4291
+
4292
+ var tx = await tester . RPC . SendToAddressAsync ( kAddress , Money . Coins ( 1.0m ) ) ;
4293
+ var tx2 = await tester . RPC . SendToAddressAsync ( kAddress , Money . Coins ( 1.0m ) ) ;
4294
+ var rawTx = await tester . RPC . GetRawTransactionAsync ( tx ) ;
4295
+ var rawTx2 = await tester . RPC . GetRawTransactionAsync ( tx2 ) ;
4296
+ var utxo = rawTx . Outputs . AsIndexedOutputs ( ) . First ( o => o . TxOut . ScriptPubKey == kScript ) ;
4297
+ var utxo2 = rawTx2 . Outputs . AsIndexedOutputs ( ) . First ( o => o . TxOut . ScriptPubKey == kScript ) ;
4298
+
4299
+ // Making sure that tx and tx2 are processed before continuing
4300
+ var tx3 = await tester . RPC . SendToAddressAsync ( legacy . Address , Money . Coins ( 1.0m ) ) ;
4301
+ var notif = tester . Notifications . WaitForTransaction ( legacy . Address , tx3 ) ;
4302
+ Assert . Equal ( legacy , notif . TrackedSource ) ;
4303
+
4304
+ await tester . Client . AddGroupAddressAsync ( "BTC" , wallet1 . GroupId , new [ ] { kAddress . ToString ( ) } ) ;
4305
+ await tester . Client . ImportUTXOs ( "BTC" , new ImportUTXORequest ( )
4306
+ {
4307
+ Utxos = [ utxo . ToCoin ( ) . Outpoint ]
4308
+ } ) ;
4309
+
4310
+ var utxos = await tester . Client . GetUTXOsAsync ( wallet1TS ) ;
4311
+ var matched = Assert . Single ( utxos . Unconfirmed . UTXOs ) ;
4312
+ Assert . Equal ( kAddress , matched . Address ) ;
4313
+
4314
+ // tx2 didn't matched when it was in the mempool, so the block will not match it either because of the cache.
4315
+ tester . GetService < RepositoryProvider > ( ) . GetRepository ( "BTC" ) . RemoveFromCache ( new [ ] { tx2 } ) ;
4316
+ tester . Notifications . WaitForBlocks ( await tester . RPC . GenerateAsync ( 1 ) ) ;
4317
+ utxos = await tester . Client . GetUTXOsAsync ( wallet1TS ) ;
4318
+ Assert . Equal ( 2 , utxos . Confirmed . UTXOs . Count ) ;
4319
+ Assert . Contains ( tx , utxos . Confirmed . UTXOs . Select ( u => u . Outpoint . Hash ) ) ;
4320
+ Assert . Contains ( tx2 , utxos . Confirmed . UTXOs . Select ( u => u . Outpoint . Hash ) ) ;
4321
+
4322
+ await tester . Client . ImportUTXOs ( "BTC" , new ImportUTXORequest ( )
4323
+ {
4324
+ Utxos = [ utxo2 . ToCoin ( ) . Outpoint ]
4325
+ } ) ;
4326
+
4327
+ utxos = await tester . Client . GetUTXOsAsync ( wallet1TS ) ;
4328
+ Assert . Equal ( 2 , utxos . Confirmed . UTXOs . Count ) ;
4329
+ //utxo2 may be confirmed but we should have saved the timestamp based on block time or current date
4330
+ var utxoInfo = utxos . Confirmed . UTXOs . First ( u => u . ScriptPubKey == utxo2 . TxOut . ScriptPubKey ) ;
4331
+ Assert . NotEqual ( NBitcoin . Utils . UnixTimeToDateTime ( 0 ) , utxoInfo . Timestamp ) ;
4332
+
4333
+ //test2: try adding in fake utxos or spent ones
4334
+ var fakescript = new Key ( ) . PubKey . GetAddress ( ScriptPubKeyType . Segwit , tester . Network ) . ScriptPubKey ;
4335
+ var fakeUtxo = new Coin ( new OutPoint ( uint256 . One , 1 ) , new TxOut ( Money . Coins ( 1.0m ) , fakescript ) ) ;
4336
+ var kToSpend = new Key ( ) ;
4337
+ var kToSpendAddress = kToSpend . GetAddress ( ScriptPubKeyType . Segwit , tester . Network ) ;
4338
+ var tospendtx = await tester . RPC . SendToAddressAsync ( kToSpendAddress , Money . Coins ( 1.0m ) ) ;
4339
+ var tospendrawtx = await tester . RPC . GetRawTransactionAsync ( tospendtx ) ;
4340
+ var tospendutxo = tospendrawtx . Outputs . AsIndexedOutputs ( ) . First ( o => o . TxOut . ScriptPubKey == kToSpendAddress . ScriptPubKey ) ;
4341
+ var validScript = new Key ( ) . PubKey . GetAddress ( ScriptPubKeyType . Segwit , tester . Network ) . ScriptPubKey ;
4342
+ var spendingtx = tester . Network . CreateTransactionBuilder ( )
4343
+ . AddKeys ( kToSpend )
4344
+ . AddCoins ( new Coin ( tospendutxo ) )
4345
+ . SendEstimatedFees ( new FeeRate ( 100m ) )
4346
+ . SendAll ( validScript ) . BuildTransaction ( true ) ;
4347
+ await tester . RPC . SendRawTransactionAsync ( spendingtx ) ;
4348
+
4349
+ var validScriptUtxo = spendingtx . Outputs . AsIndexedOutputs ( ) . First ( o => o . TxOut . ScriptPubKey == validScript ) ;
4350
+
4351
+ await tester . Client . ImportUTXOs ( "BTC" , new ImportUTXORequest ( )
4352
+ {
4353
+ Utxos =
4354
+ [
4355
+ fakeUtxo . Outpoint ,
4356
+ tospendutxo . ToCoin ( ) . Outpoint ,
4357
+ validScriptUtxo . ToCoin ( ) . Outpoint
4358
+ ]
4359
+ } ) ;
4360
+
4361
+ utxos = await tester . Client . GetUTXOsAsync ( wallet1TS ) ;
4362
+ Assert . Empty ( utxos . Unconfirmed . UTXOs ) ;
4363
+
4364
+ // let's test add an utxo after it has been mined
4365
+ var yoScript = new Key ( ) . PubKey . GetAddress ( ScriptPubKeyType . Segwit , tester . Network ) ;
4366
+ var yoTxId = await tester . SendToAddressAsync ( yoScript , Money . Coins ( 1.0m ) ) ;
4367
+ var yoTx = await tester . RPC . GetRawTransactionAsync ( yoTxId ) ;
4368
+ var yoUtxo = yoTx . Outputs . AsIndexedOutputs ( ) . First ( o => o . TxOut . ScriptPubKey == yoScript . ScriptPubKey ) ;
4369
+
4370
+ await tester . Client . ImportUTXOs ( "BTC" , new ImportUTXORequest ( )
4371
+ {
4372
+ Utxos = [ yoUtxo . ToCoin ( ) . Outpoint ]
4373
+ } ) ;
4374
+
4375
+ utxos = await tester . Client . GetUTXOsAsync ( wallet1TS ) ;
4376
+ Assert . Empty ( utxos . Unconfirmed . UTXOs ) ;
4377
+
4378
+ var aaa = await tester . RPC . GenerateAsync ( 1 ) ;
4379
+ tester . Notifications . WaitForBlocks ( aaa ) ;
4380
+ utxos = await tester . Client . GetUTXOsAsync ( wallet1TS ) ;
4381
+ Assert . Empty ( utxos . Unconfirmed . UTXOs ) ;
4382
+
4383
+ await tester . Client . AddGroupAddressAsync ( "BTC" , wallet1 . GroupId , new [ ] { yoScript . ToString ( ) } ) ;
4384
+ await tester . Client . ImportUTXOs ( "BTC" , new ImportUTXORequest ( )
4385
+ {
4386
+ Utxos = [ yoUtxo . ToCoin ( ) . Outpoint ]
4387
+ } ) ;
4388
+
4389
+ utxos = await tester . Client . GetUTXOsAsync ( wallet1TS ) ;
4390
+ var confirmedUtxo = utxos . Confirmed . UTXOs . Single ( utxo1 => utxo1 . ScriptPubKey == yoScript . ScriptPubKey ) ;
4391
+ Assert . Equal ( 1 , confirmedUtxo . Confirmations ) ;
4392
+ Assert . NotEqual ( NBitcoin . Utils . UnixTimeToDateTime ( 0 ) , confirmedUtxo . Timestamp ) ;
4393
+ }
4272
4394
}
4273
4395
}
0 commit comments