Compare commits

..

1191 Commits

Author SHA1 Message Date
Nicolas Dorier
5147b5f261
bump deps 2026-06-10 10:12:06 +09:00
Nicolas Dorier
6ad4941712
Merge pull request #544 from s373nZ/docker-curl
Add `curl` to Docker image to support health checks
2026-06-02 09:07:36 +09:00
se7enz
98d89f924a
docker: Add curl to Docker image to support health checks 2026-05-26 09:52:03 +02:00
Nicolas Dorier
36e1596b5c
Faster tests 2026-04-24 14:14:09 +09:00
Nicolas Dorier
83e0ab4f68
Relax xpub validation 2026-04-24 12:25:52 +09:00
Nicolas Dorier
c24b47bb03
Deprecate import into RPC 2026-04-24 09:05:55 +09:00
Nicolas Dorier
b9f324b2eb
bump testframework 2026-04-23 15:33:14 +09:00
Nicolas Dorier
e0af3bf493
Fix broken links 2026-04-23 15:23:49 +09:00
Nicolas Dorier
06349f9818
Trying to speedup tests 2026-04-23 14:51:28 +09:00
Nicolas Dorier
c46949ac60
Merge pull request #542 from dgarage/chunk
Add chunk fee and weight to transaction metadata
2026-04-22 18:14:34 +09:00
Nicolas Dorier
5e570a84fb
Add chunk fee and weight to transaction metadata 2026-04-22 18:03:29 +09:00
Nicolas Dorier
060d19f6a8
bump 2026-04-22 11:13:19 +09:00
Nicolas Dorier
b83178be8d
Fix tests 2026-04-22 10:51:31 +09:00
Nicolas Dorier
f72fc4d321
Remove some error during tests 2026-04-22 09:59:01 +09:00
Nicolas Dorier
9242d645b6
Trying to speed up tests 2026-04-22 09:25:43 +09:00
Nicolas Dorier
05df9b5037
Decrease spams in test logs 2026-04-22 09:06:09 +09:00
Nicolas Dorier
456717935c
Fix tests 2026-04-22 09:03:32 +09:00
Nicolas Dorier
98df6b7fdd
Improve error messages for RPC wallet errors 2026-04-22 08:54:33 +09:00
Nicolas Dorier
449568be9a
Bump test to 31.0 2026-04-22 08:47:51 +09:00
Nicolas Dorier
abb5fd3a6c
bump bitcoin core in tests 2026-04-21 21:21:35 +09:00
Nicolas Dorier
deb868b2c0
Add PushNuget.sh 2026-04-21 10:38:46 +09:00
Nicolas Dorier
f0000ceab6
bump libs 2026-04-21 10:30:00 +09:00
Nicolas Dorier
d9cba8b0b5
bump nbt 2026-04-11 09:17:57 +09:00
Nicolas Dorier
becdc3f47d
Fix DOGE getting stuck 2026-04-10 22:33:18 +09:00
Nicolas Dorier
aa462e7af1
bump 2026-04-10 11:28:07 +09:00
Nicolas Dorier
34d6b0dcf3
Potentially fix dogecoin bug, improve logs when node connection fail with exception 2026-04-10 11:27:47 +09:00
Nicolas Dorier
87037e17e1
Improve logs at startup 2026-04-10 10:44:46 +09:00
Nicolas Dorier
c806e9022e
Do not use static logger in tests 2026-04-10 09:44:04 +09:00
Nicolas Dorier
39b1861d7a
Fix test 2026-04-10 09:27:13 +09:00
Nicolas Dorier
a379506b46
Port code to dotnet10.0 style 2026-04-10 09:18:44 +09:00
Nicolas Dorier
68513b4668
Remove message broker leftover 2026-04-10 09:09:44 +09:00
Nicolas Dorier
802945291a
Remove message broker dependencies 2026-04-10 08:55:28 +09:00
Nicolas Dorier
3756b54138
Fix compatibility with core 31.0 2026-04-10 08:17:05 +09:00
Nicolas Dorier
9eeaf71195
bump 2026-04-03 11:05:43 +09:00
Nicolas Dorier
0e4222a1f2
Merge pull request #539 from Unknown-Kush/master
Add Pepecoin
2026-04-03 11:04:13 +09:00
Unknown-Kush
34831b2d85 Add Pepecoin support 2026-04-02 09:51:55 -04:00
Unknown-Kush
aba8cdc8c6 bump NBitcoin.Altcoins 2026-04-02 09:24:52 -04:00
Nicolas Dorier
ba76d3b059
bump dockerfile 2026-04-01 10:07:47 +09:00
Nicolas Dorier
26b3dddc28
bump nbx 2026-03-11 09:54:42 +09:00
Nicolas Dorier
1a38ffa497
Bump libraries 2026-03-11 09:54:42 +09:00
Nicolas Dorier
2fb63ae18e
Merge pull request #537 from NicolasDorier/retry-flaky-rpc
Retry flaky idempotent RPC requests
2026-03-11 09:44:29 +09:00
Nicolas Dorier
206fd0017f
Fix .net10 mention 2026-03-11 09:42:01 +09:00
Nicolas Dorier
5d78c7034c
Retry flaky idempotent RPC requests 2026-03-11 09:32:27 +09:00
Nicolas Dorier
f226238779
Add publish-docker.sh 2026-03-11 09:15:22 +09:00
Nicolas Dorier
05d8ed054e bump 2025-12-26 11:05:07 +09:00
Nicolas Dorier
4ebd7c4c2f
Update to .NET10.0 and bump libs (#534) 2025-12-20 23:29:30 +09:00
Nicolas Dorier
8670e58923 bump postgres 2025-12-18 16:51:01 +09:00
nicolas.dorier
5ea76f64bb
bump NBitcoin.Testframework 2025-09-09 22:06:10 +09:00
nicolas.dorier
be086282ae
Allow feature tag to version on docker 2025-09-06 11:29:45 +09:00
nicolas.dorier
58bde3c916
bump asp.net 2025-09-06 11:25:36 +09:00
nicolas.dorier
8af448f724
bump 2025-08-27 19:41:21 +09:00
Nicolas Dorier
273333b040
Fix documentation AI slop, allow more format for broadcasting (#529) 2025-08-27 11:40:53 +01:00
Nicolas Dorier
d03465c240
Fix incorrect doc for GetUTXOs (#530) 2025-08-27 11:40:33 +01:00
nicolas.dorier
58e9bba5a2
Fix: Unable to delete a ADDRESS: item in a group 2025-08-27 19:40:09 +09:00
nicolas.dorier
c58c60c9fa
bump 2025-08-25 10:09:38 +09:00
nicolas.dorier
939a575c51
Fix: Periodic tasks would sometimes stop firing 2025-08-25 10:09:37 +09:00
nicolas.dorier
ca97104831
bump 2025-07-02 12:48:43 +09:00
gruve-p
a51911f4ce
Bump GRS to 29.0 (#517) 2025-07-02 12:32:49 +09:00
Nicolas Dorier
e8316d959b
Add key index to events (#525) 2025-07-02 12:32:33 +09:00
nicolas.dorier
be51a4af50
Add PSBTCoin.HDKeysFor extension method 2025-06-09 17:54:18 +09:00
nicolas.dorier
b7dc5b5c5f
Add PSBT.HDKeysFor for derivation schemes 2025-06-09 17:41:08 +09:00
nicolas.dorier
cdac2c264e
Add overloads to SignAll and GetBalance PSBT 2025-06-09 17:27:20 +09:00
nicolas.dorier
e494a46de9
Bump NBX 2025-06-09 16:56:54 +09:00
nicolas.dorier
10ac6d65d8
Bump NBitcoin 2025-06-09 16:56:02 +09:00
nicolas.dorier
7f84ef983c
Fix doc anchor 2025-06-02 18:57:15 +09:00
nicolas.dorier
9a784b5cce
Major bump version for NBXplorer.Client 2025-06-02 16:56:43 +09:00
nicolas.dorier
dfe7ca478f
Pass keypath when possible to Derivation 2025-06-02 16:36:10 +09:00
nicolas.dorier
ea4ac907b6
Add convenience overload for GetLineFor 2025-06-02 16:28:23 +09:00
nicolas.dorier
fa8bf56623
bump 2025-06-02 11:12:14 +09:00
Nicolas Dorier
f4640c8c4f
Support miniscript derivation scheme (#523)
* Support miniscript derivation scheme

* Adjustements

* fix
2025-06-02 11:11:28 +09:00
nicolas.dorier
fdc8281e88
Bump NBitcoin 2025-05-15 13:43:36 +09:00
nicolas.dorier
8597067cbe
bump NBitcoin 2025-05-15 10:35:22 +09:00
nicolas.dorier
ae99da7299
Simplify code, fix another bug which can stall sync 2025-04-05 10:53:41 +09:00
nicolas.dorier
4666c13a1b
bump 2025-04-04 23:16:04 +09:00
nicolas.dorier
3118b71004
Fix: NBXplorer stalling every 2000 blocks 2025-04-04 23:07:41 +09:00
nicolas.dorier
d61f3db4fa
bump nbxplorer 2025-04-04 22:28:04 +09:00
nicolas.dorier
10e139c3d1
For liquid blinded addresses returns blindingKey in KeyInformation 2025-04-04 22:22:15 +09:00
nicolas.dorier
206165736a
Add doc for the fullschema 2025-04-02 21:43:58 +09:00
nicolas.dorier
f68c50c4cf
Fix C# comment 2025-03-11 17:23:02 +09:00
Nicolas Dorier
75cbba9427
Get transaction data from peer when possible (#511) 2025-03-06 17:40:09 +09:00
Nicolas Dorier
1916ea7050
Add PSBTVersion to CreatePSBT (#510) 2025-03-05 18:26:38 +09:00
nicolas.dorier
e43031411d
bump nbx 2025-02-17 18:00:30 +09:00
nicolas.dorier
7b7cd2314d
Fix regression from .21 2025-02-09 23:53:44 +09:00
nicolas.dorier
4ce8ef6954
CreatePSBT: explicitFee shouldn't be mutually exclusive with feeRate settings 2025-02-09 23:10:44 +09:00
nicolas.dorier
80f16ce8bc
Allow CreatePSBT.ExplicitChangeAddress to be an hex script 2025-01-30 17:34:29 +09:00
nicolas.dorier
fed67b354f
bump 2025-01-30 17:27:20 +09:00
Nicolas Dorier
da251bdbe0
CreatePSBT accept raw scripts (#509) 2025-01-30 17:27:00 +09:00
nicolas.dorier
11a3564ab4
Fix: Feature property was always null. Exception thrown if PSBTDest.Value is null 2025-01-30 16:18:51 +09:00
nicolas.dorier
e3f7933ad1
Fixup GetTransaction crash in case an unknown entry isn't in the mempool 2025-01-30 15:39:20 +09:00
nicolas.dorier
8b9d5fbde7
Bump, and nicer error message if amount are negative in create PSBT 2025-01-30 15:36:46 +09:00
Nicolas Dorier
916a52b49e
Add fee and mempool information to txs fetched in the mempool (#508) 2025-01-30 14:21:25 +09:00
Nicolas Dorier
2ed4f7850f
Bump Bitcoin Core and NBitcoin (#506) 2025-01-17 00:07:31 +09:00
Nicolas Dorier
a38ad24023
Bump Bitcoin Core in tests (#505) 2025-01-13 12:06:19 +09:00
nicolas.dorier
ab27e4b6ce
bump 2025-01-06 19:09:02 +09:00
Nicolas Dorier
e9a235c149
New route for subscribing to events via websockets (#503) 2025-01-06 19:08:41 +09:00
nicolas.dorier
03c17c046d
Improve doc 2024-12-27 19:15:47 +09:00
nicolas.dorier
530a38030e
Fix: Issue when using custom key templates after scanning utxosets 2024-12-27 16:41:12 +09:00
nicolas.dorier
9d796828f8
Refactoring: Do not use keyPathTemplates directly 2024-12-27 16:31:54 +09:00
Nicolas Dorier
eaeeea22a9
Remove dependencies to keytemplates (#502) 2024-12-27 16:16:12 +09:00
nicolas.dorier
5170fd92e1
Returns KeyPathInformation.Index and reuse queries 2024-12-25 21:48:08 +09:00
nicolas.dorier
e5eaf763e7
Make doc a bit more user-friendly by using default postgres port (Fix #498) 2024-12-19 12:23:59 +09:00
Nicolas Dorier
326d9c5e2b
Fix: Taproot PSBT's outputs were not including internal key and taproot hd information (#495) 2024-12-05 15:20:51 +09:00
Nicolas Dorier
8ea14672dd
Bump dependencies (#494) 2024-12-03 14:40:37 +09:00
nicolas.dorier
4d81c236f5
Remove deps 2024-11-30 10:30:12 +09:00
nicolas.dorier
a2afbd951e
More docs 2024-11-29 14:27:00 +09:00
nicolas.dorier
35b449a49f
Fix build 2024-11-29 14:13:57 +09:00
nicolas.dorier
5ab3da5031
Improve docs, show version on startup 2024-11-29 14:09:03 +09:00
nicolas.dorier
dbfd09e2f9
bump 2024-11-29 10:02:06 +09:00
Nicolas Dorier
24d24f2f25
Fix timeout errors, Fix NBXplorer hanging when exiting (#491) 2024-11-29 10:01:21 +09:00
nicolas.dorier
1eeb0ff3ef
Fixup doc 2024-11-29 10:01:01 +09:00
nicolas.dorier
b31fa111b4
Add doc 2024-11-29 10:00:25 +09:00
nicolas.dorier
fb2691748f
Reword doc 2024-11-28 22:32:44 +09:00
nicolas.dorier
c64dc124b5
Better docs 2024-11-28 22:30:11 +09:00
nicolas.dorier
97214740c1
Avoid crash on useless config parameters (#470) 2024-11-28 22:10:07 +09:00
nicolas.dorier
3e0b2047d3
Avoid crash on useless config parameters (#470) 2024-11-28 21:20:01 +09:00
nicolas.dorier
2fa2ca69a0
bump 2024-11-28 19:27:22 +09:00
nicolas.dorier
f37ef1c2d5
Improve doc for groups 2024-11-28 14:34:05 +09:00
Nicolas Dorier
f165b14c52
GetTransactions can be called with from/to timestamp filter (#490) 2024-11-28 14:25:59 +09:00
Andrew Camilleri
c249b842ef
Import utxo (#465) 2024-11-28 13:38:27 +09:00
Nicolas Dorier
531f28817e
Refactor transaction matching, fix elements (#489)
Co-authored-by: Kukks <evilkukka@gmail.com>
2024-11-28 13:30:08 +09:00
nicolas.dorier
654b103128
Improve doc 2024-11-28 13:14:09 +09:00
José Manuel Nieto
21122b4a1f
Refine endpoints (#488) 2024-11-28 12:29:27 +09:00
José Manuel Nieto
e547b99a47
Add API documentation for /cryptos/{cryptoCode}/rpc (#486)
* Add documentation for /cryptos/{cryptoCode}/rpc

* Fix issues during review

* Improve wording
2024-11-28 12:18:07 +09:00
Nicolas Dorier
88f7d8248a
Refactor matching (#487) 2024-11-26 22:05:48 +09:00
nicolas.dorier
a201004b42
Fix: RPC proxy wasn't supporting requests with string as version 2024-11-26 13:25:57 +09:00
nicolas.dorier
0bf9492e1d
Replace from azure to github pages 2024-11-25 17:34:30 +09:00
nicolas.dorier
8275c6effb
Add index.html 2024-11-25 17:27:34 +09:00
nicolas.dorier
3d746aeff9
Fix some links 2024-11-25 16:24:57 +09:00
nicolas.dorier
8f7371dc47
Make API openapi 3.0 compliant 2024-11-25 16:06:40 +09:00
nicolas.dorier
048eab8625
Document basic auth 2024-11-25 15:55:59 +09:00
nicolas.dorier
d4068e7dec
Add doc for generate hot wallet 2024-11-25 13:09:55 +09:00
nicolas.dorier
1352c8dcdf
Add missing Create PSBT 2024-11-25 12:47:16 +09:00
nicolas.dorier
e2767a85c5
Adjustement docs 2024-11-25 12:13:38 +09:00
nicolas.dorier
09d56b22be
Cleanup repetitions 2024-11-25 12:09:53 +09:00
nicolas.dorier
1ee089836d
Add missing routes to groups 2024-11-25 12:04:00 +09:00
nicolas.dorier
4c9db11e6c
Fix broken link 2024-11-25 11:48:11 +09:00
nicolas.dorier
dddd825571
Fix doc of /status 2024-11-25 11:31:25 +09:00
nicolas.dorier
42c9dda280
Rewrite doc of /rescan 2024-11-25 11:17:19 +09:00
nicolas.dorier
9af5962b1e
Improve tags 2024-11-25 11:03:50 +09:00
nicolas.dorier
d5b56d3206
Add /v1 to all paths, reuse parameters definition 2024-11-25 10:41:42 +09:00
Andrew Camilleri
ddde4ae301
Is tracked api (#482) 2024-11-25 10:12:59 +09:00
José Manuel Nieto
a76965d12c
Enrich API documentation (#485)
* Enrich API documentation

* Add missing parameter "includeTransaction" in /cryptos/{cryptoCode}/transactions/{txId}

* Add missing "includeTransaction" parameter in /cryptos/{cryptoCode}/derivations/{derivationScheme}/transactions
2024-11-25 09:31:21 +09:00
Nicolas Dorier
7fb40b5a81
Add browseable doc (#484) 2024-11-18 17:55:58 +09:00
José Manuel Nieto
2e75bdf65e
Add Redoc (#483)
* Add Redoc

* Add CORS Service

* Make sure index.html is served correctly
2024-11-18 17:13:03 +09:00
nicolas.dorier
ac6e63c20f
Bump NBitcoin 2024-11-14 22:11:59 +09:00
nicolas.dorier
5519caf601
Bump NBitcoin 2024-11-14 21:40:51 +09:00
nicolas.dorier
1814138225
Bump NBitcoin 2024-11-13 15:34:05 +09:00
nicolas.dorier
d612420059
bump 2024-11-13 14:47:03 +09:00
nicolas.dorier
f3aeea447d
Fix: Transaction dates are wrong after server / BTC node downtime 2024-11-13 14:46:48 +09:00
nicolas.dorier
261a1e73a1
bump 2024-10-29 10:49:20 +09:00
gruve-p
7c870bdeaa
Bump GRS to 28.0 (#480) 2024-10-29 10:10:30 +09:00
Andrew Camilleri
15a07a8b47
Support mutiny net (#474)
* Support mutiny net

* Small fixup

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-10-29 10:09:52 +09:00
nicolas.dorier
52258aecd1
bump 2024-10-26 10:22:36 +09:00
nicolas.dorier
b672a28c1c
Fix DASH block parsing 2024-10-26 10:21:56 +09:00
nicolas.dorier
a33de3bec7
Make sure index progress has at least one block 2024-10-25 23:57:59 +09:00
nicolas.dorier
f98fa1addb
Fix WatchDog being stuck 2024-09-17 15:46:44 +09:00
nicolas.dorier
b5b90871ae
Remove IRPCClient 2024-09-17 15:39:21 +09:00
nicolas.dorier
e937448b6b
Rewrite for readability 2024-09-17 15:21:20 +09:00
nicolas.dorier
c84c0fce97
Small adaptation for 28.0 2024-08-30 22:32:24 +09:00
nicolas.dorier
4456dd04d0
bump 2024-08-30 16:31:40 +09:00
Nicolas Dorier
6ba0315de4
Merge pull request #475 from NicolasDorier/improve-taproot
Fix taproot PSBT generation
2024-08-30 16:31:21 +09:00
nicolas.dorier
2c6418c058
Fix taproot PSBT generation 2024-08-30 16:15:22 +09:00
Nicolas Dorier
f978e88b85
Merge pull request #469 from NicolasDorier/inputsinfo
Add MatchedInputs to events
2024-06-04 11:52:55 +09:00
nicolas.dorier
22b8e0b17a
Add matchedInputs to events 2024-06-04 11:45:51 +09:00
nicolas.dorier
2bd46f7820
Small refactoring 2024-06-04 09:30:40 +09:00
nicolas.dorier
d03b511db6
Remove useless code 2024-06-03 18:27:02 +09:00
nicolas.dorier
0c9a3569e6
Bump images 2024-05-21 11:03:03 +09:00
Nicolas Dorier
f200ea4930
Merge pull request #466 from NicolasDorier/fixtests
Fix tests
2024-05-21 09:28:00 +09:00
nicolas.dorier
dfef4e8c7e
Fix tests 2024-05-21 00:28:03 +09:00
nicolas.dorier
4bf8e25902
bump packages 2024-05-20 21:23:26 +09:00
Nicolas Dorier
019f8c420a
Merge pull request #463 from NicolasDorier/removedbtrie2
Remove DBTrie
2024-04-08 16:58:16 +09:00
nicolas.dorier
a2dc680338
Remove dead code 2024-04-08 16:43:55 +09:00
nicolas.dorier
72a102a8fc
Split actions into several controllers 2024-04-08 16:31:56 +09:00
nicolas.dorier
e7c701f3f7
Renaming namespace 2024-04-08 15:53:06 +09:00
nicolas.dorier
7c710e34b7
Remove abstractions (Interfaces) 2024-04-08 15:49:53 +09:00
nicolas.dorier
efc9ae6444
Remove abstractions 2024-04-08 14:32:43 +09:00
nicolas.dorier
b20d1567e0
Remove DBTrie 2024-04-08 14:07:00 +09:00
nicolas.dorier
21f5f04f64
Remove debug logs 2024-03-06 19:27:33 +09:00
nicolas.dorier
825d5bc78d
Fix: NBX would sometimes stays stuck in case of reorg (Fix #461 #409) 2024-03-06 16:21:21 +09:00
nicolas.dorier
e83cfdb720
bump npgsql 2024-02-26 09:33:04 +09:00
nicolas.dorier
96633a3b7d
Pass down cancellationToken to RPC commands 2024-02-26 09:11:32 +09:00
nicolas.dorier
900f99545d
Avoid crashing PostgresIndexers.StartAsync 2024-02-05 10:24:39 +09:00
nicolas.dorier
082373501e
bump 2024-01-18 15:43:52 +09:00
nicolas.dorier
279521c507
Bump NBitcoin 2024-01-17 22:07:49 +09:00
nicolas.dorier
2000affe7b
Bump libs 2024-01-17 12:44:56 +09:00
Nicolas Dorier
7d70e72d91
Merge pull request #457 from NicolasDorier/groupapi
Add Group API
2024-01-12 09:10:00 +09:00
nicolas.dorier
9b6358221a
Add Group API 2024-01-10 21:15:04 +09:00
nicolas.dorier
5d4d028b0e
Rename WalletTrackedSource to GroupTrackedSource 2024-01-09 13:09:29 +09:00
nicolas.dorier
07b1193237
bump 2023-12-31 12:32:09 +09:00
nicolas.dorier
56d2293c30
Fix possible nbxplorer sync hanging 2023-12-31 12:31:56 +09:00
nicolas.dorier
84305c84d8
Remove unused code, Use IEnumerable.Chunk instead of custom Batch method 2023-12-30 23:45:30 +09:00
nicolas.dorier
fffa6f0bca
bump 2023-12-25 14:20:38 +09:00
Nicolas Dorier
15eee7ae47
Merge pull request #456 from NicolasDorier/foiqntq
Remove useless db roundtrip when indexing transactions
2023-12-25 14:20:21 +09:00
nicolas.dorier
a490c24351
Remove useless db roundtrip when indexing transactions 2023-12-25 14:15:24 +09:00
nicolas.dorier
6eb53ed8d2
Update sln 2023-12-18 13:33:15 +09:00
Nicolas Dorier
6ca2560a1a
Merge pull request #454 from gruve-p/patch-4
Bump Groestlcoin Core to 26.0
2023-12-18 13:31:41 +09:00
Nicolas Dorier
f69581f69e
Merge pull request #455 from dgarage/rewriteci
Use buildx
2023-12-18 13:31:25 +09:00
nicolas.dorier
190eabbc5e
Use buildx 2023-12-18 13:30:26 +09:00
gruve-p
826dc2d4ac
Bump Groestlcoin Core to 26.0 2023-12-13 17:45:39 +01:00
nicolas.dorier
95f28ac578
Properly delete chain-slim.dat 2023-12-12 19:11:05 +09:00
nicolas.dorier
8d309e4de5
bump NBX 2023-12-12 19:06:14 +09:00
nicolas.dorier
9bcf963a4a
Fix weird error during migration 2023-12-12 18:59:00 +09:00
nicolas.dorier
85f508756a
Bump deps 2023-12-12 13:05:16 +09:00
nicolas.dorier
5ddba1d816
bump 2023-12-11 16:17:56 +09:00
Nicolas Dorier
eca963def6
Merge pull request #453 from NicolasDorier/txts3
Add WalletTrackedSource to backend
2023-12-11 16:02:43 +09:00
nicolas.dorier
cfedff079d
Add WalletTrackedSource to backend 2023-12-11 15:57:23 +09:00
Nicolas Dorier
ceef9651f3
Merge pull request #452 from dgarage/watchdog
Fix spurious NBXplorer stuck synching
2023-12-11 13:23:10 +09:00
nicolas.dorier
1d89e331ed
Fix spurious NBXplorer stuck synching 2023-12-11 10:39:42 +09:00
Nicolas Dorier
7860ebbda4
Merge pull request #451 from NicolasDorier/refactorgetblocks
Refactor getting block headers from RPC efficiently
2023-12-06 17:49:53 +09:00
nicolas.dorier
2f855b5cc3
Refactor getting block headers from RPC efficiently 2023-12-06 17:43:39 +09:00
Nicolas Dorier
8ec608f2bb
Merge pull request #449 from NicolasDorier/trackctx
Introduce TrackedSourceContext
2023-12-05 13:06:05 +09:00
Kukks
4260cb8e04
Introduce TrackedSourceContext 2023-12-05 12:51:49 +09:00
nicolas.dorier
9b6c0010fb
Fix spurious deadlocks 2023-12-04 08:53:20 +09:00
nicolas.dorier
a01893d09d
Remove chain-slim.dat after migration to postgres 2023-11-30 08:14:26 +09:00
nicolas.dorier
57048b0d05
bump 2023-11-27 12:10:35 +09:00
Nicolas Dorier
9a64c246b8
Merge pull request #448 from NicolasDorier/qoinq
Replaced[] wasn't set for transactions mined into a block
2023-11-27 08:53:09 +09:00
nicolas.dorier
fa26cd4fcf
Replaced[] wasn't set for transactions mined into a block 2023-11-24 18:16:20 +09:00
nicolas.dorier
3b192971f0
fixup 2023-11-24 16:41:18 +09:00
Nicolas Dorier
02e4714bce
Merge pull request #447 from NicolasDorier/fwpoeqz
Fix double spend detection (Fix #421)
2023-11-24 09:44:06 +09:00
nicolas.dorier
342a16ad07
Fix double spend detection (Fix #421) 2023-11-24 09:29:25 +09:00
nicolas.dorier
31a1ab4c89
bump NBX 2023-11-21 12:49:00 +09:00
nicolas.dorier
5de2a002a0
bump npgsql 2023-11-21 11:31:01 +09:00
Nicolas Dorier
9faab8f97d
Merge pull request #446 from NicolasDorier/fixuri
Make sure NBXClient's uris are well escaped
2023-11-17 15:41:51 +09:00
nicolas.dorier
dbef90b927
Do not use connection pooling for the refresh wallet history 2023-11-17 15:32:43 +09:00
nicolas.dorier
30c1536323
Make sure NBXClient's uris are well encoded 2023-11-17 15:29:48 +09:00
nicolas.dorier
ef1f78b9e9
Improve performance of get_wallet_recents 2023-11-16 10:09:15 +09:00
nicolas.dorier
d7a9af1d7a
Do not hardcode public schema in migration scripts 2023-11-16 07:42:15 +09:00
Nicolas Dorier
1ac249db79
Merge pull request #442 from NicolasDorier/bumplibs
Bump dependencies
2023-11-15 21:56:48 +09:00
nicolas.dorier
c547f20d20
Bump dependencies 2023-11-15 20:00:18 +09:00
nicolas.dorier
a9a8f56d73
Bump commandtimeout for migration 2023-11-15 18:13:25 +09:00
nicolas.dorier
42dbfdb59d
bump client lib 2023-11-15 15:42:29 +09:00
nicolas.dorier
f8707aca8b
Update README.md 2023-11-15 13:08:42 +09:00
nicolas.dorier
f76543311d
Update README 2023-11-15 13:07:31 +09:00
Nicolas Dorier
8a051016f4
Merge pull request #441 from NicolasDorier/net8
Bump to .NET 8.0
2023-11-15 13:05:13 +09:00
nicolas.dorier
506388937e
Bump to .NET 8.0 2023-11-15 12:57:47 +09:00
Nicolas Dorier
600a642ebc
Merge pull request #440 from dgarage/qointee
Stop supporting DBTrie backend
2023-11-15 11:33:01 +09:00
nicolas.dorier
3cbb864713
Stop supporting DBTrie backend 2023-11-15 11:22:30 +09:00
Nicolas Dorier
25a3f579d8
Merge pull request #437 from dgarage/wionqr
Allow creation of PSBTs conflicting with mempool with includeOnlyOutpoints
2023-11-15 11:14:55 +09:00
nicolas.dorier
a858d56552
Allow creation of PSBTs conflicting with mempool with includeOnlyOutpoints 2023-11-15 11:05:24 +09:00
Nicolas Dorier
06ad54141e
Merge pull request #377 from Kukks/add-input-index-to-output
Add InputIndex to matched inputs
2023-11-15 11:03:25 +09:00
Kukks
ef84e74bbb
Add InputIndex to matched inputs 2023-11-15 10:40:36 +09:00
nicolas.dorier
3579fcd226
Fix: Confirmation status of unrelated double spend txs was not updated 2023-11-15 09:29:46 +09:00
Nicolas Dorier
fbf8787ad0
Merge pull request #438 from dgarage/cleanup
Code cleanup
2023-11-14 19:15:57 +09:00
nicolas.dorier
f6e1a5c4df
Remove unused code 2023-11-14 19:10:50 +09:00
nicolas.dorier
e03ef32ab9
Remove unused usings 2023-11-14 18:11:52 +09:00
nicolas.dorier
26f23c0e75
Remove #if for .net core 2.1 2023-11-14 17:39:54 +09:00
Nicolas Dorier
299032a210
Merge pull request #436 from dgarage/qointqqlc
Properly detect double spending even if not directly related to a wallet
2023-11-14 09:04:22 +09:00
nicolas.dorier
f2b91e3513
Properly detect double spending even if not directly related to a wallet 2023-11-13 21:08:42 +09:00
nicolas.dorier
c7b5a73ea1
bump 2023-11-09 10:33:25 +09:00
Nicolas Dorier
4385f75d39
Merge pull request #435 from NicolasDorier/qptnnq
Refactor Broadcaster
2023-11-09 10:13:57 +09:00
nicolas.dorier
a9059c1fba
Fix: Transactions not properly marked replaced (Fix #421) 2023-11-09 10:13:23 +09:00
nicolas.dorier
50532c4a92
Refactor Broadcaster 2023-11-08 08:39:04 +09:00
nicolas.dorier
73e6100a19
Fix rejecting replacement RPC message detection 2023-11-07 09:35:16 +09:00
nicolas.dorier
ebec46553c
Better detect replaced transactions 2023-11-06 09:18:10 +09:00
Nicolas Dorier
7cd88f88da
Merge pull request #432 from bitcoinbrisbane/patch-1
Update API.md
2023-10-19 16:57:29 +09:00
Lucas Cullen
0534c50898
Update API.md
Update grammar
2023-10-15 10:52:23 +10:00
nicolas.dorier
70d608bc09
Fix ElementsTests 2023-10-11 23:11:34 +09:00
nicolas.dorier
d0ad70f6cb
Fix occasional fetch_matches if temp tables are partially created 2023-09-27 16:04:53 +09:00
Nicolas Dorier
f35be21e74
Merge pull request #425 from farukterzioglu/output-below-dust
Output below dust
2023-09-26 17:14:18 +09:00
nicolas.dorier
faf7621545
If sweep funds without coins should send not-enough-funds error 2023-09-26 16:45:41 +09:00
Nicolas Dorier
092018cb6f
Merge pull request #427 from farukterzioglu/merge-outputs
mergeOutputs documentation
2023-09-26 10:53:23 +09:00
Faruk Terzioglu
c2c4cb7709 Add Reason to NBXplorerError 2023-09-14 00:28:42 +03:00
Faruk Terzioglu
994dbfe55b mergeOutputs 2023-09-13 09:21:23 +03:00
Faruk Terzioglu
b1a14c1bd3 Merge branch 'master' of https://github.com/dgarage/NBXplorer into output-below-dust 2023-09-13 09:00:43 +03:00
Nicolas Dorier
256f62385b
Merge pull request #426 from farukterzioglu/merge-outputs
Add MergeOutputs parameter to CreatePSBT
2023-09-13 08:47:29 +09:00
Faruk Terzioglu
bc92de9db4 Add MergeOutputs parameter to CreatePSBT 2023-09-12 19:29:31 +03:00
Faruk Terzioglu
8ada11e7cb
throws output-below-dust before not-enough-funds 2023-09-12 14:56:41 +03:00
Faruk Terzioglu
f9a5cdc999 Handle outputs below dust 2023-09-07 18:40:45 +02:00
Nicolas Dorier
898258d87f
Merge pull request #424 from dgarage/qoitbq
bump NBitcoin
2023-09-01 15:55:58 +09:00
nicolas.dorier
f07649fe1c
bump NBitcoin 2023-09-01 15:32:14 +09:00
Nicolas Dorier
9dfd5f6adf
Merge pull request #417 from gruve-p/patch-3
Bump GRS test to 25.0
2023-08-30 13:46:23 +09:00
nicolas.dorier
cfb9aa9506
Do not remove a tx from mempool if fee too low for mempool 2023-08-23 15:25:33 +09:00
Nicolas Dorier
d7c2da3f11
Merge pull request #419 from NicolasDorier/feiowtnq
Allow SOCKSv5 configuration for P2P connection
2023-06-26 11:26:59 +09:00
nicolas.dorier
9998c66492
Fix CI 2023-06-26 11:22:58 +09:00
nicolas.dorier
5437af5975
Allow SOCKSv5 configuration for P2P connection 2023-06-23 23:04:19 +09:00
nicolas.dorier
a8c5bdc615
Fix tests 2023-06-14 19:57:16 +09:00
nicolas.dorier
67edae79d0
Do not crash if loadwallet is sending RPC_WALLET_ALREADY_LOADED error 2023-06-14 19:27:58 +09:00
Nicolas Dorier
32b048f0b6
Can build NBXplorer without DBTrie support (#418) 2023-06-14 14:26:55 +09:00
gruve-p
ab8c365ae5
Bump GRS test to 25.0 2023-06-09 20:08:54 +02:00
nicolas.dorier
919a1f68d7
bump 2023-06-01 19:06:15 +09:00
nicolas.dorier
d42378ff91
Do not use ReadOnlyDictionary for GenerateWalletRequest 2023-06-01 19:06:03 +09:00
nicolas.dorier
b287ea5521
bump nbitcoin 2023-06-01 18:21:56 +09:00
nicolas.dorier
91e76440d0
Fix crash during migration if not using public schema 2023-05-30 20:54:22 +09:00
nicolas.dorier
64376a41e1
bump 2023-05-30 10:49:36 +09:00
Nicolas Dorier
369d3579e9
Improve fetch_matches performances (#416) 2023-05-30 10:49:10 +09:00
nicolas.dorier
795af96df2
bump lib 2023-05-09 22:03:54 +09:00
nicolas.dorier
28db6d419f
bump 2023-05-09 22:03:33 +09:00
Nicolas Dorier
f3d4508e69
Fix: Timeout was possible when trimming event table at startup (#415) 2023-05-09 22:03:17 +09:00
Nicolas Dorier
b64b7abc04
Fix: If too much mempool activity, NBX would become stuck processing transactions (#414) 2023-05-09 22:02:43 +09:00
Nicolas Dorier
64cfb2fc90
Add replaced tx ids in transaction events (#413) 2023-04-29 22:21:51 +09:00
nicolas.dorier
06aa2b5dcc
Improve grammar 2023-04-10 12:50:49 +09:00
nicolas.dorier
9eeb5c6d82
The Rebroadcasted HostedService shouldn't start with postgres backend 2023-04-10 12:40:10 +09:00
nicolas.dorier
8768d129b6
Run up to 3 periodic tasks concurrently 2023-04-10 12:36:09 +09:00
Nicolas Dorier
a064c60932
Add replacedBy to cryptos/{cryptoCode}/transactions/{txId} (#412) 2023-03-25 10:42:16 +09:00
nicolas.dorier
f2e9615f9b
bump NBX 2023-03-08 16:58:51 +09:00
nicolas.dorier
7dd960dfee
Add log if txindex is supported 2023-03-08 16:57:50 +09:00
nicolas.dorier
578426da15
Reboot connection if node start sending block announcement with invs 2023-03-08 16:54:51 +09:00
nicolas.dorier
8d09c57c7d
Automatically detect txindex availability when possible 2023-02-15 10:00:18 +09:00
nicolas.dorier
db3142b92f
Remove SupportCookieAuthentication 2023-02-15 09:40:38 +09:00
nicolas.dorier
4a0e16861b
Fix possible error 500 in CreatePSBT 2023-02-13 21:54:02 +09:00
nicolas.dorier
b73cb2e110
bump 2023-02-11 19:46:04 +09:00
Nicolas Dorier
ad90a241a6
Add eventId to real time notifications (Close #393) (#405) 2023-02-11 19:45:33 +09:00
nicolas.dorier
7045800e48
Fix possible error 500 if no more address in the change address pool (Fix #403) 2023-02-11 19:35:13 +09:00
nicolas.dorier
b5592e0106
Do not send mempool message on p2p on dbtrie backend 2023-02-07 22:26:22 +09:00
nicolas.dorier
4874373783
bump 2023-02-07 15:18:44 +09:00
Nicolas Dorier
194f16c7a5
Fix: Wallets migrated from DBTrie might reuse change address (#402) 2023-02-07 15:18:03 +09:00
nicolas.dorier
c7b6885eed
Fix: Make it impossible to cancel reservation on an address already seen on-chain 2023-02-07 12:59:21 +09:00
nicolas.dorier
b0b7e98cb9
Fix tests 2023-02-06 14:25:23 +09:00
nicolas.dorier
1c535e62d4
Bump NBX 2023-02-02 08:54:36 +09:00
Andrew Camilleri
bdd32e1147
add getmempoolinfo to whitelist (#397) 2023-02-02 08:52:54 +09:00
nicolas.dorier
45f9a4adc9
bump 2023-01-18 21:22:15 +09:00
Nicolas Dorier
f28bb22c7f
Allow some RPC methods to be called even if RPC not explicitely exposed by NBX (#396) 2023-01-18 21:21:43 +09:00
nicolas.dorier
000376564e
bump client 2023-01-16 10:07:21 +09:00
nicolas.dorier
416a0e7bd2
bump 2023-01-16 09:57:23 +09:00
nicolas.dorier
1f18ac5bd2
Bump NBitcoin (Fix PSBT parsing for some altcoins) 2023-01-16 09:56:38 +09:00
nicolas.dorier
8332e05023
bump 2023-01-13 11:16:25 +09:00
Nicolas Dorier
639ad9ba45
tracked_txs shouldn't return transaction not in mempool and not confirmed (#392) 2023-01-13 11:15:41 +09:00
gruve-p
8e069c5b9b
Bump GRS to v24.0.1 on ServerTester.Environment (#390) 2022-12-16 12:28:58 +09:00
nicolas.dorier
33bfb8b9e7
Fix flaky test 2022-12-12 20:57:13 +09:00
nicolas.dorier
15f6c42245
bump 2022-12-12 20:55:00 +09:00
Nicolas Dorier
23a8e335e4
Save birthdate of wallet on GenerateWallet (#391) 2022-12-12 20:55:00 +09:00
nicolas.dorier
0801eca1eb
Do not import in RPC if not asked 2022-12-12 20:55:00 +09:00
nicolas.dorier
c6ae43adb2
bump 2022-12-12 16:11:22 +09:00
Nicolas Dorier
0a09a4ba1e
NBXplorer create default RPC wallet when possible (#389) 2022-12-12 16:10:21 +09:00
nicolas.dorier
ebac7ce987
Recalculate addresses rather than joining a table, return blinded addresses in getUtxos 2022-12-09 12:26:00 +09:00
nicolas.dorier
a4456088e8
Bump client 2022-12-08 17:35:35 +09:00
nicolas.dorier
a906570f08
bump 2022-12-08 17:35:14 +09:00
nicolas.dorier
6becdca483
Include Address in the Get UTXOs route 2022-12-08 17:35:04 +09:00
nicolas.dorier
086fc3b42c
Add Address to txevent.outputs 2022-12-08 16:58:34 +09:00
nicolas.dorier
b9fd115c6c
Add confirmations number to block event 2022-12-08 10:32:44 +09:00
nicolas.dorier
844af377e2
bump 2022-12-08 10:19:14 +09:00
nicolas.dorier
d17f60913a
Fix: Confirmation number of tx event were incorrect (Fix #388) 2022-12-08 10:18:39 +09:00
nicolas.dorier
19c57353e6
If blocks isn't downloaded after querying, connection should be restarted 2022-11-29 21:16:19 +09:00
Nicolas Dorier
a993e45266
bump NBitcoin.TestFramwork on 24.0 (#386) 2022-11-26 00:32:41 +09:00
nicolas.dorier
4e13f90605
Can disable block mining on regtest via NBXPLORER_NOWARMUP=1 2022-11-23 11:21:42 +09:00
nicolas.dorier
8a6f6dee9c
Fix error if too low amount sent on an output with substractfee 2022-11-16 08:07:38 +09:00
nicolas.dorier
00df1e03be
Fix error if too low amount sent on an output with substractfee 2022-11-15 23:26:28 +09:00
nicolas.dorier
f063979667
Stop asking mempool connection after connecting to a node 2022-11-15 23:06:11 +09:00
nicolas.dorier
9f691db055
bump 2022-11-14 19:09:17 +09:00
nicolas.dorier
07323f16ea
Fix potential invalid evts_ids after migration 2022-11-14 19:04:11 +09:00
nicolas.dorier
1ddf28ee2a
Update doc 2022-11-10 21:24:34 +09:00
nicolas.dorier
fd61110a1e
Throw output-too-small if SubstractFee is called on an output that doesn't have enough funds 2022-11-10 21:15:13 +09:00
nicolas.dorier
754278f473
bump NBitcoin 2022-11-02 13:26:00 +09:00
nicolas.dorier
af2ee4c09c
Fix spurious problem when importing descriptors 2022-10-25 09:05:10 +09:00
nicolas.dorier
381c90fc0d
Fix error on RPC import descriptors 2022-10-24 23:49:39 +09:00
nicolas.dorier
030084cdfa
Do not timeout on wallets_history_refresh 2022-10-20 18:21:39 +09:00
nicolas.dorier
38ddc7d211
Fix tests 2022-10-18 21:46:43 +09:00
nicolas.dorier
a1205d8f97
Support saving keys in descriptors wallets 2022-10-18 21:25:45 +09:00
nicolas.dorier
f381e00023
Fix SQL query in postgres when inserting event 2022-10-18 13:37:47 +09:00
nicolas.dorier
4447eea417
Bump Bitcoin Core in tests 2022-10-18 12:59:15 +09:00
nicolas.dorier
2fb8e2630f
Fix Liquid regression where blinded transaction couldn't get decoded 2022-10-11 23:29:58 +09:00
nicolas.dorier
f7649f490a
bump packages 2022-10-07 14:50:55 +09:00
nicolas.dorier
916329e4ff
Fix performance regression on get_wallet_recent 2022-09-27 21:52:25 +09:00
nicolas.dorier
788e800b44
Bump NBitcoin, fixing qtum and dash 2022-09-22 15:15:31 +09:00
nicolas.dorier
51a300b70d
GetMetadata shouldn't crash if RPC not available 2022-09-22 14:30:09 +09:00
nicolas.dorier
848c6c2665
bump 2022-09-22 13:05:09 +09:00
nicolas.dorier
9d601ec868
bump NBitcoin.Altcoins, fixing dash issue 2022-09-22 13:04:44 +09:00
nicolas.dorier
2a493b911a
Do not crash if p2p node != from RPC node 2022-09-20 22:20:52 +09:00
d11n
da8b7b7653
Remove reference to design doc (#374)
See [this comment](https://github.com/btcpayserver/btcpayserver-doc/pull/1160#issuecomment-1248970919).
2022-09-16 17:40:28 +09:00
d11n
5cc4389cda
Docs: References and formatting (#371)
Some minor updates for better display on the docs website. See btcpayserver/btcpayserver-doc#1160.
2022-09-16 15:30:38 +09:00
nicolas.dorier
b3fcda0cf6
bump 2022-09-10 16:26:17 +09:00
nicolas.dorier
7cf5f33443
Fix get_wallets_recents not returning correct set with offset!=0 2022-09-10 16:25:28 +09:00
nicolas.dorier
b8a4a38184
Increase timeout when querying blockchaininfo 2022-07-29 14:51:36 +09:00
nicolas.dorier
dda0bac750
Fix timeout 2022-07-28 17:41:30 +09:00
nicolas.dorier
713ff5e256
bump 2022-07-28 17:31:27 +09:00
nicolas.dorier
4d32024b90
Increase timeout when running migration scripts 2022-07-28 17:30:58 +09:00
nicolas.dorier
bc72700847
bump various deps 2022-07-23 23:09:22 +09:00
nicolas.dorier
969b73a49e
Bump NBitcoin 2022-07-20 22:42:57 +09:00
nicolas.dorier
cd1f5df749
Remove outdated banlist (Fix #367) 2022-07-15 13:13:28 +09:00
d11n
2075f2200f
Update Postgres-Migration.md (#366)
See https://github.com/btcpayserver/btcpayserver/pull/3876#issuecomment-1157549182
2022-06-17 19:01:39 +09:00
nicolas.dorier
05235a6de0
Bump deps of example code 2022-06-16 21:01:18 +09:00
nicolas.dorier
5238f1f2c7
bump deps 2022-06-16 20:54:46 +09:00
nicolas.dorier
c8e9d2f135
Remove docker experimental flag in CI 2022-06-15 19:29:15 +09:00
nicolas.dorier
72830a49c4
Crash test if scanutxoset is stalling 2022-06-15 19:17:22 +09:00
nicolas.dorier
4128f8b068
Bump NBitcoin 2022-06-15 18:46:35 +09:00
nicolas.dorier
0939c00d13
Fix CI 2022-06-06 13:49:07 +09:00
nicolas.dorier
8e060f545d
Add full schema 2022-06-06 13:05:23 +09:00
nicolas.dorier
bbcb4e53e8
Fix: High postgres memory consumption for long rescan 2022-06-06 11:30:09 +09:00
nicolas.dorier
af9d7ce7a5
Make nbxv1_evts.id BIGINT 2022-06-01 12:53:28 +09:00
nicolas.dorier
0595a87f22
Rescan API should save blocks of imported tx in the repo 2022-05-31 12:54:23 +09:00
nicolas.dorier
a1dead87c3
Fix flaky tests 2022-05-30 16:49:10 +09:00
nicolas.dorier
e4700d21db
Fix query 2022-05-24 19:22:47 +09:00
nicolas.dorier
52585b3348
Small perf improvement 2022-05-24 14:45:05 +09:00
Marcelo Ceccon
4aa2cce05c
Update README.md (#363)
Just updating to make appropriate mention of the new Postgres support.
2022-05-24 14:30:52 +09:00
nicolas.dorier
37c49b356f
Fix: descriptors_scripts_unused was causing a full seq scan 2022-05-23 13:14:35 +09:00
nicolas.dorier
ce7819bdc3
Note about backends in README 2022-05-17 12:04:15 +09:00
Nicolas Dorier
d0bb7cf9ac
Do not lose timestamp information when scanutxo (#361)
* Do not lose timestamp information when scanutxo

* bump
2022-05-16 18:47:07 +09:00
nicolas.dorier
05e443fa38
Scanning unknown blocks with scan utxo shouldn't result in unconf utxos 2022-05-11 23:07:28 +09:00
nicolas.dorier
3692c797fc
CreatePSBT shouldn't select utxos with too many ancestors 2022-05-05 13:38:10 +09:00
nicolas.dorier
ea99e3cc9e
bump 2022-05-03 16:01:15 +09:00
nicolas.dorier
0b659c82e2
Fix migration for one user with corrupt DBTrie 2022-05-03 16:01:03 +09:00
nicolas.dorier
f8b4c650ba
bump 2022-04-28 22:14:09 +09:00
nicolas.dorier
ba274bfeb1
Missing clause in NewBlockCommit 2022-04-27 12:09:27 +09:00
nicolas.dorier
afb16b5890
Update doc (Fix #360) 2022-04-23 21:01:36 +09:00
nicolas.dorier
9091abff44
Throw an error if two backend are selected 2022-04-20 19:00:17 +09:00
nicolas.dorier
1fff142f6f
Improve performance on get unused of big wallets 2022-04-19 14:45:06 +09:00
nicolas.dorier
9a08aef0fb
Improve perf for CreatePSBT on SegwitP2SH with postgres 2022-04-14 16:02:18 +09:00
nicolas.dorier
229bf48bee
Fix migration when using CustomKeyPathTemplate 2022-04-13 13:13:34 +09:00
nicolas.dorier
55fbdcb9cd
Fix: Crash if wrong case on some routes 2022-04-11 22:12:15 +09:00
nicolas.dorier
53eb9d08b8
Bump timeout for vacuum/analyze after migration 2022-04-11 21:52:22 +09:00
nicolas.dorier
9d235e28dd
Fix flaky test 2022-04-11 21:51:21 +09:00
Nicolas Dorier
9fe95038a4
Make save_matches faster (#358) 2022-04-08 15:18:49 +09:00
nicolas.dorier
893ea4eb94
On regtest, still consider as synched if 200 blocks late 2022-04-08 12:59:03 +09:00
nicolas.dorier
fea82a5f57
Fix: NBXplorer not showing sync progress (for real) 2022-04-07 17:15:46 +09:00
nicolas.dorier
f7aaa1ae91
Fix: NBXplorer not showing sync progress 2022-04-06 19:04:13 +09:00
nicolas.dorier
bdd741e753
Add GetWalletsRecents filtered by code 2022-04-06 16:27:10 +09:00
nicolas.dorier
79fbbbf523
Fix to_btc helper function 2022-04-06 14:05:08 +09:00
nicolas.dorier
86d00dd152
Fix: Block could be skipped if crash during processing 2022-04-06 10:30:08 +09:00
nicolas.dorier
e15c7c3969
Fix timeout on vacuum 2022-04-05 23:41:57 +09:00
nicolas.dorier
273fffa950
Fix get_wallets_histogram 2022-04-05 20:47:37 +09:00
nicolas.dorier
ee8f65b251
bump 2022-04-05 13:32:26 +09:00
nicolas.dorier
8707ac15a6
Fix case where DBTrie doesn't create change descriptor (#357) 2022-04-05 13:23:27 +09:00
nicolas.dorier
cb5877d959
Generate addresses if get unused return null (Fix #357) 2022-04-04 23:43:17 +09:00
nicolas.dorier
813a49ad99
Bump 2022-04-04 14:23:24 +09:00
Andrew Camilleri
acf2db03e2
Liquid: Support Slip77 derivation for blinding keys (#353) 2022-04-04 14:16:02 +09:00
nicolas.dorier
ccac0d6463
Fix flaky test 2022-04-04 14:13:35 +09:00
nicolas.dorier
a03a897a0b
Fix test 2022-04-04 14:04:18 +09:00
nicolas.dorier
4f9a0688b6
Remove string concat for query building 2022-04-04 11:58:01 +09:00
nicolas.dorier
3944aca2c0
Fix exception thrown in GetTransactions, run full vacuum after migration 2022-04-04 11:15:02 +09:00
nicolas.dorier
4a35012713
GC collect after migration 2022-04-04 00:26:51 +09:00
nicolas.dorier
88a437fc60
Fix error 500 on invalid network 2022-04-03 19:45:01 +09:00
nicolas.dorier
6c708a4c07
Fix warmup regtest 2022-04-03 19:27:13 +09:00
nicolas.dorier
79c9196f22
Fix schema link 2022-04-03 19:13:15 +09:00
Nicolas Dorier
7d44d4cb74
Support postgres backend (#356) 2022-04-03 19:07:29 +09:00
nicolas.dorier
89ada4a172
Fix potential NRE 2022-01-21 21:39:48 +09:00
nicolas.dorier
b96763eca4
Fix dockerfile 2022-01-19 12:08:01 +09:00
nicolas.dorier
6454ffcc80
Delete the event table if corrupted 2022-01-19 12:03:36 +09:00
gruve-p
b42e5dc031
Fix warning from CLI parser (#351)
`-p` is deprecated as an abbreviation for `--project`, and using `-p` generates a warning.
2022-01-03 17:37:04 +09:00
nicolas.dorier
de6d25edf7
Update README 2021-12-28 17:41:26 +09:00
nicolas.dorier
edf3fab86a
Remove outdated coments 2021-12-27 14:50:43 +09:00
nicolas.dorier
3726a56374
Remove app veyor 2021-12-27 14:49:37 +09:00
Nicolas Dorier
2a610db2d3
bump nbx to 6.0 (#350) 2021-12-27 14:45:34 +09:00
Reza Ahmadi
8d993ff85f
Update README.md (#348) 2021-12-14 19:14:24 +09:00
nicolas.dorier
ebb2ef5df7
bump 2021-11-10 18:46:01 +09:00
nicolas.dorier
0ecbb03e17
Preallocate chain during before synching header 2021-11-10 18:38:30 +09:00
nicolas.dorier
70f318feb1
Fix: crash when parsing long configuration value 2021-11-09 18:32:50 +09:00
nicolas.dorier
abadb08b61
bump 2021-10-27 14:40:36 +09:00
Jonathan Underwood
8a596e3314
Feature: Config RescanIfTimeBefore (#346) 2021-10-27 14:37:52 +09:00
nicolas.dorier
310098706d
bump NBitcoin 2021-10-23 21:42:19 +09:00
nicolas.dorier
a4644e8ba5
Revert bulleyes to buster for arm32 2021-10-23 21:40:39 +09:00
nicolas.dorier
621954d176
Fix SaveMatches crashing if there is too much to index in a single transaction 2021-10-21 00:05:09 +09:00
nicolas.dorier
350972e4b9
bump NBitcoin 2021-10-12 15:40:23 +09:00
nicolas.dorier
b5252c83b5
Fix docker images 2021-10-11 20:30:07 +09:00
nicolas.dorier
56d89517a9
Fix docker images 2021-10-11 20:19:42 +09:00
nicolas.dorier
6efc72db0a
bump NBitcoin 2021-10-11 20:17:48 +09:00
nicolas.dorier
9f61543120
bump docker images and libraries 2021-10-05 15:31:46 +09:00
nicolas.dorier
358c487a89
Cache available address count only if above 1000 2021-09-28 23:19:51 +09:00
nicolas.dorier
ecbec0c70b
Bitcoin NBitcoin and Bitcoin core 2021-09-27 18:22:36 +09:00
nicolas.dorier
4e8b803777
Implement count caching for large address pool 2021-09-24 17:53:43 +09:00
nicolas.dorier
ad75936d7c
Do not fix SelectForwardSkip for old tables 2021-09-21 20:50:07 +09:00
nicolas.dorier
8d94a3e05f
Fix some corrupted data from old install during the migration 2021-09-21 20:47:05 +09:00
nicolas.dorier
7b62008830
Fix: NBXplorer would match unrelated transactions 2021-09-21 19:04:05 +09:00
nicolas.dorier
74236a314e
Perf improvement, avoid uneeded collection copy 2021-09-21 16:47:58 +09:00
nicolas.dorier
a1fd3e5b82
Bump NBitcoin for fix liquid bug 2021-09-17 12:10:40 +09:00
nicolas.dorier
f17b5a4227
bump 2021-09-16 23:02:45 +09:00
Andrew Camilleri
1ef9af916b
Fix Liquid support (#340)
* Ensure Transactions are stored correctly

* Fix mutattion issue with Transaction in serializer

* add more tests

* fix mutation issue closer to source and bump nbitcoin altcoins

* Revert

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2021-09-16 22:59:01 +09:00
nicolas.dorier
ea00dd0276
bump 2021-09-11 14:00:19 +09:00
nicolas.dorier
ef95dda31d
Bump DBTrie 2021-09-11 13:59:39 +09:00
nicolas.dorier
94d9a58b12
bump NBitcoin.Altcoins 2021-09-10 21:34:57 +09:00
gruve-p
77c2d8ac8e
Use same min rpc version as Bitcoin (#337)
This is needed as the upcoming GRS version (v22.0.0) has `MinRPCVersion = 220000,`
2021-09-10 14:54:38 +09:00
nicolas.dorier
ad1baa5bde
Refactor ExplorerBehavior to be sure to process blocks in order with parallel download 2021-09-02 17:23:12 +09:00
nicolas.dorier
d466b64836
Add wipe feature 2021-09-02 13:13:23 +09:00
nicolas.dorier
2528842acb
Process block one by one 2021-09-02 12:47:42 +09:00
nicolas.dorier
cba0150fc4
Fix possible memory exhaustion on pruning 2021-09-01 16:54:07 +09:00
nicolas.dorier
23acb53e84
Block hanging error might happen by mistake 2021-09-01 12:43:33 +09:00
nicolas.dorier
ee85a6d238
bump 2021-08-31 19:23:07 +09:00
nicolas.dorier
a80cce1ee6
Throw NotSupportedException if unsupported ScriptPubKeyType 2021-08-31 19:22:47 +09:00
Manan Sharma
1e4c4df6fb
Added taproot derivation keypath (#336) 2021-08-31 19:19:09 +09:00
nicolas.dorier
3cc354160b
Add taproot to doc 2021-08-30 11:43:13 +09:00
nicolas.dorier
65becaf257
bump 2021-08-30 11:40:35 +09:00
nicolas.dorier
ecf1af0224
Keep support netstandard2.0 2021-08-30 11:38:08 +09:00
nicolas.dorier
4604485ce3
Minor test update 2021-08-30 11:21:03 +09:00
Manan Sharma
7c5d4942b8
Add taproot derivation scheme (#335)
* Add taproot derivation scheme

* Modifies the API docs

* Minor Change
2021-08-30 11:17:22 +09:00
nicolas.dorier
76345c74c6
Fix: some output spending would fail to be detected 2021-08-25 21:03:11 +09:00
Nicolas Dorier
5283ea3c4a
Save TxOut in the Outpoint index (#333) 2021-08-24 16:47:40 +09:00
Manan Sharma
e7e1e29336
Populate OutPoint table from existing Transactions table. (#330)
* Added cred for Bitcoin RPC

* Draft implementation for Outpoint migration

* Fixed a bug

* Improve logging

* Testing data for outpoint migration

* Create test for outpoint migration

* Restart the migration if crashed

* Fixed a bug

* Simplify code

* Fix bug when having pruned tracked transaction

* Add test covering pruned tracked transactions

* Fix expectations of CanMigrateOutPointsFromTransactions

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2021-08-24 16:31:18 +09:00
Andrew Camilleri
dd01fb7835
bump nbitcoin (#332) 2021-08-24 10:35:49 +09:00
Manan Sharma
8c2dbd5808
Get KeyPathInformations from OutPoints (#329)
* Draft function for getting keyinfo from outpoints

* Get GetMatches ready for taproot

* Created a function for getting outpoint index

* Populate the outpoint index in UpdateRepository

* Create a Function to populate OutPoint index

* Fixed a bug in writing to the outpoint index

* Update outpoint index when saving txns

* Make GetKeyInformations fetch from outpoint index

* Some Clean up

* Fixed a bug

* Removed unnecessary complexity

* Factored code of populating outpoint index

* Code clean up

* Fetching function that also updates the index

* ExtractInfo also provides the mappings

* Outpoint index only saves scripts

* Access the data via a join

* Read & Write Script

* Fixed a bug

* Code clean up

* Removed a bug

* Avoid some allocations

* Save outpoint mapping only in SaveMatches

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2021-08-18 14:57:11 +09:00
nicolas.dorier
b4237b59ce
bump NBitcoin 2021-08-11 12:38:03 +09:00
nicolas.dorier
e2bd17b5b7
Revert "Revert "Do not attempt defrag if it crashed once""
This reverts commit 18471e33af.
2021-08-10 11:20:35 +09:00
nicolas.dorier
b491a6b10d
Revert "Fallback on unsafe defrag if the previous defrag failed"
This reverts commit aac4bc7060.
2021-08-10 11:20:32 +09:00
nicolas.dorier
aac4bc7060
Fallback on unsafe defrag if the previous defrag failed 2021-08-10 11:07:40 +09:00
nicolas.dorier
18471e33af
Revert "Do not attempt defrag if it crashed once"
This reverts commit 2b96fe49f7.
2021-08-10 11:00:25 +09:00
nicolas.dorier
2b96fe49f7
Do not attempt defrag if it crashed once 2021-08-10 10:13:16 +09:00
nicolas.dorier
6250a78430
Update tested bitcoin core version 2021-08-05 13:36:40 +09:00
nicolas.dorier
04dfb92d43
Can get taproot support from API 2021-08-05 13:15:16 +09:00
nicolas.dorier
963f2cb914
Bump NBitcoin (support taproot for signet) 2021-08-01 00:26:31 +09:00
Nicolas Dorier
b84337fccf
Document and properly test immature transaction handling (#324) 2021-07-31 20:59:53 +09:00
Manan Sharma
09efceaeea
Support for Immature Coin Base Transaction (#323)
* Added Immature transaction annotation

* Segregate immature and Conf txn

* Immature txn in txn response

* Get Immature txns in Gettransactions

* Return Immature in GetBalance

* Fixed Total Balance counting bug

* Removed deprecated env vaiables
2021-07-31 10:51:53 +09:00
nicolas.dorier
853955171e
Update NBitcoin 2021-07-28 21:10:46 +09:00
Nicolas Dorier
9ba550201c
Merge pull request #319 from Kukks/exposepercurrency
Allow exposing RPC per node
2021-07-14 12:10:30 +09:00
Kukks
3c8c0bb1e0 Allow exposing RPC per node 2021-07-12 09:18:46 +02:00
nicolas.dorier
998f598e2c
Update NBitcoin 2021-07-08 17:13:10 +09:00
Nicolas Dorier
f0260bf794
Merge pull request #316 from soenkehahn/patch-1
Fix typo
2021-06-06 20:54:42 +09:00
Sönke Hahn
58cc70b7bc
Fix typo 2021-06-03 15:54:23 -04:00
nicolas.dorier
9a711cb1aa
bump NBitcoin 2021-05-18 11:44:22 +09:00
Nicolas Dorier
f45e49bc42
Merge pull request #314 from dalijolijo/btx-v0.90.9.10
Update BitCore version to 0.90.9.10
2021-05-17 11:31:26 +09:00
dalijolijo
4c477aeab8 Update BitCore version to 0.90.9.10 2021-05-03 23:56:44 +02:00
Nicolas Dorier
5428ed0caa
Merge pull request #313 from denuoweb/master
bump version
2021-04-23 17:48:32 +09:00
nicolas.dorier
f8f58036db
bump 2021-04-23 16:02:06 +09:00
nicolas.dorier
1527584e08
Remove network type check, as it is a breaking change with liquid 2021-04-23 16:01:11 +09:00
Jaron Rosenau
b3e3c673c3
bump version
need this bumped so that btcpayserver can pass the pull test
2021-04-19 22:44:21 -07:00
nicolas.dorier
7912fafa85
bump 2021-04-19 12:59:21 +09:00
nicolas.dorier
6823a7009d
update NBitcoin 2021-04-19 12:53:59 +09:00
nicolas.dorier
7c5c43dd14
Fix bug where PSBTUpdate would fail to fill WitnessUtxo properly 2021-04-19 12:50:44 +09:00
nicolas.dorier
ee9dcbf5d5
Better error message when network config mismatch 2021-04-08 16:29:52 +09:00
nicolas.dorier
c9171b4d24
Add Examples 2021-03-11 23:59:12 +09:00
Nicolas Dorier
2ab475b351
Merge pull request #307 from denuoweb/master
add altcoin support for althash
2021-03-08 16:42:40 +09:00
Jaron Rosenau
16b060f6dc
bump nbitcoin to 5.0.76 2021-03-07 16:44:15 -08:00
Jaron Rosenau
da32bfbab1
bump nbitcoin.altcoin version to 2.0.30 2021-03-07 16:28:22 -08:00
Jaron Rosenau
48e9e4a4fd
bump nbitcoin.altcoin version 2021-03-05 04:31:50 -08:00
Jaron Rosenau
93b4e77ca0
correct keypath value 2021-02-23 23:02:43 -08:00
Jaron Rosenau
1726bbcdca
keypath update 2021-02-13 01:06:58 -08:00
Jaron Rosenau
8bc1c46163
fix keypath to internal 2021-02-12 09:13:56 -08:00
Jaron Rosenau
eb9b7caed7
fix MinRPCVersion 2021-02-11 03:26:24 -08:00
Jaron Rosenau
b215e91876
switch coin type to be external 2021-02-10 00:26:59 -08:00
Jaron Rosenau
546ee710b5
add Althash 2021-01-31 10:54:33 -08:00
Jaron Rosenau
7142581ac0
add Althash 2021-01-31 10:53:28 -08:00
Jaron Rosenau
80faf48bfe
add Althash, RPCVersion, Keypaths 2021-01-31 10:52:32 -08:00
nicolas.dorier
45819a7c47
remove stable and latest branch 2021-01-28 22:50:39 +09:00
nicolas.dorier
cde241b3f5
Fix incorrect warning about node blacklisting 2021-01-26 11:34:52 +09:00
nicolas.dorier
c4e25d3894
allow -signet 2021-01-25 19:19:43 +09:00
nicolas.dorier
4a32c51584
bump 2021-01-25 19:01:03 +09:00
nicolas.dorier
7e9fa2cea9
Bump NBitcoin and support signet 2021-01-25 18:59:57 +09:00
nicolas.dorier
4fb78e9cc3
fix CI 2020-12-07 20:31:05 +09:00
nicolas.dorier
4d60e2b62e
Make sure does not crash on bitcoin core 0.21 2020-12-07 20:27:33 +09:00
nicolas.dorier
6ae1b8c0bb
Do not crash if not send RPC_INVALID_PARAMETER in getblockheader 2020-12-02 20:14:58 +09:00
Nicolas Dorier
f8859115a9
Merge pull request #297 from CodeFunta/feature/nbxplorer_client_getbalance_by_address
add GetBalance by address methods
2020-11-24 13:20:56 +09:00
Andrey Litvinov
1d68c24277 change GetBalanceAsync to use BitcoinAddress 2020-11-23 15:39:20 +03:00
Andrey Litvinov
20a0b98146 add GetBalance by address methods 2020-11-22 20:40:59 +03:00
nicolas.dorier
98bbdc3d0e
update testframework 2020-11-11 12:14:21 +09:00
nicolas.dorier
0e7199a661
Fix potential double spend crash 2020-10-29 22:01:20 +09:00
nicolas.dorier
592233ac0a
bump nbx 2020-10-24 15:19:43 +09:00
nicolas.dorier
70b54c1f85
Fix warning 2020-10-24 14:05:54 +09:00
nicolas.dorier
e0354f41c3
bump NBitcoin 2020-10-24 14:00:32 +09:00
nicolas.dorier
56d2bf2904
Update NBitcoin 2020-09-24 14:09:57 +09:00
nicolas.dorier
ce2f21fbc4
bump 2020-09-10 12:11:11 +09:00
nicolas.dorier
cc548a4aba
[Breaking change] Do not include global xpub by default in the PSBT 2020-09-10 12:10:52 +09:00
nicolas.dorier
6675b56132
bump NBitcoin 2020-09-03 23:39:00 +09:00
nicolas.dorier
a33be45253
bump 2020-08-19 11:12:58 +09:00
Nicolas Dorier
a8512a7cea
Merge pull request #290 from prayank23/patch-1
Update README
2020-08-10 22:51:58 +09:00
nicolas.dorier
e4080a658c
bump 2020-08-09 13:54:25 +09:00
nicolas.dorier
8b6ec4ae94
Bump NBitcoin to make NBXplorer scan properly capabilities with whitelisting enabled 2020-08-09 13:52:08 +09:00
Prayank
28e7545be5
Update README
Removed 'Bitcoin' from line 32 which was mentioned in altcoins
2020-08-08 16:46:11 +05:30
nicolas.dorier
8576184ded
bump nbx 2020-08-04 13:54:49 +09:00
Nicolas Dorier
b04d4971e2
Merge pull request #286 from sotblad/master
Add MonetaryUnit (MUE) support
2020-08-04 13:53:07 +09:00
Sotiris Blad
83f117a751
Merge branch 'master' into master 2020-08-03 18:47:21 +03:00
Sotiris Blad
0a7d7972f0
Update NBXplorer.csproj 2020-08-03 16:22:49 +03:00
Sotiris Blad
b9543a1430
Update NBXplorer.Tests.csproj 2020-08-03 16:22:20 +03:00
Sotiris Blad
f7077870a7
Update NBXplorer.Client.csproj 2020-08-03 16:21:52 +03:00
nicolas.dorier
860a6c47f8
more resilient test 2020-08-03 16:26:29 +09:00
nicolas.dorier
1436809365
bump 2020-08-03 16:18:24 +09:00
nicolas.dorier
99b072dc75
Fix exception during double spend (fix #288) 2020-08-03 16:17:47 +09:00
nicolas.dorier
82f53b609c
Bump NBitcoin 2020-08-03 13:11:12 +09:00
nicolas.dorier
7c8be9dbc9
bump 2020-08-01 13:30:45 +09:00
nicolas.dorier
49ef619272
Do not update RedeemScript and WitnessScript if already set in PSBT 2020-08-01 12:29:01 +09:00
nicolas.dorier
9ca3665c3d
Improve performance of Create PSBT on wallet with large number of UTXO 2020-08-01 11:32:41 +09:00
nicolas.dorier
4b17554b19
Bump dbtrie 2020-07-29 13:22:45 +09:00
nicolas.dorier
2c1cf53048
Fix: FirstSeen lost if the transaction was not confirmed rapidly. 2020-07-29 13:12:40 +09:00
nicolas.dorier
24ad9535d5
Fix error on broadcast for xamarin (Fix #287) 2020-07-28 11:05:59 +09:00
Sotiris Blad
81a79d864a
trigger tests 2020-07-25 10:58:29 +03:00
Sotiris Blad
77a458a26e
trigger tests 2020-07-25 10:58:05 +03:00
nicolas.dorier
d0dabc96c8
Better way to commit when trimming events 2020-07-16 23:22:37 +09:00
nicolas.dorier
6228d18fb8
Fix defragment not saving enough bytes 2020-07-16 16:36:01 +09:00
nicolas.dorier
80b79e2fa5
bump 2020-07-16 12:31:30 +09:00
Nicolas Dorier
03999d5e98
Merge pull request #282 from dgarage/dbtrie
Change Dbreeze to DBTrie
2020-07-16 12:31:05 +09:00
nicolas.dorier
e4c21e4e8a
Improve migrations on big db 2020-07-16 12:20:53 +09:00
nicolas.dorier
5e3ecee2c0
Trim events when starting 2020-07-16 12:19:21 +09:00
nicolas.dorier
78e87e4a13
Implement saved transaction migration 2020-07-16 12:19:21 +09:00
nicolas.dorier
413533dbdf
Cancel transaction enumeration if too long 2020-07-16 12:16:54 +09:00
nicolas.dorier
71d5c187ea
Change Dbreeze to DBTrie 2020-07-16 12:16:20 +09:00
nicolas.dorier
f437a3365f
Allow tester to keep previous data 2020-07-16 12:14:45 +09:00
nicolas.dorier
8d3bec302d
Improve test on transaction ordering 2020-07-16 12:13:45 +09:00
nicolas.dorier
76ecdb250b
Improve topological sort on large number of transactions 2020-07-16 12:08:17 +09:00
nicolas.dorier
7b3b92b3af
Fix fingerprint distribution crashing on dash (#285) 2020-07-15 11:11:45 +09:00
nicolas.dorier
e85dbe5d28
bump 2020-06-23 10:30:17 +09:00
nicolas.dorier
97a6ae17b7
Fix #275 2020-06-23 10:27:57 +09:00
nicolas.dorier
d526a8ee9a
bump nbitcoin and nbx 2020-06-22 17:18:16 +09:00
nicolas.dorier
4d81308f5b
Fix #271 2020-06-22 17:17:12 +09:00
nicolas.dorier
a38720cbce
bump nbitcoin 2020-06-19 19:04:05 +09:00
Sotiris Blad
015f9b5e32
Monetary unit (#1)
* Create NBXplorerNetworkProvider.MonetaryUnit.cs

* Update NBXplorerNetworkProvider.cs

* Update ServerTester.Environment.cs

* Update README.md
2020-06-17 04:01:10 +03:00
nicolas.dorier
9a36db30fd
bump client 2020-06-11 22:35:02 +09:00
nicolas.dorier
b33af19886
bump 2020-06-11 20:26:24 +09:00
Jonathan Underwood
351b4c7919
Add GetLatestEvents (#268) 2020-06-11 20:25:35 +09:00
Andrew Camilleri
81b7623e54
Allow attempting to set the full tx for segwit inputs (#266)
* Allow attempting to set the full tx for segwit inputs

An new option to allow *attempting* to set `non_witness_utxo` for segwit inputs on PSBT create/update.

* rename and fix text
2020-06-10 09:26:28 +09:00
Dabura667
8715d3d1ba
Add Qtum support (#264) 2020-05-29 10:22:02 +09:00
Oleg Semyonov
7dacd8e892
Add Argoneum (#256)
Co-authored-by: Oleg Semyonov <osnwt@github.com>
2020-05-29 10:21:34 +09:00
nicolas.dorier
3ae2befd91
bump nbitcoin.altcoins 2020-05-27 13:25:34 +09:00
nicolas.dorier
8d989ef3c3
Allow fingerprinting for altcoins 2020-05-27 12:11:20 +09:00
nicolas.dorier
037f753815
bump NBitcoin 2020-05-26 18:03:53 +09:00
nicolas.dorier
46828bd735
Use fingerprint randomization only on BTC 2020-05-26 17:36:04 +09:00
nicolas.dorier
9ffe87a4e3
Fix ObjectDisposedException when CTRL+C (https://github.com/dgarage/NBXplorer/issues/261) 2020-05-26 03:19:14 +09:00
nicolas.dorier
4b1a6e52d5
Only tip of an unconfirmed chain should be replaceable 2020-05-24 20:15:47 +09:00
nicolas.dorier
7230e89029
bump 2020-05-24 08:44:06 +09:00
nicolas.dorier
9cdfc5ceac
Merge branch 'stats' 2020-05-24 08:42:14 +09:00
nicolas.dorier
a8d6180d07
bump versions 2020-05-24 08:37:03 +09:00
Nicolas Dorier
713368e6ab
Replaced collection should return more information about replacement (#263) 2020-05-24 08:11:17 +09:00
nicolas.dorier
c24738ac1d
bump 2020-05-24 04:40:32 +09:00
Nicolas Dorier
e59c1c46e7
Randomize the created PSBTs based on fingerprint distribution (#262) 2020-05-24 03:24:03 +09:00
nicolas.dorier
f43919ba8f
Randomize the created PSBTs based on fingerprint distribution 2020-05-24 03:16:30 +09:00
nicolas.dorier
cc1626594c
Publish the pdb with sourcelink for client library 2020-05-23 07:33:58 +09:00
nicolas.dorier
70416f5a5d
bump version 2020-05-23 07:24:56 +09:00
nicolas.dorier
faff2699d5
bump 2020-05-23 07:18:55 +09:00
nicolas.dorier
9db0bea772
Bump NBitcoin 2020-05-15 21:14:32 +09:00
nicolas.dorier
db6a5d99be
bump .net core in docker 2020-05-14 16:37:10 +09:00
nicolas.dorier
6a1bcecd6e
Document instanceName 2020-05-14 16:32:27 +09:00
nicolas.dorier
0e316fba7b
Merge branch 'addName' 2020-05-14 16:27:27 +09:00
junderw
51b82602ce
Add server alias option 2020-05-14 16:27:09 +09:00
nicolas.dorier
5edc3be0b3
Prevent NRE in ExplorerBehavior 2020-05-14 16:03:31 +09:00
nicolas.dorier
c6f3c20c62
Do not cache parsing of normal BitcoinStrings 2020-05-10 20:18:40 +09:00
nicolas.dorier
9a2f6d83a5
Bump NBitcoin 2020-05-05 18:53:04 +09:00
nicolas.dorier
29d53baa84
Discourage fee sniping by default 2020-05-05 06:46:02 +09:00
nicolas.dorier
20b50df1dc
Make sure fee rate can be decimal 2020-05-05 06:34:33 +09:00
nicolas.dorier
8635bc9dec
bump 2020-04-16 19:26:37 +09:00
nicolas.dorier
0447e857f2
bump NBitcoin 2020-04-16 19:26:04 +09:00
nicolas.dorier
002aba4a4b
Make tests more reliable by saving block events before broadcast 2020-04-16 19:24:50 +09:00
nicolas.dorier
5f78363479
bump NBitcoin 2020-04-08 19:16:21 +09:00
nicolas.dorier
86562f82b9
Fix a corner case of too strict pruning 2020-04-05 23:00:24 +09:00
nicolas.dorier
61f7d31ab4
bump NBitcoin 2020-04-03 18:39:15 +09:00
nicolas.dorier
15a1701426
Update PSBT should fill out segwit outputs 2020-04-03 18:37:13 +09:00
nicolas.dorier
b6d1935dec
bump 2020-04-03 17:40:00 +09:00
nicolas.dorier
bc7ad92ddb
Use UTXOUpdatePSBT to update UTXOs 2020-04-03 17:39:45 +09:00
nicolas.dorier
9f20f9263b
Do not update the HDKeys of already finalized inputs (this would make insane PSBT) 2020-04-03 17:26:51 +09:00
nicolas.dorier
f2de46d24d
Fix rpc code 2020-04-02 12:43:29 +09:00
nicolas.dorier
e7224324eb
Better RPC error for mempoolverify 2020-04-02 12:39:23 +09:00
nicolas.dorier
555e6c197d
Better RPC equivalent for mempool verify errors 2020-04-02 12:10:23 +09:00
nicolas.dorier
70005df0b7
Fix RPC Code for broadcast verify only 2020-04-02 12:05:23 +09:00
nicolas.dorier
3fc5dc46cc
bump client 2020-04-01 21:22:14 +09:00
nicolas.dorier
d8293c9a7a
Confirmation count in TransactionResult can be negative 2020-04-01 21:12:26 +09:00
nicolas.dorier
a739c1b9e1
Add canSupportTransactionCheck 2020-03-31 18:40:06 +09:00
nicolas.dorier
fd91fc8453
bump 2020-03-11 22:28:51 +09:00
nicolas.dorier
1d3398bbad
Do not fire events when saving matches from broadcast 2020-03-11 21:58:14 +09:00
nicolas.dorier
7f5facdaa5
Fix crash on PSBT creation when duplicating extpubkeys in a scheme 2020-03-11 13:39:00 +09:00
nicolas.dorier
bb5422d6a9
bump 2020-03-10 21:01:39 +09:00
Justin F. Hallett
c8b8050a6f
Add Terracoin Support (#231)
* Added Terracoin Support

* fix spacing

* Truely fix spacing

* 12.2.4 is required

* Add proper derivation path

* Add missing comma

* Get the newest version of NBitcoin which contains the latest Terracoin changes
2020-03-10 21:00:09 +09:00
nicolas.dorier
24874195d9
Add backward compatible ExplorerClient 2020-03-10 18:27:12 +09:00
nicolas.dorier
72cc6737c0
Bump NBitcoin 2020-03-10 18:25:09 +09:00
nicolas.dorier
5eaaeab698
bump client 2020-03-10 18:19:24 +09:00
nicolas.dorier
1ff970e1d3
bump 2020-03-10 18:18:21 +09:00
Andrew Camilleri
b72819c219
Add RPC Client Proxy (#247)
* Add RPC Client Proxy

blocked by https://github.com/MetacoSA/NBitcoin/pull/826

* remove need for special proxy logic

* add exposerpc option

* document & align  error messages with existing api

* small formatting fix
2020-03-10 18:15:42 +09:00
KeyFiDennis
836b9a8f57
Custom auth header (#244)
* Add a custom auth header option to Client

* Revert changes caused by formatter

* Expose IAuth

Co-authored-by: Dennis <dennis@battlestarcap.com>
2020-03-10 18:10:13 +09:00
sunerok
7f82f634db
fix typo (#248) 2020-03-08 12:09:53 +09:00
nicolas.dorier
cfa0de1a9e
Renew the DBreeze transaction every 10 min 2020-02-28 21:05:15 +09:00
nicolas.dorier
56365cdae0
bump 2020-02-28 20:51:44 +09:00
nicolas.dorier
01c2b8c489
Mask error sometimes thrown The response ended prematurely. 2020-02-25 00:25:49 +09:00
nicolas.dorier
3c8a998e83
bump 2020-02-13 14:59:52 +09:00
Jonathan Underwood
5f2c27b675
Add ? for GetExplorerBehavior (#243)
Co-authored-by: joemphilips <joemphilips@gmail.com>

Co-authored-by: Joe Miyamoto <joemphilips@gmail.com>
2020-02-13 14:59:13 +09:00
Jonathan Underwood
a4da994c67
Save Matches from Transaction after successful broadcast from bitcoind. (#242)
Co-authored-by: joemphilips <joemphilips@gmail.com>

Co-authored-by: Joe Miyamoto <joemphilips@gmail.com>
2020-02-13 12:40:34 +09:00
nicolas.dorier
2e02de3290
bump NBitcoin 2020-02-11 04:10:05 +00:00
nicolas.dorier
90e3285c07
Warning instead of crash if ban list can't be loaded (Related to #239) 2020-02-08 12:22:07 +00:00
nicolas.dorier
713f0499d3
Remove lock file (pain in the ass, Fix #236) 2020-02-05 02:50:18 +09:00
nicolas.dorier
870eca72c0
bump 2020-01-27 18:14:15 +09:00
Andrew Camilleri
9530bcee03 Allow specifying testMempoolAccept in Broadcast method in client (#235) 2020-01-27 18:10:18 +09:00
nicolas.dorier
3025329cab
bump 2020-01-21 21:32:52 +09:00
nicolas.dorier
6bd8e6f210
Improve error message when connecting to a node (#1215) 2020-01-21 16:29:24 +09:00
nicolas.dorier
44a6af04c3
bump to sdk 3.1.101 and version 3.1.1 (security patch) 2020-01-18 17:03:56 +09:00
nicolas.dorier
74409946ce
Fix unreliable test CanUseLongPollingOnEvents 2020-01-13 16:57:59 +09:00
nicolas.dorier
3ba4edb0eb
Fix serialization bug for Broadcast on shitcoin 2020-01-13 16:49:48 +09:00
nicolas.dorier
fa9b9c1986
bump client 2020-01-13 16:04:44 +09:00
nicolas.dorier
48bc2203e5
bump 2020-01-13 16:00:54 +09:00
nicolas.dorier
7c2231b236
Restrict AuthorizedOptions and add tests on option ordering 2020-01-13 15:53:54 +09:00
nicolas.dorier
1883c10f4a
Fix test ShowRBFedTransaction 2020-01-13 14:55:10 +09:00
nicolas.dorier
9e9b38d78e
Make derivation schemes immutable class 2020-01-13 14:41:42 +09:00
nicolas.dorier
5d8a24898a
Allow null additional options 2020-01-13 14:25:04 +09:00
nicolas.dorier
449b7aadcc
Optimize case where there is no additional options 2020-01-13 14:24:08 +09:00
nicolas.dorier
b707ff57e2
Allow additional options only for liquid 2020-01-13 14:20:29 +09:00
Andrew Camilleri
dca91aa915 Add unblinded flag , alternative edition (#230) 2020-01-13 13:48:36 +09:00
nicolas.dorier
9084c0dc9c
Bump NBitcoin, Bitcoin Core in tests, make CanPrune reliable 2020-01-13 00:03:17 +09:00
nicolas.dorier
d4db7c2b8c
Fixing stuck NBXplorer 2020-01-12 20:33:46 +09:00
nicolas.dorier
bfc37f46c4
bump 2020-01-06 13:59:57 +09:00
Peter Williams
22bfb7c262 Increase min version (#232)
CoinType definition
2020-01-01 21:46:04 +09:00
nicolas.dorier
37bbc48c53
Disable minutxovalue for liquid 2019-12-19 15:55:28 +09:00
nicolas.dorier
d543daea1d
Fix MinUtxoValue which broke liquid support 2019-12-19 15:51:44 +09:00
nicolas.dorier
66d321c031
Small refacotring for the MinUTXOValue 2019-12-19 14:21:05 +09:00
Jonathan Underwood
d8fa1d0d84 I am a genius. + Joe helped. (#227)
Co-authored-by: joemphilips <joemphilips@gmail.com>
2019-12-19 12:58:52 +09:00
nicolas.dorier
ca7859ad38
Remove nuget fallback for appveyor 2019-12-18 17:26:27 +09:00
nicolas.dorier
e7817e44d7
Add logs for the dotnet restore 2019-12-18 17:11:58 +09:00
nicolas.dorier
1fe37e8568
Clear nuget cache in appveyor before restore 2019-12-18 16:58:58 +09:00
nicolas.dorier
d7bf343fd5
fix build 2019-12-18 16:53:18 +09:00
Jonathan Underwood
f30f486452 Add MinUtxoValue for ignoring spam (#225)
* Add MinUtxoValue for ignoring spam

* int to Money

* GetOrDefault int only
2019-12-18 16:50:42 +09:00
nicolas.dorier
2e2f689538
Fix lock files 2019-12-17 15:04:07 +09:00
nicolas.dorier
fbb91b94bb
Remove lockfile on libs 2019-12-17 14:21:58 +09:00
nicolas.dorier
e443dad842
Add lock files, Add MinValue for PSBT creation 2019-12-17 14:14:19 +09:00
nicolas.dorier
fd432961fe
Bump NBXplorer 2019-12-16 13:56:38 +09:00
Faruk Terzioğlu
dac34e77e5 Filter RabbitMq tests on CircleCI (#224)
* Add RabbitMq support for new transactions.

* Add RabbitMq support for blocks

* Add virtual host settings for RabbitMq.

* Add virtual host for RabbitMq

* Update readme for RabbitMq support

* Remove RabbitMq from Docker compose file

* Add RabbitMq exchange creation

* Fixed the conflict

* Add RabbitMq to appveyor.yml

* Update README for 'rescan' parameter

* Filter RabbitMq tests on CircleCI
2019-12-15 20:28:37 +09:00
nicolas.dorier
5763ed7864
Remove RabbitMQ tests 2019-12-13 14:50:20 +09:00
Faruk Terzioğlu
180c0f06c8 Update README for 'rescan' parameter (#222) 2019-12-13 14:41:01 +09:00
nicolas.dorier
9323386829
Improve UpdatePSBT 2019-12-13 14:21:54 +09:00
nicolas.dorier
b6f6ec7e3e
Skip nonWitnessUtxo for bcash 2019-12-13 13:01:10 +09:00
nicolas.dorier
f1337556f0
Revert "Adding DogeCash Support (#220)"
This reverts commit 02f88a9a2c.
2019-12-10 21:48:42 +09:00
Liquid369
02f88a9a2c Adding DogeCash Support (#220)
* Creating Support for DogeCash

Already updated NBitcoin.Altcoins now just setting up this portion.

* Init DogeCash

* DOGEC Node Data

* Update README.md
2019-12-10 14:05:16 +09:00
Faruk Terzioğlu
b98d81687e Add RabbitMq Support (#208) 2019-12-09 14:21:53 +09:00
Nicolas Dorier
93fb32796b
Update SDK requirement in README 2019-12-07 16:03:44 +09:00
Nicolas Dorier
2aaad298f4
Update SDK requirement in README 2019-12-07 16:03:37 +09:00
nicolas.dorier
5e68f8566d
bump nbx test netcore version 2019-12-04 13:46:06 +09:00
nicolas.dorier
3a1d8cf94d
Fix circleci 2019-12-04 13:44:30 +09:00
nicolas.dorier
ee08904e05
Bump to .NET 3.1 2019-12-04 13:42:40 +09:00
nicolas.dorier
45878f611f
Bump 2019-12-02 17:25:47 +09:00
nicolas.dorier
cdc97df92d
bump 2019-12-02 17:13:41 +09:00
nicolas.dorier
b591e98b66
fix test 2019-12-02 17:09:55 +09:00
nicolas.dorier
7bedfc291b
Pass KeyInfo to AfterMatch 2019-12-02 17:02:31 +09:00
nicolas.dorier
9e0f975c42
Allow zero size cache 2019-12-02 16:32:26 +09:00
nicolas.dorier
0e61a0c604
Import address and private keys after GetUnused 2019-12-02 16:31:22 +09:00
nicolas.dorier
1ad62ef2d5
Cache empty metadata 2019-12-02 15:50:44 +09:00
nicolas.dorier
e4c8ec545f
Track addresses from inside the repository 2019-12-02 15:34:39 +09:00
nicolas.dorier
9374cf783d
Remove useless call to AddBlindingKey, improve the liquid test 2019-12-02 15:08:24 +09:00
Andrew Camilleri
d557fb5ce3 Fix NBXplorerNetworkProvider testnet init (#210)
* Fix NBX Network factory ctor

Since Liquid has no testnet, the NBXplorerNetworkProvider would crash when attempting to init a testnet NBXplorerNetworkProvider

* add test
2019-12-02 13:07:00 +09:00
nicolas.dorier
f32a2fdba0
Make sure liquid always import blinding key to RPC 2019-12-01 22:16:19 +09:00
nicolas.dorier
2512ff3ae1
Also import the blinding key in AfterMatch, just in case 2019-12-01 22:14:53 +09:00
nicolas.dorier
1e5224f84a
Add comment on values nbx can't unblind 2019-12-01 21:37:50 +09:00
nicolas.dorier
4ff911faf1
Do not push all tags on PushNuget 2019-12-01 21:24:59 +09:00
nicolas.dorier
fdaa18e8db
fix push 2019-12-01 21:15:42 +09:00
nicolas.dorier
453addb443
Do not push all tags with publish script 2019-12-01 21:13:56 +09:00
nicolas.dorier
5a0e219c6f
bump 2019-12-01 21:10:09 +09:00
Andrew Camilleri
649e82a9f7 Add Liquid Support (#198) 2019-12-01 20:53:30 +09:00
nicolas.dorier
5ee8b32a56
Add GetBalance operation 2019-12-01 17:48:46 +09:00
nicolas.dorier
b2833fc92a
Remove MnemonicConverter (See #205) 2019-12-01 13:31:45 +09:00
Andrew Camilleri
79fdcbb6d8 Add import wallet API (#205)
* Add import wallet API

This is almost identical to `generate` but allows you to use an existing seed you have.

* Change to extending generate endpoint

* Update API.md
2019-12-01 13:22:44 +09:00
nicolas.dorier
ed7120f740
Forgot file 2019-11-30 17:17:24 +09:00
nicolas.dorier
957a4a652a
Move code out of Repository into its own class 2019-11-30 17:16:58 +09:00
nicolas.dorier
06b0d13511
Refactor, remove NeedUpdate and NeedRemove from MatchedData 2019-11-29 19:15:56 +09:00
nicolas.dorier
3c6cfa49eb
Fix error 500 if substracting fee from a broke destination (https://github.com/btcpayserver/btcpayserver/pull/1184) 2019-11-22 15:39:14 +09:00
Nicolas Dorier
9c6f3126bb
Merge pull request #204 from farukterzioglu/master
Add CryptoCode to Azure message properties
2019-11-22 15:01:03 +09:00
Faruk Terzioğlu
ca9fa2117b Add CryptoCode to Azure message properties 2019-11-21 20:16:00 +03:00
nicolas.dorier
87d9e0e54f
Fix build 2019-11-20 17:27:14 +09:00
nicolas.dorier
14d3b874de
Properly serialize in AzureBroker 2019-11-20 17:00:27 +09:00
nicolas.dorier
9904cc543e
Do not finalize already finalized inputs 2019-11-19 23:32:24 +09:00
Nicolas Dorier
235acc7022
Merge pull request #203 from Kukks/factory-refactoring
Always reuse DerivationStrategyFactory from the NBX Network
2019-11-18 22:15:17 +09:00
nicolas.dorier
6f9981f2ad
bump 2019-11-18 21:23:24 +09:00
nicolas.dorier
96672560a6
Fix tests 2019-11-18 21:14:15 +09:00
nicolas.dorier
05fcf2e148
Remove autopruning feature, make pruning easier 2019-11-18 21:06:43 +09:00
Kukks
e8ec943fba fix missing factory setter 2019-11-18 09:09:55 +01:00
Kukks
240421f026 Always reuse DerivationStrategyFactory from the NBX Network instead of creating a new instance
If there is an NBX network that has its own derived factory, NBX would fail in various places to use that correct type. This fixes that but also makes sense beyond that. Also included here: typo fix in a log + helper method to create `ExplorerClient` from the NBX network directly
2019-11-18 08:52:09 +01:00
nicolas.dorier
73db6ce427
fix build 2019-11-18 14:50:56 +09:00
nicolas.dorier
51cd86d860
Do not forget to save address in KeyPathInformation 2019-11-18 14:49:42 +09:00
nicolas.dorier
99ca6e4588
Fix the feeRate if the ExplicitChangeAddress is in the Destinations 2019-11-17 16:13:32 +09:00
nicolas.dorier
4ee4bc922c
bump 2019-11-16 17:15:49 +09:00
nicolas.dorier
3d48872aed
Make sure that accountKeyPath always refer to a rootedKeyPath 2019-11-16 17:10:49 +09:00
nicolas.dorier
b3f3e44a8f
Document metadata AccountKeyPath 2019-11-16 17:04:33 +09:00
nicolas.dorier
22640cbc75
Fix build 2019-11-16 16:55:29 +09:00
nicolas.dorier
2742e60e53
bump client 2019-11-16 16:53:32 +09:00
nicolas.dorier
4dd4051b78
Set up account key to be a RootedKeyPath 2019-11-16 16:51:41 +09:00
nicolas.dorier
8ed3832bb5
Refactor Generate Wallet to not save private keys by default, rename fields 2019-11-16 16:19:45 +09:00
nicolas.dorier
8b7701f326
fix test 2019-11-16 14:32:44 +09:00
nicolas.dorier
ae65894a14
If the http request is cancelled, the query to the database should as well 2019-11-16 14:25:10 +09:00
nicolas.dorier
b6c5652659
bump 2019-11-15 18:38:43 +09:00
Nicolas Dorier
f0ff747ba2
Merge pull request #202 from junderw/patch-2
Fix Health Check
2019-11-15 18:37:46 +09:00
Jonathan Underwood
6b065d0db6
Fix Health Check 2019-11-15 17:01:40 +09:00
nicolas.dorier
0e55f31b9a
Update API.md 2019-11-15 13:15:37 +09:00
nicolas.dorier
a1467ae187
Fix build 2019-11-14 16:39:31 +09:00
nicolas.dorier
b84e41864b
Make sure a network can customize address format 2019-11-14 15:13:46 +09:00
nicolas.dorier
27fcffebd2
Fix test 2019-11-14 15:03:51 +09:00
nicolas.dorier
92242b85bc
Fix build 2019-11-14 14:38:45 +09:00
nicolas.dorier
d7dec3824a
Make sure the NBXplorer network is passed to NBX serializer 2019-11-14 14:38:02 +09:00
Kukks
733b41931a
make tests use existing NBX network 2019-11-14 14:36:24 +09:00
Nicolas Dorier
ee2ba53e44
Merge pull request #201 from dgarage/feature/generate-wallet
Add a new action to generate a wallet managed by NBXplorer and or RPC
2019-11-14 14:14:03 +09:00
nicolas.dorier
1d7f256c46
Document the health check 2019-11-14 14:12:03 +09:00
nicolas.dorier
f80e856561
Update doc 2019-11-14 14:07:06 +09:00
nicolas.dorier
5e37817847
Add table of content to the doc 2019-11-14 13:52:25 +09:00
nicolas.dorier
18be1f9794
Add documentation and add CoinTypes in NBXplorer 2019-11-14 13:32:24 +09:00
nicolas.dorier
0adf9fddd3
Add a new action to generate a wallet managed by NBXplorer and or RPC 2019-11-14 01:22:41 +09:00
nicolas.dorier
3005dbe24b
Push NBitcoin 2019-11-13 14:53:36 +09:00
nicolas.dorier
2a21003eda
Can test mempool accept 2019-11-12 18:28:48 +09:00
nicolas.dorier
89b8c349b8
Add /health (Fix #187) 2019-11-12 18:19:59 +09:00
nicolas.dorier
0233281875
bump 2019-11-11 17:36:08 +09:00
nicolas.dorier
d25e372449
bump 2019-11-11 17:34:18 +09:00
nicolas.dorier
cc7d8cb431
Bump client 2019-11-11 15:31:24 +09:00
nicolas.dorier
512e3e8192
Fix tests 2019-11-11 15:30:10 +09:00
nicolas.dorier
a1d25cb22f
Bump NBitcoin to 5.0 2019-11-11 15:18:00 +09:00
nicolas.dorier
f2d876ecf4
Remove some error message spamming the logs 2019-10-30 18:03:04 +09:00
nicolas.dorier
3df70c6ddd
fix build 2019-10-23 14:43:46 +09:00
nicolas.dorier
74bfd27395
bump 2019-10-23 14:42:45 +09:00
nicolas.dorier
9c6a8ec5fd
bump client 2019-10-23 14:38:44 +09:00
nicolas.dorier
a1da63daff
bump 2019-10-23 14:38:01 +09:00
FS
f254523eb8 Add support for Chaincoin (#199)
* Add support for Chaincoin

* Update NBXplorerNetworkProvider.cs
2019-10-16 00:22:41 +09:00
nicolas.dorier
a5c23997e7
Restore progress in test on circleci 2019-10-09 16:40:21 +09:00
nicolas.dorier
f6916bb68d
Add log progress 2019-10-09 16:37:12 +09:00
nicolas.dorier
1c093c3a34
Use --logger:"console;noprogress=true" 2019-10-09 16:34:16 +09:00
nicolas.dorier
30e7b88cd3
bump NBitcoin, remove unused code 2019-10-07 14:40:11 +09:00
nicolas.dorier
afd9496539
bump dbriize 2019-10-01 18:57:44 +09:00
nicolas.dorier
d00d2fa145
Update packages 2019-10-01 15:38:11 +09:00
nicolas.dorier
8f3227e3fa
Try fix test hanging on appveyor 2019-10-01 15:02:10 +09:00
nicolas.dorier
8071c44dda
bump and bump app veyor visual studio image 2019-10-01 14:37:50 +09:00
nicolas.dorier
3a44058c60
Do not use GetRawTransaction without blockId if --btc.hashtxindex is not set 2019-10-01 13:51:03 +09:00
nicolas.dorier
ae464575bd
Try to use RPC GetRawTransaction when fetching a transaction via NBXplorer 2019-09-30 21:49:51 +09:00
nicolas.dorier
06bc369463
Update test framework 2019-09-29 01:16:03 +09:00
nicolas.dorier
6f0fbc5829
Fix tests 2019-09-29 01:11:19 +09:00
nicolas.dorier
95fd94e0ed
Catch ObjectDisposedException 2019-09-28 21:33:13 +09:00
nicolas.dorier
fbf09ff233
Prevent delay on stop if error while saving a block 2019-09-28 21:30:51 +09:00
nicolas.dorier
407839333e
Add some missing TaskCreationOptions.RunContinuationsAsynchronously preventing deadlock 2019-09-28 21:27:10 +09:00
nicolas.dorier
cc2276fb6d
Update NBitcoin 2019-09-28 14:34:03 +09:00
nicolas.dorier
b77545f127
Remove uneeded call 2019-09-28 13:45:50 +09:00
nicolas.dorier
faf36b4dfd
Use docker image in circleCI to run tests 2019-09-28 13:44:37 +09:00
nicolas.dorier
be5ca52720
Update readme 2019-09-28 13:32:24 +09:00
nicolas.dorier
98afc2bf71
bump all code to netcoreapp3.0 2019-09-28 13:29:38 +09:00
Bub
5da254b803 Increase timeout for chain loading from cache for Dogecoin (#194) 2019-09-27 20:51:51 +09:00
nicolas.dorier
6069d0a06a
Remove spent confirmed outpoint from GetUtxo (useless) 2019-09-21 00:45:26 +09:00
Andras
c97f807c17 3 launch project configurations for different coins (#189)
* Added new launch configs

Added 3 launch configs to the NBXplorer:
"NBXplorer - BTC on Mainnet" -> run NBXplorer with the default arguments
"NBXplorer - BTC+LTC on Regtest" -> run NBXplorer with the BTC+LTC config as in README.md
"NBXplorer - DASH on Testnet" -> run NBXplorer with default DASH Testnet settings

* Changed the startup URLs

Changed the startup URLs to the status page of BTC, LTC and DASH

* Added default RPC usernames and passwords

* Removed the iisSettings from the configuration

* Removed rpcuser and rpcpassword from the BTC and LTC settings

* Removed the RPC username and password from the Dash config as well

* Added a few notes and sample parameters to the command line section

* Fixed the "run from source" command and the btcnodeendpoint parameter.

* Removed the rescan demonstration.

* Changed the "Run from source" and "Run using built DLL" sections

* Removed the double "compiled" word

* Added details about the --noauth parameter in the Postman section
2019-09-04 18:35:18 +09:00
nicolas.dorier
bbc358d9e4
bump 2019-08-30 21:11:37 +09:00
nicolas.dorier
d0145c5946
Adding some logs, and make sure HighestInFlight can't go backward 2019-08-30 21:11:24 +09:00
nicolas.dorier
03de0afaf7
bump 2019-08-28 23:52:32 +09:00
nicolas.dorier
4c172224bf
Make sure does NBXplorer does not block 2019-08-28 22:55:56 +09:00
nicolas.dorier
d76dff5731
fix test 2019-08-22 20:36:57 +09:00
nicolas.dorier
dbd3626bdf
Check in bitcoin core if we don't have the previous transaction for non segwit addresses 2019-08-22 20:20:38 +09:00
nicolas.dorier
06d63d34eb
bump 2019-08-13 17:26:21 +09:00
nicolas.dorier
b24b5c2268
Add arm64v8 to circleci 2019-08-13 17:24:46 +09:00
nicolas.dorier
59dad6922a
Fix .net core 3.0 2019-08-13 17:13:13 +09:00
nicolas.dorier
c36bb8b53b
Add request parameter to Track call 2019-08-08 17:24:48 +09:00
nicolas.dorier
6a558d876d
Add duplicates sent back by cryptos/{cryptoCode}/scripts/{script} in some circumstances 2019-08-08 13:00:09 +09:00
nicolas.dorier
79b9638162
Use EnableBuffering instead of EnableRewind 2019-08-08 11:27:50 +09:00
nicolas.dorier
e120fa87e1
Revert "Make sure ScanTxoutSet does not lead to address reuse"
This reverts commit acaca283ba.
2019-08-08 11:01:19 +09:00
nicolas.dorier
acaca283ba
Make sure ScanTxoutSet does not lead to address reuse 2019-08-08 10:14:09 +09:00
nicolas.dorier
82128d0ab2
Make sure the intermediate derivations are cached 2019-08-08 09:57:52 +09:00
nicolas.dorier
65beaa6815
Make NBXplorer buildable for .netcore 3.0 2019-08-06 23:59:36 +09:00
nicolas.dorier
84ce4130a2
Remove useless file 2019-08-06 17:26:29 +09:00
nicolas.dorier
23618f1cea
Add link to nodejs client 2019-08-06 15:04:59 +09:00
nicolas.dorier
f5a739f1ba
Make sure rescan is finished even if btc crash 2019-08-05 15:57:55 +09:00
nicolas.dorier
e82faf4ab0
Add manual pruning feature 2019-07-30 18:04:47 +09:00
nicolas.dorier
8bcc16187f
Fix scanutxoset for Custom derivation feature 2019-07-30 17:09:34 +09:00
nicolas.dorier
15324901c2
Properly handle empty customkeypathtemplate 2019-07-30 17:02:11 +09:00
Jonathan Underwood
8f3a468f5c Update docker-compose.regtest.yml (#178) 2019-07-30 17:00:37 +09:00
nicolas.dorier
f9cdcf2386
Improve parsing error detection of KeyPathTemplate 2019-07-30 16:37:53 +09:00
nicolas.dorier
b850fbc487
Add a new derivation feature via customizable with customKeyPathTemplate 2019-07-30 16:27:43 +09:00
nicolas.dorier
567cd245be
Update docker-compose.regtest.yml 2019-07-29 15:19:40 +09:00
nicolas.dorier
dd9524af50
PSBT must set the global xpubs 2019-07-15 19:28:29 +09:00
nicolas.dorier
f6eddf7af2
Update greg's list 2019-07-15 19:17:28 +09:00
nicolas.dorier
d064d559c8
Bump NBitcoin 2019-07-15 18:57:30 +09:00
nicolas.dorier
a0085bb9a0
NBXplorer fix: PSBT for altcoins was not working 2019-07-15 18:06:26 +09:00
nicolas.dorier
b93e26ab80
bump 2019-07-04 18:42:44 +09:00
nicolas.dorier
b9f113a415
Fix some logic 2019-06-28 14:01:01 +09:00
nicolas.dorier
4cea321d87
Rewrite transaction ordering in topological sort 2019-06-28 12:33:46 +09:00
nicolas.dorier
f95464e53f
Give capacity of hashset 2019-06-27 14:38:01 +09:00
nicolas.dorier
95d9107a6f
Better implementation of topological sort 2019-06-27 14:08:18 +09:00
nicolas.dorier
7added24c6
Fix timeout issues when rescanning the utxo set 2019-06-18 13:25:06 +09:00
nicolas.dorier
7e095d0181
Rebroadcast transactions and clean transactions in the background (RebroadcasterHostedService) 2019-06-06 17:01:23 +09:00
nicolas.dorier
876069bdb7
Fix potential NRE 2019-06-05 20:22:40 +09:00
nicolas.dorier
4fe7a78bce
bump 2019-06-04 12:01:02 +09:00
nicolas.dorier
06be151fb6
Fix exception if rescan is used in command line (fix #174) 2019-06-03 15:02:41 +09:00
nicolas.dorier
5b4c62a30f
Refactor _Tx initialization 2019-06-02 15:38:24 +09:00
nicolas.dorier
daa667bf0b
Update should work on segwit without having previous transactions 2019-05-30 22:07:24 +09:00
nicolas.dorier
9bd740f8cd
Fix crash to update PSBT if an input's scriptPubKey is not found 2019-05-30 18:57:38 +09:00
nicolas.dorier
4a32fcfa10
Fix doc 2019-05-30 18:55:50 +09:00
nicolas.dorier
fef3fa241c
NBXplorer support Update PSBT 2019-05-30 18:47:05 +09:00
nicolas.dorier
175400f65b
Remove unused code 2019-05-23 15:32:23 +09:00
nicolas.dorier
bfb4874dd7
refactor getmetadata to be generic 2019-05-20 18:12:06 +09:00
nicolas.dorier
6df39c9f1e
Can attach/detach Metadata to derivation schemes 2019-05-20 16:21:24 +09:00
nicolas.dorier
0be9ffac8c
Restore old behavior of transaction ordering 2019-05-19 17:03:29 +09:00
nicolas.dorier
3a488725a1
bump 2019-05-19 15:49:43 +09:00
nicolas.dorier
45e7f1ab0b
Fix warning 2019-05-19 15:38:05 +09:00
nicolas.dorier
aacb4b88d9
Simplify code 2019-05-19 15:30:50 +09:00
nicolas.dorier
10f9469029
Fix pruning 2019-05-19 15:23:58 +09:00
nicolas.dorier
37e18a2049
Fix tests and warnings 2019-05-19 14:58:39 +09:00
nicolas.dorier
2805439b80
Fix test doc 2019-05-19 13:25:34 +09:00
nicolas.dorier
6a5799dfe2
fix link 2019-05-19 13:25:02 +09:00
nicolas.dorier
c68a058069
reword readme 2019-05-19 13:24:09 +09:00
nicolas.dorier
1a550a92e6
Add design documentation 2019-05-19 13:22:56 +09:00
nicolas.dorier
39d1901a80
Bump NBitcoin 2019-05-19 13:13:19 +09:00
nicolas.dorier
1dc5290396
Improve test 2019-05-19 11:46:53 +09:00
nicolas.dorier
820c4b824b
Simplify code, pass several time on the list of unconfirmed transaction for cleaning the double spends 2019-05-19 00:02:57 +09:00
nicolas.dorier
302e3b35d0
comment test 2019-05-18 23:27:49 +09:00
nicolas.dorier
7b20acef8b
Simplify the code, fix a test 2019-05-18 23:23:48 +09:00
nicolas.dorier
36837b14ce
Improve readability, tests and perf of transaction ordering 2019-05-18 22:57:24 +09:00
nicolas.dorier
a16012d5f7
More efficient Topological Sort 2019-05-18 12:14:06 +09:00
nicolas.dorier
1bc990c067
bump 2019-05-17 01:24:33 +09:00
nicolas.dorier
cbf59321ee
Bump NBitcoin 2019-05-17 00:44:57 +09:00
nicolas.dorier
f0c7772ac8
Bump NBitcoin, add a nicer way to pass RootedKeyPath to NBX 2019-05-14 15:07:40 +09:00
nicolas.dorier
5aafdb54d2
The PSBT create request can also rebase hdkeypaths 2019-05-10 19:08:09 +09:00
nicolas.dorier
b60c291207
Fix NBXplorer crashing if a wallet is receiving a coinbase 2019-05-08 20:15:10 +09:00
nicolas.dorier
3afe782fff
Fix bug of NBXplorer not showing progress anymore 2019-05-08 14:02:56 +09:00
nicolas.dorier
d61d002677
Remove useless instance of JsonSerializerSettings 2019-05-07 19:10:44 +09:00
nicolas.dorier
a0d469651f
bump 2019-05-07 08:22:03 +09:00
nicolas.dorier
cb5164b73d
Make sure that recognized output in CreatePSBT are properly filled with HDKey path 2019-05-07 08:12:48 +09:00
nicolas.dorier
3eef314255
Add better logs to CI tests 2019-05-07 07:43:06 +09:00
nicolas.dorier
a8c526258b
Parallelize some key derivation 2019-05-07 07:28:43 +09:00
nicolas.dorier
e267204b75
Fetch all the scripts from a block at once 2019-05-07 01:18:20 +09:00
nicolas.dorier
231824ee94
Fix CreatePSBT not working if the transaction builder does not recognize the transaction scheme 2019-05-07 01:07:16 +09:00
nicolas.dorier
6d829dcfe8
Remove debug log 2019-05-03 22:23:52 +09:00
nicolas.dorier
a135445e2f bump nbitcoin, use 0.18.0 in tests 2019-05-03 10:07:53 +09:00
nicolas.dorier
3f2578adfe Make sure a tracked transaction does not have dups 2019-05-03 08:18:01 +09:00
nicolas.dorier
226ea19ca1 Fix build 2019-05-02 21:33:48 +09:00
nicolas.dorier
c1c8bf0047 Fix minRelayTxFee for PSBT 2019-05-02 20:22:06 +09:00
nicolas.dorier
4a03273ef0 Cache the NetworkInfo, and set the MinRelayTxFee of the transaction correctly in TxBuilder 2019-05-02 20:21:30 +09:00
nicolas.dorier
f12c367030 Use a dummy to detect change addresses 2019-05-02 17:15:21 +09:00
nicolas.dorier
3a8ac1ff64 Add explicitChangeAddress to Create PSBT 2019-05-02 17:00:22 +09:00
nicolas.dorier
3788e85120 bump 2019-05-02 15:36:29 +09:00
nicolas.dorier
b348737fe3 Can customize Version and LockTime in CreatePSBT 2019-05-02 15:17:16 +09:00
nicolas.dorier
51aeb4401c Add excludeOutpoints/includeOnlyOutpoints 2019-05-02 14:04:51 +09:00
nicolas.dorier
89e4f9e154 Add CreatePSBTRequest.MinConfirmations 2019-05-02 13:10:42 +09:00
nicolas.dorier
f560a9f381 fix typo 2019-05-01 15:50:56 +09:00
nicolas.dorier
194fa7524c bump 2019-05-01 15:46:34 +09:00
nicolas.dorier
e2dbaa3352 Document createPSBT 2019-05-01 15:46:07 +09:00
nicolas.dorier
e9f865fdab Add CreatePSBT route 2019-05-01 15:23:46 +09:00
nicolas.dorier
b5db39282d bump nbx 2019-04-29 12:15:48 +09:00
nicolas.dorier
482f5488c8 bump nbx 2019-04-29 12:10:32 +09:00
nicolas.dorier
53deaeb13b Update DBriize 2019-04-29 11:53:40 +09:00
nicolas.dorier
bb37a17b30 bump 2019-04-28 22:02:41 +09:00
nicolas.dorier
e934f0bfaf bump dbriize 2019-04-26 12:04:43 +09:00
nicolas.dorier
34395c397b Rename file to DBriize 2019-04-23 14:17:01 +09:00
nicolas.dorier
784a39a1cb Replace DBreeze by DBriize 2019-04-23 14:16:23 +09:00
nicolas.dorier
3b4a0684e3 cleanup nbxplorer shutdown 2019-04-22 15:07:08 +09:00
nicolas.dorier
2289e76624 Replacing AutoResetEvent with a signaler 2019-04-22 15:02:36 +09:00
nicolas.dorier
c7655be34b Linear backoff on errors raising in the waiter loop 2019-04-22 14:42:33 +09:00
nicolas.dorier
f56a79100d Less spam with RPC errors 2019-04-22 14:35:11 +09:00
nicolas.dorier
5ef7fa1709 Do not depend on NodeGroup 2019-04-22 13:53:23 +09:00
nicolas.dorier
0437798a95 update banlist 2019-04-11 16:14:55 +09:00
Lucas Cullen
02ddfa88e9 Fix spelling of successful (#169) 2019-04-10 18:39:40 +09:00
nicolas.dorier
c3e8d6023d Having more than one thread writing to DB 2019-04-09 15:48:30 +09:00
nicolas.dorier
43e483709e log errors in AddressPoolService 2019-04-09 12:54:47 +09:00
nicolas.dorier
bc571edcfb bump 2019-04-08 16:56:12 +09:00
nicolas.dorier
5a87fbe5cf Fix: If too many addresses was asked at once, NBXplorer could have timeouts 2019-04-08 16:55:20 +09:00
nicolas.dorier
77b2bd06bb Prevent NBXplorer stalling with too much altcoins, by having one processing queue for refilling an address pool by altcoin 2019-04-08 14:20:15 +09:00
nicolas.dorier
f1e7bb6b14 Prevent stupid misconfiguration 2019-04-01 12:13:30 +09:00
nicolas.dorier
16df4f1cf5 bump 2019-03-31 11:47:41 +09:00
nicolas.dorier
7ec7d98ef2 bump nbitcoin 2019-03-29 23:06:14 +09:00
nicolas.dorier
7ed77bb0df update publish script 2019-03-29 18:38:23 +09:00
nicolas.dorier
e52eb8ab60 Make latest branch 2019-03-29 17:47:06 +09:00
nicolas.dorier
7db8160dbf bump 2019-03-28 22:13:20 +09:00
nicolas.dorier
1ef98e7a9c Remove useless code 2019-03-28 21:52:39 +09:00
nicolas.dorier
cb29f39d4d Use Node.ConnectAsync 2019-03-28 20:51:10 +09:00
nicolas.dorier
69ba5bc824 Rewrite AddAsync for addrman 2019-03-28 16:45:08 +09:00
nicolas.dorier
cfeb95802c Fix AddAsync on dns endpoints 2019-03-28 13:19:03 +09:00
nicolas.dorier
a7c78c055b Make sure NBXplorer resolve the dns endpoint of the node it connects to 2019-03-28 13:14:47 +09:00
nicolas.dorier
df1d1350b4 Fix bug of Capabailities of RPC not being set 2019-03-26 18:41:34 +09:00
nicolas.dorier
590a3f8fb9 Fix timeout issues on RPC inthe BitcoinDWaiter 2019-03-26 18:28:32 +09:00
nicolas.dorier
8ba3e9ce86 Allow bigger timeout for GetStatus 2019-03-25 10:41:50 +09:00
nicolas.dorier
7472b042fc Update Microsoft.AspNetCore.App 2019-03-20 14:11:44 +09:00
nicolas.dorier
9f76a84bcb Fix dockerfiles 2019-03-20 12:30:57 +09:00
nicolas.dorier
fc1bd38411 bump 2019-03-20 12:21:39 +09:00
nicolas.dorier
f9a39e827c Prevent nbx from hanging 2019-03-20 12:21:25 +09:00
nicolas.dorier
a590929340 bump .NET Core images 2019-03-20 12:03:35 +09:00
nicolas.dorier
f64926933c Scope RPC request timeout to UTXOSetScan only 2019-03-18 21:45:38 +09:00
nicolas.dorier
92a89aa4dc Add external address information in GetStatus 2019-03-18 12:06:56 +09:00
nicolas.dorier
6c84739788 Can configure signalfilesdir 2019-03-18 11:54:16 +09:00
nicolas.dorier
ea0e372eb7 Update greg's banlist 2019-03-18 11:48:05 +09:00
nicolas.dorier
91d3d92cbd Make sure NBXplorer is not hanging 2019-03-15 16:48:59 +09:00
nicolas.dorier
433ae9afeb Remove NodeWaiter (not needed with the fully_synched file signaling) 2019-03-13 15:07:58 +09:00
nicolas.dorier
261fc5932d Create a file to signal to file system that a chain is fully synced 2019-03-13 12:51:22 +09:00
nicolas.dorier
4a89975d0e Add banlist to load in NBXplorer 2019-03-11 12:40:38 +09:00
nicolas.dorier
6f952775a1 bump NBitcoin 2019-03-03 01:33:48 +09:00
nicolas.dorier
84ae9324c6 Remove logs spam from the HttpClient 2019-03-02 23:01:52 +09:00
nicolas.dorier
5d2efb570b bump client 2019-03-01 14:40:03 +09:00
nicolas.dorier
c9e8527ceb bump nbitcoin and nbxplorer 2019-03-01 14:38:40 +09:00
nicolas.dorier
07710c2d2d Use the HttpClientFactory to prevent opening a new socket connection for each RPC request 2019-02-27 14:14:11 +09:00
nicolas.dorier
0d6ad418e9 Making sure Process does not reap the child 2019-02-23 00:43:55 +09:00
nicolas.dorier
f6f0c50290 Revert "Do not waitpid instead of Process on linux"
This reverts commit 913eb95be1.
2019-02-23 00:37:07 +09:00
nicolas.dorier
913eb95be1 Do not waitpid instead of Process on linux 2019-02-23 00:20:55 +09:00
nicolas.dorier
eca29c308a Fix https://github.com/dgarage/NBXplorer/pull/145 2019-01-29 12:23:45 +09:00
nicolas.dorier
6accf14da4 bump 2019-01-20 12:26:31 +09:00
nicolas.dorier
8506f44e3b Does not completely block nbxplorer if a block fails to save in the database 2019-01-20 12:25:45 +09:00
nicolas.dorier
fb63c9eff0 Fix concurrent calls to AskBlocks() 2019-01-20 12:11:36 +09:00
nicolas.dorier
130da35412 bump 2019-01-20 00:42:05 +09:00
nicolas.dorier
374dd83881 Only make the ping test if the node is fully synced 2019-01-20 00:37:37 +09:00
nicolas.dorier
b311287a97 Fix a corner case where ScanUTXOSet would not found all the utxos 2019-01-17 22:46:31 +09:00
nicolas.dorier
984c582507 Add more tests to scan utxo 2019-01-17 21:46:36 +09:00
nicolas.dorier
de6245f0c9 NodeWaiter should not prevent node from starting 2019-01-17 18:25:44 +09:00
nicolas.dorier
efe035bd56 fix nodewaiter 2019-01-17 18:08:07 +09:00
nicolas.dorier
bf0f89fb5d Make NodeWaiter able to manage a child process 2019-01-17 17:41:50 +09:00
nicolas.dorier
8ec7dbd861 bump NBitcoin 2019-01-16 15:05:04 +09:00
nicolas.dorier
8333f00d6d Disable noMatch caching if not in ExplorerBehavior 2019-01-16 13:57:30 +09:00
nicolas.dorier
a4b4545510 Revert "Fix cache condition"
This reverts commit 5160a155c7.
2019-01-16 13:49:29 +09:00
nicolas.dorier
5160a155c7 Fix cache condition 2019-01-16 13:36:42 +09:00
nicolas.dorier
4ca4c0e249 bump deps and version 2019-01-16 13:34:55 +09:00
nicolas.dorier
7bc154623e GC collect after asking for the next blocks 2019-01-09 14:09:58 +09:00
nicolas.dorier
862ba3d53c Collect memory aggressively while we sync 2019-01-09 14:09:01 +09:00
nicolas.dorier
dfaa7adbc3 Simplify nbxplorer nomatch caching 2019-01-04 13:54:20 +09:00
nicolas.dorier
322281e564 Document query of single tx for a derivation scheme 2018-12-25 15:49:00 +09:00
nicolas.dorier
f1fd9730e6 Catch errors during init 2018-12-15 00:17:57 +09:00
nicolas.dorier
ade0601bbc reformat logs 2018-12-15 00:17:01 +09:00
nicolas.dorier
be9d6e76c5 bump 2018-12-14 23:58:41 +09:00
nicolas.dorier
2a0c4f1685 Show errors when treating a block 2018-12-14 23:58:25 +09:00
nicolas.dorier
6c7695d3b6 Fix stuck rescan 2018-12-14 23:54:24 +09:00
nicolas.dorier
b4a4bfdb25 update the current location only when saving the index progress 2018-12-14 20:13:57 +09:00
nicolas.dorier
0a3cebb52a Saving progress after saving block 2018-12-14 20:02:54 +09:00
nicolas.dorier
f4fb9edff7 Disable batching if synching 2018-12-14 19:44:40 +09:00
nicolas.dorier
edcc0e0a45 Do not ask for transactions while we are synching 2018-12-14 19:13:07 +09:00
nicolas.dorier
b40e4d846a Fix case for cryptoCode at the provider level 2018-12-14 12:41:45 +09:00
nicolas.dorier
17dfb21ff9 Fix #145 2018-12-14 01:12:08 +09:00
nicolas.dorier
3479606c97 Remove dependency to the chain in ScanUTXOSetService 2018-12-05 22:17:02 +09:00
nicolas.dorier
4e03857bc7 Fix license in package 2018-12-01 16:46:46 +09:00
David
b05126e1ce bump NBitcoin (#140)
see https://github.com/MetacoSA/NBitcoin/pull/605
2018-12-01 16:41:06 +09:00
nicolas.dorier
008630a9a0 No latest image 2018-11-24 16:56:08 +09:00
nicolas.dorier
cc3d29167c no latest images 2018-11-24 16:51:16 +09:00
nicolas.dorier
028b097792 bump 2018-11-24 16:38:40 +09:00
nicolas.dorier
efce69acc1 Add circle CI badge 2018-11-24 16:38:00 +09:00
nicolas.dorier
edbb68ac6a only execute non azure tests 2018-11-24 16:35:28 +09:00
nicolas.dorier
e92b1fcadf do not apt-get update 2018-11-24 16:29:45 +09:00
nicolas.dorier
e61d3461f5 Use framework dependant images 2018-11-24 16:28:40 +09:00
Rockstar Developer
bcf72c158b CircleCI and building of Docker images (#137)
* Arm32 docker image build for NBXplorer

* Tweaking CircleCI config, using $DOCKERHUB_REPO to generalize script
2018-11-24 16:25:48 +09:00
nicolas.dorier
445d29df21 bump 2018-11-21 18:38:17 +09:00
nicolas.dorier
7adfc16bb0 Cache transactions not matching any scriptPubKey so we don't have too much to do when a new block comes in 2018-11-21 18:37:14 +09:00
nicolas.dorier
997d34be6d Make no request to db if the lastEventId did not changed 2018-11-21 17:23:53 +09:00
nicolas.dorier
b8f716de4d Let client decide of the polling timeout 2018-11-21 17:15:12 +09:00
nicolas.dorier
6f3c8c7f5d Remove bookmark references from the doc 2018-11-21 16:32:44 +09:00
nicolas.dorier
b4ef988195 Remove Bookmark code server side 2018-11-21 16:29:46 +09:00
nicolas.dorier
760bf2108e Remove remaining dependencies to bookmarks 2018-11-21 16:16:31 +09:00
nicolas.dorier
8fc05f2cf7 Remove tests on GetTransactions with bookmarks 2018-11-21 15:52:22 +09:00
nicolas.dorier
e3c55eeab4 Remove dependencies to bookmarks in tests 2018-11-21 15:29:40 +09:00
nicolas.dorier
39581b0f6b Remove useless code which made tests slower 2018-11-21 14:45:23 +09:00
nicolas.dorier
d6bc3c7571 Decrease reliance on bookmarks in tests 2018-11-21 14:36:42 +09:00
nicolas.dorier
4e15094403 Test CanTrack should not rely on bookmarks 2018-11-21 14:08:56 +09:00
nicolas.dorier
241770fabd Remove useless parameter 2018-11-21 13:35:26 +09:00
nicolas.dorier
db44f0ade2 Reword 2018-11-20 18:42:17 +09:00
nicolas.dorier
a2fa25d5f4 fix indent 2018-11-20 18:41:16 +09:00
nicolas.dorier
6870359088 Remove use of slim semaphore 2018-11-20 18:39:16 +09:00
nicolas.dorier
7d7031b20e Event stream API 2018-11-20 18:19:38 +09:00
nicolas.dorier
936a538da5 Moving Event serialization code inside NewEventBase 2018-11-20 12:41:56 +09:00
nicolas.dorier
85f1f3205d Can query a single transaction from a wallet 2018-11-19 16:48:49 +09:00
nicolas.dorier
6c464707e8 Remove BlockEvent type 2018-11-19 15:32:27 +09:00
nicolas.dorier
e8483d8e25 Remove intermediate type NewTransactionMatchEvent 2018-11-19 15:08:40 +09:00
nicolas.dorier
f45d19ece3 bump 2018-11-17 00:06:03 +09:00
nicolas.dorier
b5221f67cd Bump to 2.1.6 2018-11-17 00:05:17 +09:00
nicolas.dorier
6dc498b22f fix unlikely case 2018-11-16 20:34:54 +09:00
nicolas.dorier
beb7c8d4fa Small refactoring 2018-11-16 20:34:01 +09:00
nicolas.dorier
6984f1ef8e Properly show the blockTime of a transaction when running UTXO scan 2018-11-16 20:21:56 +09:00
nicolas.dorier
e7cc3c9412 bump NBitcoin 2018-11-15 18:06:13 +09:00
nicolas.dorier
c0fd730827 Make sure there is no crash if getpeerinfo contains unresolvable domain name 2018-11-15 17:06:33 +09:00
nicolas.dorier
54d897d7e6 Add better exception if chain fails to load from node 2018-11-15 16:14:48 +09:00
nicolas.dorier
a09ac55a99 More logs during P2P connection 2018-11-15 15:47:34 +09:00
nicolas.dorier
bca43e18b4 Better error message if handshake fail 2018-11-15 12:41:30 +09:00
nicolas.dorier
e72d478e7b Add warning if handshake fails 2018-11-14 23:11:25 +09:00
nicolas.dorier
c29f227833 Do not crash if async method in a subscription throws an exception 2018-11-13 22:19:11 +09:00
nicolas.dorier
28ef902464 Remove global.json because MS retired sdk 403 2018-11-13 16:42:45 +09:00
nicolas.dorier
7d964eb061 bump versions 2018-11-09 15:40:14 +09:00
nicolas.dorier
9f474d142d bump testframework 2018-11-09 15:38:33 +09:00
David
a4f33f898d bump (#130)
see 8fc2644d38
2018-11-09 15:37:32 +09:00
nicolas.dorier
1497c7e08b bump 2018-11-04 12:58:44 +09:00
nicolas.dorier
505e6662ba bump 2018-11-04 12:39:20 +09:00
nicolas.dorier
18930c7a0b Add Address to KeyInformation path 2018-11-04 12:38:12 +09:00
Cronos
71a0623aed More Altcoins (#99)
* More Altcoins

Initial implementation of Colossus, Monoeci and Gobyte

* Update 

Colossus Support
Gobyte Support
Monoeci Support

* Bump Nbitcoin.Altcoin

* Fix GoByte
2018-11-03 14:17:25 +09:00
nicolas.dorier
88a8db8be3 Make sure to not crash if we can't reach RPC 2018-11-02 15:03:08 +09:00
nicolas.dorier
da7df86019 bump nbx 2018-11-02 11:29:39 +09:00
nicolas.dorier
9422ea02af bump 2018-10-31 16:30:45 +09:00
nicolas.dorier
cc0a9c1bb7 Add appveyor badge 2018-10-31 15:57:58 +09:00
nicolas.dorier
c37b21fa0c add appveyor support 2018-10-31 15:49:40 +09:00
nicolas.dorier
f18ba2f5c6 Remove async void methods 2018-10-31 15:42:57 +09:00
nicolas.dorier
82646bc041 update sln 2018-10-31 13:10:06 +09:00
nicolas.dorier
a632abf8c3 bump 2018-10-31 12:36:08 +09:00
nicolas.dorier
f9a937d4fc Update .NET Core and some deps 2018-10-31 12:36:07 +09:00
David
0b67b7d0d0 Add BitCore BTX support (#126)
* Add BitCore BTX support

* Update ServerTester.Environment.cs
2018-10-31 12:25:03 +09:00
nicolas.dorier
d3fbc499a9 Small refactoring and indenting 2018-10-31 11:49:10 +09:00
nicolas.dorier
b85d4842d3 Remove Redeem information from GetInformations (very costly to derive) 2018-10-31 11:26:24 +09:00
nicolas.dorier
bde87d7c17 Topoligically sort AnnotatedCollection 2018-10-31 01:26:08 +09:00
nicolas.dorier
244adcb313 Add missing commit when tracking individual addresses 2018-10-31 00:42:26 +09:00
nicolas.dorier
7a6f44993d Fix bug where ScanUTXOSet would stop working 2018-10-30 15:20:04 +09:00
Nicolas Dorier
be5d1b5182
Update API.md 2018-10-28 14:35:41 +09:00
Nicolas Dorier
b281286a35
Update API.md 2018-10-28 14:34:28 +09:00
Nicolas Dorier
aebd0d274d
Update API.md 2018-10-28 14:33:19 +09:00
Nicolas Dorier
043356253e
Update API.md 2018-10-28 14:30:57 +09:00
nicolas.dorier
cb43cd1d35 Return proper error on GetFeeRate if the currency do not support it 2018-10-27 23:00:05 +09:00
nicolas.dorier
62ae513dd0 Fix bugs in ScanUTXOSet, not returning proper data for completed run 2018-10-26 22:42:23 +09:00
nicolas.dorier
042b690b45 Fix bug: NBXplorer takes sometimes 1 min to go from Synching to Ready state 2018-10-26 19:23:41 +09:00
nicolas.dorier
e2c2cc07c0 bump 2018-10-25 18:50:49 +09:00
nicolas.dorier
ad9283e849 Add remaining time to state of scan 2018-10-25 15:10:19 +09:00
nicolas.dorier
a54227d567 Do not wait for CleanTransactions 2018-10-25 14:41:42 +09:00
nicolas.dorier
73dac5d01f bump 2018-10-25 13:08:59 +09:00
nicolas.dorier
c55f65e94d Move GetMatches inside the Repository class 2018-10-25 13:00:34 +09:00
nicolas.dorier
ce9f785368 Document ScanUTXOSet 2018-10-25 12:58:57 +09:00
nicolas.dorier
34c4c10063 Add timing info in the scan state 2018-10-25 12:58:44 +09:00
nicolas.dorier
b12a4227b1 Report overall progress of scanning 2018-10-25 12:47:33 +09:00
nicolas.dorier
efbd3b823c Make AddressTrackedSource a IDestination 2018-10-25 11:34:08 +09:00
nicolas.dorier
d598f86ff7 Update API.md 2018-10-25 11:28:25 +09:00
nicolas.dorier
4ffa5bbfc5 Fix test 2018-10-24 23:12:22 +09:00
nicolas.dorier
6f7f277fc7 Remove MatchedTransaction type, make tests more resilient, update NBitcoin to solve port exhaustion issue in RPCClient 2018-10-24 22:53:27 +09:00
nicolas.dorier
b64f960401 Do not show the Feature property for address match 2018-10-24 21:25:05 +09:00
nicolas.dorier
e8fcddcd14 Deprecate GetKeyInformations 2018-10-24 21:15:22 +09:00
nicolas.dorier
5099c87693 Better logs in test and on matching and fix improperly pruned records 2018-10-24 18:45:39 +09:00
nicolas.dorier
f55707e509 Better logs to tests 2018-10-24 17:59:11 +09:00
nicolas.dorier
0f3d47ca6a Fix tests, update NBitcoin 2018-10-24 17:47:35 +09:00
nicolas.dorier
8db153b517 Simplify tests 2018-10-24 17:03:28 +09:00
nicolas.dorier
7be06f21f1 fix build 2018-10-23 13:27:01 +09:00
nicolas.dorier
c24bc6a376 Fix ScanUTXOSet if rescan a transaction already existing 2018-10-23 13:26:18 +09:00
nicolas.dorier
cd42e550a2 Asyncify the code 2018-10-23 13:00:31 +09:00
nicolas.dorier
8cd9bfdbb1 Fix some indents 2018-10-23 12:20:16 +09:00
nicolas.dorier
6fc5b27c0c Make UTXO scanning configurable 2018-10-23 11:51:45 +09:00
nicolas.dorier
1b8aadfa82 Fix pruning 2018-10-23 00:53:28 +09:00
nicolas.dorier
764b1752d7 Add GetKeyInformation(extkey, scriptPubKey) 2018-10-22 23:26:05 +09:00
nicolas.dorier
09b3147c6e Implement ScanTxoutSet 2018-10-22 23:02:13 +09:00
nicolas.dorier
4fa724c88c Add Index and TransactionHash to UTXOs because it is easier to parse 2018-10-22 18:53:13 +09:00
nicolas.dorier
893e58392a Refactoring by removing some useless type and mapping 2018-10-22 18:45:25 +09:00
nicolas.dorier
c3e1c7dd86 add experimental notice 2018-10-21 19:34:47 +09:00
nicolas.dorier
2c7fa5dc79 Flatten TrackedTransaction hierarchy 2018-10-21 15:51:06 +09:00
nicolas.dorier
164f90aedb Do not directly depends on knowing the transaction when a match occurs 2018-10-21 01:43:22 +09:00
nicolas.dorier
1225375313 Make sure TrackAsync is run in constant time 2018-10-20 23:59:08 +09:00
nicolas.dorier
f96cf0ea6c Implement auto pruning 2018-10-20 22:28:54 +09:00
nicolas.dorier
520bd58cde Remove Events collection, keep track of prunable transaction in UTXOByOutpoint 2018-10-20 14:11:29 +09:00
nicolas.dorier
85657eefcd [Refactor] Introduce the concept of PrunedTrackedTransaction 2018-10-20 12:58:08 +09:00
nicolas.dorier
ed3ce02a74 Expose node capabilities to NBXplorer 2018-10-18 22:21:34 +09:00
nicolas.dorier
469e2c507e Take advantage of ScanRPCCapabilties from NBitcoin to seamlessly fallback to compatible RPC calls 2018-10-18 19:09:01 +09:00
nicolas.dorier
66c2f85213 Bump test to 0.17.0 2018-10-17 16:57:10 +09:00
nicolas.dorier
2633c4ee4a Remove logs 2018-10-17 16:26:23 +09:00
nicolas.dorier
972b16c53f Add log on the ip used to connect to the node 2018-10-14 20:13:46 +09:00
nicolas.dorier
a35d34b841 Fix build error 2018-10-14 20:08:21 +09:00
nicolas.dorier
e687149636 GetFeeRate should work if estimatesmartfee not supported (Fix #115) 2018-10-11 23:44:25 +09:00
nicolas.dorier
1c70889ebb Fix potential error 500 on GetTransactions if a transaction has been orphaned 2018-10-11 18:36:28 +09:00
nicolas.dorier
49325079d9 bump 2018-10-11 00:42:04 +09:00
nicolas.dorier
9af604834d Commit new address creation 2018-10-11 00:41:24 +09:00
nicolas.dorier
243521c2a9 Revert "Add logs if error in explorer behavior"
This reverts commit ec02fd91a1.
2018-10-11 00:33:07 +09:00
nicolas.dorier
ec02fd91a1 Add logs if error in explorer behavior 2018-10-07 23:24:00 +09:00
nicolas.dorier
fbc1b3e041 fix typo 2018-10-02 22:10:43 +09:00
nicolas.dorier
72511d86de Document sdk requirement 2018-10-02 22:07:31 +09:00
nicolas.dorier
e7fe75b982 Update .net images 2018-10-02 19:10:07 +09:00
nicolas.dorier
d8ee138522 bump 2018-10-02 18:19:04 +09:00
nicolas.dorier
929090d1b1 Cleanup json if using single address and document single address API 2018-10-02 18:11:30 +09:00
nicolas.dorier
ed662ee1e8 Add test 2018-10-02 17:39:44 +09:00
nicolas.dorier
3c2489d41e Remove portential deadlock 2018-10-02 17:38:47 +09:00
nicolas.dorier
900db2780d Improve deserialization performance by caching serialization of base58 strings 2018-10-02 17:37:48 +09:00
nicolas.dorier
4a7d640f0a Add support for tracking individual addresses 2018-10-02 16:33:26 +09:00
chriswhellams
6ee6b2a4b6 Hash Azure Serive Bus messageId and Validate (#110) 2018-10-01 17:29:55 +09:00
Issei hoshikawa
916444553a fixed description (#108) 2018-09-28 22:46:28 +09:00
nicolas.dorier
67b03fbfc0 bump client 2018-09-28 10:03:46 +09:00
nicolas.dorier
6a2e3866f2 Fix #96 2018-09-28 10:03:06 +09:00
nicolas.dorier
07fe181cbf bump 2018-09-27 17:48:15 +09:00
nicolas.dorier
c588da7112 Drop cached chain if tip inside it is unknown by the node 2018-09-27 17:47:42 +09:00
Jonathan Underwood
8fa607de4d Bump bitcoind version for docker-compose example. (#107) 2018-09-27 15:47:40 +09:00
nicolas.dorier
c2dce69fe6 Change message id for azure to not have collisions 2018-08-30 16:39:54 +09:00
nicolas.dorier
76c4204dc6 bump 2018-08-30 16:33:20 +09:00
nicolas.dorier
e6204c57ec Rewrite code to be more consistent on longpolling timeout 2018-08-30 16:33:06 +09:00
nicolas.dorier
0622b5c2fe Update README.md 2018-08-30 15:39:47 +09:00
nicolas.dorier
ac905261cb Generalize the AzureServiceBus to BrokerHostedService 2018-08-30 15:34:47 +09:00
nicolas.dorier
a7fecd761f Merge branch 'azureservicebus' of https://github.com/WhaleLend/NBXplorer into WhaleLend-azureservicebus 2018-08-30 14:09:25 +09:00
David
4e4a4f01f9 Bring supported coins in alphabetic order (#88) 2018-08-30 10:45:28 +09:00
chris@whalelend.com
35c747030b Azure Service Bus message broker 2018-08-22 17:43:29 +03:00
nicolas.dorier
a9b6ef141b bump 2018-08-21 13:38:00 +09:00
nicolas.dorier
5b09108a51 Remove MarkAsUsed 2018-08-21 01:21:10 +09:00
nicolas.dorier
7d859f9f37 Generate most of the keys in a background thread 2018-08-21 01:01:23 +09:00
nicolas.dorier
14e79c2f8a Remove unused code 2018-08-16 16:37:12 +09:00
nicolas.dorier
ff67fc2b57 remove manual pruning (It is actually not needed) 2018-08-16 15:52:57 +09:00
nicolas.dorier
f6e118f2e7 LogWarn => LogInfo, do not wait long for pruning if RPC is not available 2018-08-16 15:25:15 +09:00
nicolas.dorier
afd8b0de28 Make sure PruneKeepOnly has reasonable value 2018-08-16 14:11:16 +09:00
nicolas.dorier
09c4ed1853 Add prunekeeponly option 2018-08-16 14:07:05 +09:00
nicolas.dorier
6509624141 Handle "Blockchain is shorter than the attempted prune height." during pruning 2018-08-15 00:50:15 +09:00
nicolas.dorier
ab3a914134 Delay pruning if the blockchain is too small to be pruned 2018-08-14 23:48:09 +09:00
nicolas.dorier
3965336852 bump client package 2018-08-14 22:39:35 +09:00
nicolas.dorier
c6d8911a9e remove useless code 2018-08-14 22:37:38 +09:00
nicolas.dorier
09d5adf7ae Bump 2018-08-14 22:30:47 +09:00
nicolas.dorier
d7ca0f2dc2 Support btc.prunebeforeheight 2018-08-14 22:29:22 +09:00
nicolas.dorier
0deeb9a08b Decrease dependencies on DBreeze 2018-08-14 21:45:57 +09:00
chriswhellams
551f48d6bf Readme typos (#84)
* Update README.md

Fixed typos, added a section on using Postman with Basic Auth to test the API , with example.

* Fixed README headings

Fixed markdown for a couple of headings

* Update README.md

more typo fixes

* Update README.md
2018-08-13 21:25:53 +09:00
chriswhellams
3a0e9344ab Update README.md (#83)
Fixed typos, added a section on using Postman with Basic Auth to test the API , with example.
2018-08-13 20:54:41 +09:00
nicolas.dorier
2a6d728b3a Refactor Repository 2018-08-12 17:30:29 +09:00
nicolas.dorier
08f48100e4 fix indent of doc 2018-08-11 01:31:39 +09:00
nicolas.dorier
d866fc1267 Remove ```json 2018-08-11 01:30:50 +09:00
nicolas.dorier
81cfcd5cd9 bump 2018-08-11 01:24:58 +09:00
nicolas.dorier
88b11589d3 Can rescan transactions after tracking a derivation scheme 2018-08-11 01:23:25 +09:00
nicolas.dorier
cfc74f20a8 Do not throw Error 500 if rpc is not available 2018-08-07 23:17:23 +09:00
nicolas.dorier
0037b4a0c0 Document HTTP error codes 2018-08-07 23:08:56 +09:00
nicolas.dorier
7e20b13e0f Bump package 2018-08-07 18:03:07 +09:00
nicolas.dorier
a8b683a38f Force GC collect once the blocks are loaded 2018-08-04 23:55:04 +09:00
nicolas.dorier
1860ebb449 bump NBitcoin 2018-08-04 11:38:45 +09:00
nicolas.dorier
caf09c205b bump 2018-08-04 03:20:42 +09:00
nicolas.dorier
b9019ed83d Bump NBitcoin 2018-08-04 03:17:32 +09:00
nicolas.dorier
87d3211d78 fix load chain bug 2018-08-04 01:58:27 +09:00
nicolas.dorier
031d578799 bump 2018-08-04 01:34:14 +09:00
nicolas.dorier
7196fcfa85 Update NBitcoin, remove Async version for loading and saving the hash chain 2018-08-04 01:30:59 +09:00
nicolas.dorier
994b6c0e7c Fix crash for chain cache migration 2018-08-04 01:04:54 +09:00
nicolas.dorier
d01eda31d6 Use NBitcoin's SlimChain 2018-08-04 00:51:05 +09:00
nicolas.dorier
9babc8364d Properly cap exponential easing of the waiter 2018-07-30 20:59:49 +09:00
nicolas.dorier
4372c0c654 NodeWaiter should return if status.BitcoinStatus.IsSynched is true 2018-07-28 01:49:00 +09:00
nicolas.dorier
4be7fc4ceb Fix NodeWaiter 2018-07-28 01:40:26 +09:00
nicolas.dorier
460fd5edeb Add explorerurl 2018-07-28 01:21:35 +09:00
nicolas.dorier
c0a112020f Add tools NodeWaiter 2018-07-28 00:32:52 +09:00
nicolas.dorier
7b876ff971 Make sure the cookie get created at app startup 2018-07-26 13:00:33 +09:00
Nicolas Dorier
8e8105797c
Fix #72 2018-07-25 17:50:56 +09:00
nicolas.dorier
aa7fdf7ff3 bump NBitcoin 2018-07-24 00:35:47 +09:00
nicolas.dorier
354d126f57 bump 2018-07-23 11:37:17 +09:00
nicolas.dorier
c42ced63a5 bump 2018-07-21 23:16:46 +09:00
nicolas.dorier
bc9a31c16e Do not create folders when using NBXplorerDefaultSettings 2018-07-21 23:16:05 +09:00
nicolas.dorier
e485a18d35 Update NBitcoin 2018-07-21 22:47:21 +09:00
nicolas.dorier
d51dbaae49 Update NBitcoin to support VIA 2018-07-13 15:29:54 +09:00
Ben Woosley
73b826ef34 Ignore .vscode directory used by Visual Studio Code (#68) 2018-07-10 11:55:01 +09:00
Ben Woosley
b7e49a2d80 Remove defunct 'currently supporting' note from readme (#67) 2018-07-10 11:54:44 +09:00
nicolas.dorier
15a4644d7e Create NBXplorer cookie before the first request 2018-06-30 23:01:33 +09:00
nicolas.dorier
1316359c79 bump 2018-06-29 13:26:04 +09:00
ChekaZ
6748cf66de Reverse Feathercoin fix (#63)
- Cookie Auth now enabled.
2018-06-27 21:36:32 +09:00
nicolas.dorier
9fd9f6a87c Improve logs 2018-06-24 21:04:22 +09:00
nicolas.dorier
09b3c1975d Add groestlcoin as supported in README 2018-06-23 23:59:44 +09:00
nicolas.dorier
0572d770af Support for Groestlcoin 2018-06-23 23:27:48 +09:00
ChekaZ
139fa4bd05 Feathercoin fix (#62)
- fix for RPC-Authentication
2018-06-23 10:59:26 +09:00
nicolas.dorier
9280707dfc Fix package 2018-06-21 22:47:15 +09:00
nicolas.dorier
7628b2e4de bump test framework 2018-06-21 22:02:40 +09:00
Romano
df941a62aa Add Viacoin (#61) 2018-06-21 21:59:25 +09:00
nicolas.dorier
670c855dc0 update link 2018-06-01 13:23:17 +09:00
nicolas.dorier
4a45f781c6 Update packages and use C# 7.3 2018-05-31 16:24:53 +09:00
nicolas.dorier
8751027a32 bump 2018-05-31 16:08:23 +09:00
nicolas.dorier
ff43044532 Update packages, pass on .net 2.1 final 2018-05-31 16:08:23 +09:00
ChekaZ
e3cf900556 Add UFO (#60) 2018-05-30 11:05:21 +09:00
ChekaZ
2c74fc9fcd Add Feathercoin (#59) 2018-05-22 22:25:16 +09:00
nicolas.dorier
92a2c5a4ad bumping test project 2018-05-15 22:30:37 +09:00
nicolas.dorier
ddf608c0c7 fix run.sh 2018-05-14 22:09:17 +09:00
nicolas.dorier
7847cae40f bump 2018-05-11 15:12:53 +09:00
cryptcoin-junkey
d6c5ae7a31 Add Monacoin. (#57) 2018-05-11 13:49:43 +09:00
292 changed files with 28680 additions and 6281 deletions

43
.circleci/config.yml Normal file
View File

@ -0,0 +1,43 @@
version: 2
jobs:
test:
machine:
- image: ubuntu-2004:202201-02
steps:
- checkout
- run:
command: |
cd .circleci && ./run-tests.sh
# publish jobs require $DOCKERHUB_REPO, $DOCKERHUB_USER, $DOCKERHUB_PASS defined
docker:
docker:
- image: cimg/base:stable
steps:
- setup_remote_docker
- checkout
- run:
command: |
docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
docker buildx create --use
docker buildx build -t $DOCKERHUB_REPO:$LATEST_TAG --platform linux/amd64,linux/arm64,linux/arm/v7 --push .
workflows:
version: 2
build_and_test:
jobs:
- test
publish:
jobs:
- docker:
filters:
branches:
ignore: /.*/
# only act on version tags v1.0.0.88 or v1.0.2-1
# OR feature tags like abc
# OR features on specific versions like v1.0.0.88-abc-1
tags:
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/

7
.circleci/run-tests.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
set -e
cd ../NBXplorer.Tests
docker-compose -v
docker-compose build
docker-compose run tests

View File

@ -121,5 +121,4 @@ bower_components
output
.vs
NBXplorer.Tests/
**/launchSettings.json

17
.gitattributes vendored Normal file
View File

@ -0,0 +1,17 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.h text
# Declare files that will always have CRLF line endings on checkout.
*.sln text eol=crlf
# Declare files that will always have CRLF line endings on checkout.
*.sh text eol=lf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary

4
.gitignore vendored
View File

@ -26,6 +26,8 @@ bld/
# Visual Studio 2015 cache/options directory
.vs/
# Visual Studio Code directory
.vscode/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
@ -46,7 +48,6 @@ dlldata.c
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c
@ -286,3 +287,4 @@ __pycache__/
*.btm.cs
*.odx.cs
*.xsd.cs
/NBXplorer.Tests/Properties/launchSettings.json

View File

@ -1,19 +1,21 @@
FROM microsoft/dotnet:2.1.300-rc1-sdk-alpine3.7 AS builder
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0.301-noble AS builder
WORKDIR /source
COPY NBXplorer/NBXplorer.csproj NBXplorer/NBXplorer.csproj
COPY NBXplorer.Client/NBXplorer.Client.csproj NBXplorer.Client/NBXplorer.Client.csproj
# Cache some dependencies
RUN cd NBXplorer && dotnet restore && cd ..
COPY . .
RUN cd NBXplorer && \
dotnet add package ILLink.Tasks --version 0.1.5-preview-1461378 --source https://dotnet.myget.org/F/dotnet-core/api/v3/index.json && \
dotnet publish --output /app/ --configuration Release -r linux-musl-x64
dotnet publish --output /app/ --configuration Release
FROM microsoft/dotnet:2.1.0-rc1-runtime-deps-alpine3.7
FROM mcr.microsoft.com/dotnet/aspnet:10.0.9-noble
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
RUN mkdir /datadir
ENV NBXPLORER_DATADIR=/datadir
VOLUME /datadir
COPY --from=builder "/app" .
ENTRYPOINT ["./NBXplorer"]
ENTRYPOINT ["dotnet", "NBXplorer.dll"]

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NBXplorer.Client" Version="4.2.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,183 @@
using NBitcoin;
using NBitcoin.RPC;
using NBXplorer;
using NBXplorer.DerivationStrategy;
using NBXplorer.Models;
using System;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
namespace MultiSig
{
class Program
{
class Party
{
public Party(Mnemonic mnemonic, string password, KeyPath accountKeyPath)
{
// Note: you could just generate the ExtKey with new ExtKey() and save extKey.GetWif(network) somewhere.
// But saving a mnemonic + password is well known UX
Mnemonic = mnemonic;
PartyName = password; //lazy yes
RootExtKey = mnemonic.DeriveExtKey(password);
AccountExtPubKey = RootExtKey.Derive(accountKeyPath).Neuter();
// The AccountKeyPath should be stored along the AccountExtPubKey
// This is the keypath + the hash of the root hd key.
// During signing, NBitcoin need this information to derive the RootExtKey to the address keypath properly.
AccountKeyPath = new RootedKeyPath(RootExtKey.GetPublicKey().GetHDFingerPrint(), accountKeyPath);
}
public string PartyName;
public Mnemonic Mnemonic;
public ExtPubKey AccountExtPubKey;
public ExtKey RootExtKey;
public RootedKeyPath AccountKeyPath;
}
// We will:
// 1. Create a multi sig wallet of Alice and Bob
// 2. Fund it with 1 BTC
// 3. Send 0.4 BTC to a random address from it
public static async Task Main(string[] args)
{
// Start bitcoind and NBXplorer in regtest:
// * Run "bitcoind -regtest"
// * Run ".\build.ps1", then ".\run.ps1 -regtest" in NBXplorer
var network = Network.RegTest;
var client = CreateNBXClient(network);
// Now let's simulate alice and bob in a 2-2 multisig
var alice = new Party(new Mnemonic(Wordlist.English), "Alice",
new KeyPath("1'/2'/3'"));
var bob = new Party(new Mnemonic(Wordlist.English), "Bob",
new KeyPath("5'/2'/3'"));
Console.WriteLine($"Alice should secretly save '{alice.Mnemonic}', and remember her password 'Alice'");
Console.WriteLine("---");
Console.WriteLine($"Alice should secretly save '{bob.Mnemonic}', and remember her password 'Bob'");
Console.WriteLine("---");
Console.WriteLine($"Alice should share '{alice.AccountExtPubKey.GetWif(network)}' with Bob");
Console.WriteLine("---");
Console.WriteLine($"Bob should share '{bob.AccountExtPubKey.GetWif(network)}' with Alice");
var factory = new DerivationStrategyFactory(network);
var derivationStrategy = factory.CreateMultiSigDerivationStrategy(new[]
{
alice.AccountExtPubKey.GetWif(network),
bob.AccountExtPubKey.GetWif(network)
}, 2, new DerivationStrategyOptions() { ScriptPubKeyType = ScriptPubKeyType.SegwitP2SH });
Console.WriteLine("---");
Console.WriteLine($"The derivation strategy '{derivationStrategy}' represents all the data you need to know to track the multisig wallet");
// NBXplorer will start tracking this wallet.
await client.TrackAsync(derivationStrategy);
// This allow you to get events out of NBXPlorer
var evts = client.CreateLongPollingNotificationSession();
// Now let's fund the wallet
var address1 = (await client.GetUnusedAsync(derivationStrategy, DerivationFeature.Deposit)).Address;
var rpc = new RPCClient(network);
// If that fail, your bitcoin node need some bitcoins
// bitcoin-cli -regtest getnewaddress
// bitcoin-cli -regtest generatetoaddress 101 <address>
await rpc.SendToAddressAsync(address1, Money.Coins(1.0m));
await WaitTransaction(evts, derivationStrategy);
Console.WriteLine("---");
Console.WriteLine("Sent some money to the multi sig wallet");
Console.WriteLine("---");
// You can list transactions
var txs = await client.GetTransactionsAsync(derivationStrategy);
Console.WriteLine($"Number of unconf transactions: {txs.UnconfirmedTransactions.Transactions.Count}");
Console.WriteLine("---");
var balance = await client.GetBalanceAsync(derivationStrategy);
Console.WriteLine($"Balance: {balance.Unconfirmed}");
Console.WriteLine("---");
var randomDestination = new Key().PubKey.GetAddress(ScriptPubKeyType.Segwit, network);
var psbt = (await client.CreatePSBTAsync(derivationStrategy, new CreatePSBTRequest()
{
Destinations =
{
new CreatePSBTDestination()
{
Destination = randomDestination,
Amount = Money.Coins(0.4m),
SubstractFees = true // We will pay fee by sending to destination a bit less than 0.4 BTC
}
},
FeePreference = new FeePreference()
{
// 10 sat/byte. You can remove this in prod, as it will use bitcoin's core estimation.
ExplicitFeeRate = new FeeRate(10.0m)
}
})).PSBT;
var signedByAlice = Sign(alice, derivationStrategy, psbt);
Console.WriteLine("---");
var signedByBob = Sign (bob, derivationStrategy, psbt);
// OK both have signed
var fullySignedPSBT = signedByAlice.Combine(signedByBob);
fullySignedPSBT.Finalize();
var fullySignedTx = fullySignedPSBT.ExtractTransaction();
await client.BroadcastAsync(fullySignedTx);
// Let's wait NBX receives the tx
await WaitTransaction(evts, derivationStrategy);
balance = await client.GetBalanceAsync(derivationStrategy);
Console.WriteLine($"New balance: {balance.Unconfirmed}");
}
private static PSBT Sign(Party party, DerivationStrategyBase derivationStrategy, PSBT psbt)
{
psbt = psbt.Clone();
// NBXplorer does not have knowledge of the account key path, KeyPath are private information of each peer
// NBXplorer only derive 0/* and 1/* on top of provided account xpubs,
// This mean that the input keypaths in the PSBT are in the form 0/* (as if the account key was the root)
// RebaseKeyPaths modifies the PSBT by adding the AccountKeyPath in prefix of all the keypaths of the PSBT
// Note that this is not necessary to do this if the account key is the same as root key.
// Note that also that you don't have to do this, if you do not pass the account key path in the later SignAll call.
// however, this is best practice to rebase the PSBT before signing.
// If you sign with an offline device (hw wallet), the wallet would need the rebased PSBT.
psbt.RebaseKeyPaths(party.AccountExtPubKey, party.AccountKeyPath);
Console.WriteLine("A PSBT is a data structure with all information for a wallet to sign.");
var spend = psbt.GetBalance(derivationStrategy, party.AccountExtPubKey, party.AccountKeyPath);
Console.WriteLine($"{party.PartyName}, Do you agree to sign this transaction spending {spend}?");
// Ok I sign
psbt.SignAll(derivationStrategy, // What addresses to derive?
party.RootExtKey.Derive(party.AccountKeyPath), // With which account private keys?
party.AccountKeyPath); // What is the keypath of the account private key. If you did not rebased the keypath like before, you can remove this parameter
return psbt;
}
static async Task<NewTransactionEvent> WaitTransaction(LongPollingNotificationSession evts, DerivationStrategyBase derivationStrategy)
{
while (true)
{
var evt = await evts.NextEventAsync();
if (evt is NBXplorer.Models.NewTransactionEvent tx)
{
if (tx.DerivationStrategy == derivationStrategy)
return tx;
}
}
}
private static ExplorerClient CreateNBXClient(Network network)
{
NBXplorerNetworkProvider provider = new NBXplorerNetworkProvider(network.ChainName);
ExplorerClient client = new NBXplorer.ExplorerClient(provider.GetFromCryptoCode(network.NetworkSet.CryptoCode));
return client;
}
}
}

View File

@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MultiSig", "MultiSig\MultiSig.csproj", "{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Debug|x64.ActiveCfg = Debug|Any CPU
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Debug|x64.Build.0 = Debug|Any CPU
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Debug|x86.ActiveCfg = Debug|Any CPU
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Debug|x86.Build.0 = Debug|Any CPU
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Release|Any CPU.Build.0 = Release|Any CPU
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Release|x64.ActiveCfg = Release|Any CPU
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Release|x64.Build.0 = Release|Any CPU
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Release|x86.ActiveCfg = Release|Any CPU
{DBB077D5-4F0A-4D09-8E73-5A9C56877DDB}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,405 @@
using NBitcoin;
using System;
using System.Collections.Generic;
namespace NBXplorer
{
public class AssetMoney : IComparable, IComparable<AssetMoney>, IEquatable<AssetMoney>, IMoney
{
long _Quantity;
public long Quantity
{
get
{
return _Quantity;
}
// used as a central point where long.MinValue checking can be enforced
private set
{
CheckLongMinValue(value);
_Quantity = value;
}
}
private static void CheckLongMinValue(long value)
{
if (value == long.MinValue)
throw new OverflowException("satoshis amount should be greater than long.MinValue");
}
private readonly uint256 _Id;
/// <summary>
/// AssetId of the current amount
/// </summary>
public uint256 AssetId
{
get
{
return _Id;
}
}
/// <summary>
/// Get absolute value of the instance
/// </summary>
/// <returns></returns>
public AssetMoney Abs()
{
var a = this;
if (a.Quantity < 0)
a = -a;
return a;
}
#region ctor
public AssetMoney(uint256 assetId)
{
if (assetId == null)
throw new ArgumentNullException(nameof(assetId));
_Id = assetId;
}
public AssetMoney(uint256 assetId, int quantity)
{
if (assetId == null)
throw new ArgumentNullException(nameof(assetId));
_Id = assetId;
Quantity = quantity;
}
public AssetMoney(uint256 assetId, uint quantity)
{
if (assetId == null)
throw new ArgumentNullException(nameof(assetId));
_Id = assetId;
Quantity = quantity;
}
public AssetMoney(uint256 assetId, long quantity)
{
if (assetId == null)
throw new ArgumentNullException(nameof(assetId));
_Id = assetId;
Quantity = quantity;
}
public AssetMoney(uint256 assetId, ulong quantity)
{
if (assetId == null)
throw new ArgumentNullException(nameof(assetId));
_Id = assetId;
// overflow check.
// ulong.MaxValue is greater than long.MaxValue
checked
{
Quantity = (long)quantity;
}
}
public AssetMoney(uint256 assetId, decimal amount, int divisibility)
{
if (assetId == null)
throw new ArgumentNullException(nameof(assetId));
_Id = assetId;
// sanity check. Only valid units are allowed
checked
{
int dec = Pow10(divisibility);
var satoshi = amount * dec;
Quantity = (long)satoshi;
}
}
#endregion
private static int Pow10(int divisibility)
{
if (divisibility < 0)
throw new ArgumentOutOfRangeException("divisibility", "divisibility should be higher than 0");
int dec = 1;
for (int i = 0; i < divisibility; i++)
{
dec = dec * 10;
}
return dec;
}
/// <summary>
/// Split the Money in parts without loss
/// </summary>
/// <param name="parts">The number of parts (must be more than 0)</param>
/// <returns>The splitted money</returns>
public IEnumerable<AssetMoney> Split(int parts)
{
if (parts <= 0)
throw new ArgumentOutOfRangeException("Parts should be more than 0", "parts");
long remain;
long result = DivRem(_Quantity, parts, out remain);
for (int i = 0; i < parts; i++)
{
yield return new AssetMoney(_Id, result + (remain > 0 ? 1 : 0));
remain--;
}
}
private static long DivRem(long a, long b, out long result)
{
result = a % b;
return a / b;
}
public decimal ToDecimal(int divisibility)
{
var dec = Pow10(divisibility);
// overflow safe because (long / int) always fit in decimal
// decimal operations are checked by default
return (decimal)Quantity / (int)dec;
}
#region IEquatable<AssetMoney> Members
public bool Equals(AssetMoney other)
{
if (other == null)
return false;
CheckAssetId(other, "other");
return _Quantity.Equals(other.Quantity);
}
internal void CheckAssetId(AssetMoney other, string param)
{
if (other.AssetId != AssetId)
throw new ArgumentException("AssetMoney instance of different assets can't be computed together", param);
}
public int CompareTo(AssetMoney other)
{
if (other == null)
return 1;
CheckAssetId(other, "other");
return _Quantity.CompareTo(other.Quantity);
}
#endregion
#region IComparable Members
public int CompareTo(object obj)
{
if (obj == null)
return 1;
AssetMoney m = obj as AssetMoney;
if (m != null)
return _Quantity.CompareTo(m.Quantity);
#if !NETSTANDARD1X
return _Quantity.CompareTo(obj);
#else
return _Quantity.CompareTo((long)obj);
#endif
}
#endregion
public static AssetMoney operator -(AssetMoney left, AssetMoney right)
{
if (left == null)
throw new ArgumentNullException(nameof(left));
if (right == null)
throw new ArgumentNullException(nameof(right));
left.CheckAssetId(right, "right");
return new AssetMoney(left.AssetId, checked(left.Quantity - right.Quantity));
}
public static AssetMoney operator -(AssetMoney left)
{
if (left == null)
throw new ArgumentNullException(nameof(left));
return new AssetMoney(left.AssetId, checked(-left.Quantity));
}
public static AssetMoney operator +(AssetMoney left, AssetMoney right)
{
if (left == null)
throw new ArgumentNullException(nameof(left));
if (right == null)
throw new ArgumentNullException(nameof(right));
left.CheckAssetId(right, "right");
return new AssetMoney(left.AssetId, checked(left.Quantity + right.Quantity));
}
public static AssetMoney operator *(int left, AssetMoney right)
{
if (right == null)
throw new ArgumentNullException(nameof(right));
return new AssetMoney(right.AssetId, checked(left * right.Quantity));
}
public static AssetMoney operator *(AssetMoney right, int left)
{
if (right == null)
throw new ArgumentNullException(nameof(right));
return new AssetMoney(right.AssetId, checked(right.Quantity * left));
}
public static AssetMoney operator *(long left, AssetMoney right)
{
if (right == null)
throw new ArgumentNullException(nameof(right));
return new AssetMoney(right.AssetId, checked(left * right.Quantity));
}
public static AssetMoney operator *(AssetMoney right, long left)
{
if (right == null)
throw new ArgumentNullException(nameof(right));
return new AssetMoney(right.AssetId, checked(left * right.Quantity));
}
public static bool operator <(AssetMoney left, AssetMoney right)
{
if (left == null)
throw new ArgumentNullException(nameof(left));
if (right == null)
throw new ArgumentNullException(nameof(right));
left.CheckAssetId(right, "right");
return left.Quantity < right.Quantity;
}
public static bool operator >(AssetMoney left, AssetMoney right)
{
if (left == null)
throw new ArgumentNullException(nameof(left));
if (right == null)
throw new ArgumentNullException(nameof(right));
left.CheckAssetId(right, "right");
return left.Quantity > right.Quantity;
}
public static bool operator <=(AssetMoney left, AssetMoney right)
{
if (left == null)
throw new ArgumentNullException(nameof(left));
if (right == null)
throw new ArgumentNullException(nameof(right));
left.CheckAssetId(right, "right");
return left.Quantity <= right.Quantity;
}
public static bool operator >=(AssetMoney left, AssetMoney right)
{
if (left == null)
throw new ArgumentNullException(nameof(left));
if (right == null)
throw new ArgumentNullException(nameof(right));
left.CheckAssetId(right, "right");
return left.Quantity >= right.Quantity;
}
public override bool Equals(object obj)
{
AssetMoney item = obj as AssetMoney;
if (item == null)
return false;
if (item.AssetId != AssetId)
return false;
return _Quantity.Equals(item.Quantity);
}
public static bool operator ==(AssetMoney a, AssetMoney b)
{
if (Object.ReferenceEquals(a, b))
return true;
if (((object)a == null) || ((object)b == null))
return false;
if (a.AssetId != b.AssetId)
return false;
return a.Quantity == b.Quantity;
}
public static bool operator !=(AssetMoney a, AssetMoney b)
{
return !(a == b);
}
public override int GetHashCode()
{
return Tuple.Create(_Quantity, AssetId).GetHashCode();
}
public override string ToString()
{
return String.Format("{0}-{1}", Quantity, AssetId);
}
public static AssetMoney Min(AssetMoney a, AssetMoney b)
{
if (a == null)
throw new ArgumentNullException(nameof(a));
if (b == null)
throw new ArgumentNullException(nameof(b));
a.CheckAssetId(b, "b");
if (a <= b)
return a;
return b;
}
#region IMoney Members
IMoney IMoney.Add(IMoney money)
{
var assetMoney = (AssetMoney)money;
return this + assetMoney;
}
IMoney IMoney.Sub(IMoney money)
{
var assetMoney = (AssetMoney)money;
return this - assetMoney;
}
IMoney IMoney.Negate()
{
return this * -1;
}
int IComparable.CompareTo(object obj)
{
return this.CompareTo(obj);
}
int IComparable<IMoney>.CompareTo(IMoney other)
{
return this.CompareTo(other);
}
bool IEquatable<IMoney>.Equals(IMoney other)
{
return this.Equals(other);
}
bool IMoney.IsCompatible(IMoney money)
{
if (money == null)
throw new ArgumentNullException(nameof(money));
AssetMoney assetMoney = money as AssetMoney;
if (assetMoney == null)
return false;
return assetMoney.AssetId == AssetId;
}
#endregion
#region IMoney Members
IEnumerable<IMoney> IMoney.Split(int parts)
{
return Split(parts);
}
#endregion
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NBXplorer
{

View File

@ -0,0 +1,18 @@
using NBitcoin.Crypto;
using NBitcoin.DataEncoders;
using System.Text;
namespace NBXplorer.Client
{
public static class DBUtils
{
public static string nbxv1_get_wallet_id(string cryptoCode, string addressOrDerivation)
{
return Encoders.Base64.EncodeData(Hashes.SHA256(new UTF8Encoding(false).GetBytes($"{cryptoCode}|{addressOrDerivation}")), 0, 21);
}
public static string nbxv1_get_descriptor_id(string cryptoCode, string strategy, string feature)
{
return Encoders.Base64.EncodeData(Hashes.SHA256(new UTF8Encoding(false).GetBytes($"{cryptoCode}|{strategy}|{feature}")), 0, 21);
}
}
}

View File

@ -1,19 +1,49 @@
using NBitcoin;
using System;
#nullable enable
using NBitcoin;
using System.Collections.Generic;
using System.Text;
#if !NO_RECORD
using static NBitcoin.WalletPolicies.MiniscriptNode;
#endif
namespace NBXplorer.DerivationStrategy
{
public class Derivation
{
public Derivation(Script scriptPubKey, Script? redeem = null)
{
ScriptPubKey = scriptPubKey;
Redeem = redeem;
}
public Script ScriptPubKey
{
get; set;
get;
}
public Script Redeem
public Script? Redeem
{
get; set;
}
}
public class KeyPathDerivation : Derivation
{
public KeyPathDerivation(KeyPath keyPath, Script scriptPubKey, Script? redeem = null)
: base(scriptPubKey, redeem)
{
KeyPath = keyPath;
}
public KeyPath KeyPath { get; }
}
#if !NO_RECORD
public class PolicyDerivation : Derivation
{
public PolicyDerivation(NBitcoin.WalletPolicies.DerivationResult details, Script scriptPubKey, Script? redeem = null)
: base(scriptPubKey, redeem)
{
Details = details;
}
public NBitcoin.WalletPolicies.DerivationResult Details { get; }
}
#endif
}

View File

@ -1,32 +1,19 @@
using NBitcoin;
using NBXplorer.DerivationStrategy;
#if !NO_RECORD
using NBitcoin.WalletPolicies;
#endif
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Net;
using System.Text.RegularExpressions;
namespace NBXplorer.DerivationStrategy
{
public class DerivationStrategyOptions
{
/// <summary>
/// If true, use P2SH (default: false)
/// </summary>
public bool P2SH
{
get; set;
}
/// <summary>
/// If false, use segwit (default: false)
/// </summary>
public bool Legacy
{
get; set;
}
public ScriptPubKeyType ScriptPubKeyType { get; set; }
/// <summary>
/// If true, in case of multisig, do not reorder the public keys of an address lexicographically (default: false)
@ -35,6 +22,8 @@ namespace NBXplorer.DerivationStrategy
{
get; set;
}
public ReadOnlyDictionary<string, string> AdditionalOptions { get; set; }
}
public class DerivationStrategyFactory
{
@ -49,13 +38,24 @@ namespace NBXplorer.DerivationStrategy
}
public DerivationStrategyFactory(Network network)
{
if(network == null)
if (network == null)
throw new ArgumentNullException(nameof(network));
_Network = network;
if (_Network.Consensus.SupportSegwit)
{
AuthorizedOptions.Add("p2sh");
}
if (_Network.Consensus.SupportTaproot)
{
AuthorizedOptions.Add("taproot");
}
AuthorizedOptions.Add("keeporder");
AuthorizedOptions.Add("legacy");
}
public HashSet<string> AuthorizedOptions { get; } = new HashSet<string>();
readonly Regex MultiSigRegex = new Regex("^([0-9]{1,2})-of(-[A-Za-z0-9]+)+$");
static DirectDerivationStrategy DummyPubKey = new DirectDerivationStrategy(new ExtKey().Neuter().GetWif(Network.RegTest)) { Segwit = false };
public DerivationStrategyBase Parse(string str)
{
var strategy = ParseCore(str);
@ -65,25 +65,78 @@ namespace NBXplorer.DerivationStrategy
private DerivationStrategyBase ParseCore(string str)
{
bool legacy = false;
ReadBool(ref str, "legacy", ref legacy);
bool p2sh = false;
ReadBool(ref str, "p2sh", ref p2sh);
bool keepOrder = false;
ReadBool(ref str, "keeporder", ref keepOrder);
bool taproot = false;
ScriptPubKeyType type = ScriptPubKeyType.Segwit;
if(!legacy && !_Network.Consensus.SupportSegwit)
throw new FormatException("Segwit is not supported");
IDictionary<string, string> optionsDictionary = new Dictionary<string, string>(5);
foreach (Match optionMatch in _OptionRegex.Matches(str))
{
var rawKey = optionMatch.Groups[1].Value.ToLowerInvariant();
var splitKey = rawKey.Split(new[]{'='}, StringSplitOptions.RemoveEmptyEntries);
var key = splitKey[0];
var value = splitKey.Length > 1 ? splitKey[1]: null;
if (!AuthorizedOptions.Contains(key))
throw new FormatException($"The option '{key}' is not supported by this network");
if (!Extensions.TryAdd(optionsDictionary, key, value))
throw new FormatException($"The option '{key}' is duplicated");
}
var hasOptions = optionsDictionary.Count != 0;
str = _OptionRegex.Replace(str, string.Empty);
if (optionsDictionary.Remove("legacy"))
{
legacy = true;
type = ScriptPubKeyType.Legacy;
}
if (optionsDictionary.Remove("p2sh"))
{
p2sh = true;
type = ScriptPubKeyType.SegwitP2SH;
}
if (optionsDictionary.Remove("keeporder"))
{
keepOrder = true;
}
if (optionsDictionary.Remove("taproot"))
{
taproot = true;
#pragma warning disable CS0618 // Type or member is obsolete
type = ScriptPubKeyType.TaprootBIP86;
#pragma warning restore CS0618 // Type or member is obsolete
}
if (!legacy && !_Network.Consensus.SupportSegwit)
throw new FormatException("Segwit is not supported you need to specify option '-[legacy]'");
if (legacy && p2sh)
throw new FormatException("The option 'legacy' is incompatible with 'p2sh'");
if (taproot)
{
if (!_Network.Consensus.SupportTaproot)
{
throw new FormatException("Taproot is not supported, you need to remove option '-[taproot]'");
}
else
{
if (p2sh)
throw new FormatException("The option 'taproot' is incompatible with 'p2sh'");
if (legacy)
throw new FormatException("The option 'taproot' is incompatible with 'legacy'");
if (keepOrder)
throw new FormatException("The option 'taproot' is incompatible with 'keeporder'");
}
}
var options = new DerivationStrategyOptions()
{
KeepOrder = keepOrder,
Legacy = legacy,
P2SH = p2sh
ScriptPubKeyType = type,
AdditionalOptions = new ReadOnlyDictionary<string, string>(optionsDictionary)
};
var match = MultiSigRegex.Match(str);
if(match.Success)
if (match.Success)
{
var sigCount = int.Parse(match.Groups[1].Value);
var pubKeys = match.Groups
@ -94,6 +147,14 @@ namespace NBXplorer.DerivationStrategy
.ToArray();
return CreateMultiSigDerivationStrategy(pubKeys, sigCount, options);
}
#if !NO_RECORD
else if (PolicyDerivationStrategy._MaybeMiniscript.IsMatch(str))
{
if (hasOptions)
throw new FormatException("The derivation scheme should not contain any option (such as -[legacy])");
return PolicyDerivationStrategy.Parse(str, _Network);
}
#endif
else
{
var key = _Network.Parse<BitcoinExtPubKey>(str);
@ -107,7 +168,7 @@ namespace NBXplorer.DerivationStrategy
/// <param name="publicKey">The public key of the wallet</param>
/// <param name="options">Derivation options</param>
/// <returns></returns>
public DerivationStrategyBase CreateDirectDerivationStrategy(ExtPubKey publicKey, DerivationStrategyOptions options = null)
public StandardDerivationStrategyBase CreateDirectDerivationStrategy(ExtPubKey publicKey, DerivationStrategyOptions options = null)
{
return CreateDirectDerivationStrategy(publicKey.GetWif(Network), options);
}
@ -118,19 +179,41 @@ namespace NBXplorer.DerivationStrategy
/// <param name="publicKey">The public key of the wallet</param>
/// <param name="options">Derivation options</param>
/// <returns></returns>
public DerivationStrategyBase CreateDirectDerivationStrategy(BitcoinExtPubKey publicKey, DerivationStrategyOptions options = null)
public StandardDerivationStrategyBase CreateDirectDerivationStrategy(BitcoinExtPubKey publicKey, DerivationStrategyOptions options = null)
{
options = options ?? new DerivationStrategyOptions();
DerivationStrategyBase strategy = new DirectDerivationStrategy(publicKey) { Segwit = !options.Legacy };
if(!options.Legacy && !_Network.Consensus.SupportSegwit)
throw new InvalidOperationException("This crypto currency does not support segwit");
if(options.P2SH && !options.Legacy)
StandardDerivationStrategyBase strategy = null;
#pragma warning disable CS0618 // Type or member is obsolete
if (options.ScriptPubKeyType != ScriptPubKeyType.TaprootBIP86)
#pragma warning restore CS0618 // Type or member is obsolete
{
strategy = new P2SHDerivationStrategy(strategy, true);
strategy = new DirectDerivationStrategy(publicKey, options.ScriptPubKeyType != ScriptPubKeyType.Legacy, options.AdditionalOptions);
if (options.ScriptPubKeyType == ScriptPubKeyType.Segwit && !_Network.Consensus.SupportSegwit)
throw new InvalidOperationException("This crypto currency does not support segwit");
if (options.ScriptPubKeyType == ScriptPubKeyType.SegwitP2SH)
{
strategy = new P2SHDerivationStrategy(strategy, true);
}
}
else
{
if (!_Network.Consensus.SupportTaproot)
throw new InvalidOperationException("This crypto currency does not support taproot");
strategy = new TaprootDerivationStrategy(publicKey, options.AdditionalOptions);
}
return strategy;
}
/// <summary>
/// Create a taproot signature derivation strategy from public key
/// </summary>
/// <param name="publicKey">The public key of the wallet</param>
/// <param name="options">Derivation options</param>
/// <returns></returns>
public TaprootDerivationStrategy CreateTaprootDerivationStrategy(BitcoinExtPubKey publicKey, ReadOnlyDictionary<string, string> options = null)
{
return new TaprootDerivationStrategy(publicKey, options);
}
/// <summary>
/// Create a multisig derivation strategy from public keys
@ -154,33 +237,19 @@ namespace NBXplorer.DerivationStrategy
public DerivationStrategyBase CreateMultiSigDerivationStrategy(BitcoinExtPubKey[] pubKeys, int sigCount, DerivationStrategyOptions options = null)
{
options = options ?? new DerivationStrategyOptions();
DerivationStrategyBase derivationStrategy = new MultisigDerivationStrategy(sigCount, pubKeys.ToArray(), options.Legacy)
{
LexicographicOrder = !options.KeepOrder
};
if(options.Legacy)
StandardDerivationStrategyBase derivationStrategy = new MultisigDerivationStrategy(sigCount, pubKeys.ToArray(), options.ScriptPubKeyType == ScriptPubKeyType.Legacy, !options.KeepOrder, options.AdditionalOptions);
if (options.ScriptPubKeyType == ScriptPubKeyType.Legacy)
return new P2SHDerivationStrategy(derivationStrategy, false);
if(!_Network.Consensus.SupportSegwit)
if (!_Network.Consensus.SupportSegwit)
throw new InvalidOperationException("This crypto currency does not support segwit");
derivationStrategy = new P2WSHDerivationStrategy(derivationStrategy);
if(options.P2SH)
if (options.ScriptPubKeyType == ScriptPubKeyType.SegwitP2SH)
{
derivationStrategy = new P2SHDerivationStrategy(derivationStrategy, true);
}
return derivationStrategy;
}
private void ReadBool(ref string str, string attribute, ref bool value)
{
value = str.Contains($"[{attribute}]");
if(value)
{
str = str.Replace($"[{attribute}]", string.Empty);
str = str.Replace("--", "-");
if(str.EndsWith("-"))
str = str.Substring(0, str.Length - 1);
}
}
readonly static Regex _OptionRegex = new Regex(@"-\[([^ \]\-]+)\]");
}
}

View File

@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using NBitcoin;
using NBitcoin.Crypto;
namespace NBXplorer.DerivationStrategy
{
public class DirectDerivationStrategy : DerivationStrategyBase
public class DirectDerivationStrategy : StandardDerivationStrategyBase
{
BitcoinExtPubKey _Root;
@ -21,10 +21,9 @@ namespace NBXplorer.DerivationStrategy
public bool Segwit
{
get;
set;
}
protected override string StringValue
protected internal override string StringValueCore
{
get
{
@ -38,21 +37,23 @@ namespace NBXplorer.DerivationStrategy
}
}
public DirectDerivationStrategy(BitcoinExtPubKey root)
public DirectDerivationStrategy(BitcoinExtPubKey root, bool segwit, ReadOnlyDictionary<string, string> additionalOptions = null) : base(additionalOptions)
{
if(root == null)
throw new ArgumentNullException(nameof(root));
_Root = root;
}
public override Derivation Derive(KeyPath keyPath)
{
var pubKey = _Root.ExtPubKey.Derive(keyPath).PubKey;
return new Derivation() { ScriptPubKey = Segwit ? pubKey.WitHash.ScriptPubKey : pubKey.Hash.ScriptPubKey };
Segwit = segwit;
}
public override DerivationStrategyBase GetLineFor(KeyPath keyPath)
public override Derivation GetDerivation(KeyPath keyPath)
{
return new DirectDerivationStrategy(_Root.ExtPubKey.Derive(keyPath).GetWif(_Root.Network)) { Segwit = Segwit };
var pubKey = _Root.ExtPubKey.Derive(keyPath).PubKey;
return new KeyPathDerivation(keyPath, Segwit ? pubKey.WitHash.ScriptPubKey : pubKey.Hash.ScriptPubKey);
}
public override IEnumerable<ExtPubKey> GetExtPubKeys()
{
yield return _Root.ExtPubKey;
}
}
}

View File

@ -1,8 +1,12 @@
using NBitcoin;
using NBitcoin.Crypto;
#nullable enable
using NBitcoin;
#if !NO_RECORD
using NBitcoin.WalletPolicies;
#endif
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using System.Linq;
namespace NBXplorer.DerivationStrategy
{
@ -10,72 +14,132 @@ namespace NBXplorer.DerivationStrategy
{
Change = 1,
Deposit = 0,
Direct = 2
}
Direct = 2,
Custom = 3,
}
public abstract class StandardDerivationStrategyBase : DerivationStrategyBase, IHDScriptPubKey
{
internal StandardDerivationStrategyBase(ReadOnlyDictionary<string, string> additionalOptions) : base(additionalOptions)
{
}
public abstract Derivation GetDerivation(KeyPath keyPath);
public override DerivationLine GetLineFor(KeyPathTemplates keyPathTemplates, DerivationFeature feature)
=> new KeyPathTemplateDerivationLine(this, keyPathTemplates, feature);
Script IHDScriptPubKey.ScriptPubKey => GetDerivation(KeyPath.Empty).ScriptPubKey;
IHDScriptPubKey? IHDScriptPubKey.Derive(KeyPath keyPath) => keyPath.IsHardenedPath ? null : new HDScriptPubKey(this, keyPath);
class HDScriptPubKey(StandardDerivationStrategyBase Parent, KeyPath KeyPath) : IHDScriptPubKey
{
public Script ScriptPubKey => Parent.GetDerivation(KeyPath).ScriptPubKey;
public IHDScriptPubKey? Derive(KeyPath keyPath) => KeyPath.IsHardenedPath ? null : new HDScriptPubKey(Parent, KeyPath.Derive(keyPath));
}
}
public abstract class DerivationStrategyBase
{
internal DerivationStrategyBase()
{
readonly ReadOnlyDictionary<string, string> Empty = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>(0));
public ReadOnlyDictionary<string, string> AdditionalOptions { get; }
internal DerivationStrategyBase(ReadOnlyDictionary<string,string>? additionalOptions)
{
AdditionalOptions = additionalOptions ?? Empty;
}
public static KeyPath GetKeyPath(DerivationFeature derivationFeature)
{
return derivationFeature == DerivationFeature.Direct ? new KeyPath() : new KeyPath((uint)derivationFeature);
}
public static DerivationFeature GetFeature(KeyPath path)
{
return path.Indexes.Length == 1 ? DerivationFeature.Direct : (DerivationFeature)path.Indexes[0];
}
public DerivationStrategyBase GetLineFor(DerivationFeature derivationFeature)
{
return derivationFeature == DerivationFeature.Direct ? this :
GetLineFor(GetKeyPath(derivationFeature));
}
public DerivationLine GetLineFor(DerivationFeature feature) => GetLineFor(KeyPathTemplates.Default, feature);
public abstract DerivationLine GetLineFor(KeyPathTemplates keyPathTemplates, DerivationFeature feature);
public abstract DerivationStrategyBase GetLineFor(KeyPath keyPath);
public Derivation Derive(uint i)
{
return Derive(new KeyPath(i));
}
public abstract Derivation Derive(KeyPath keyPath);
protected abstract string StringValue
protected internal abstract string StringValueCore
{
get;
}
public override bool Equals(object obj)
string? _StringValue;
string StringValue
{
DerivationStrategyBase item = obj as DerivationStrategyBase;
if(item == null)
return false;
return StringValue.Equals(item.StringValue);
}
public static bool operator ==(DerivationStrategyBase a, DerivationStrategyBase b)
{
if(System.Object.ReferenceEquals(a, b))
return true;
if(((object)a == null) || ((object)b == null))
return false;
return a.StringValue == b.StringValue;
get
{
if (_StringValue == null)
{
if (AdditionalOptions.Count == 0)
_StringValue = StringValueCore;
else
_StringValue = $"{StringValueCore}{GetSuffixOptionsString()}";
}
return _StringValue;
}
}
public static bool operator !=(DerivationStrategyBase a, DerivationStrategyBase b)
private string GetSuffixOptionsString()
{
return !(a == b);
return string.Join("", new SortedDictionary<string, string>(AdditionalOptions).Select(pair => $"-[{pair.Key}{(string.IsNullOrEmpty(pair.Value)?string.Empty: $"={pair.Value}")}]"));
}
#nullable enable
public override bool Equals(object? obj) => obj is DerivationStrategyBase o && StringValue.Equals(o.StringValue);
public static bool operator ==(DerivationStrategyBase? a, DerivationStrategyBase? b) => a is null ? b is null : a.Equals(b);
public static bool operator !=(DerivationStrategyBase? a, DerivationStrategyBase? b) => !(a == b);
public override int GetHashCode() => StringValue.GetHashCode();
#nullable restore
public override int GetHashCode()
{
return StringValue.GetHashCode();
}
public abstract IEnumerable<ExtPubKey> GetExtPubKeys();
public override string ToString()
{
return StringValue;
}
}
#if !NO_RECORD
public class MiniscriptDerivationLine : DerivationLine
{
public MiniscriptDerivationLine(PolicyDerivationStrategy derivationStrategy, DerivationFeature derivationFeature) : base(derivationFeature)
{
DerivationStrategy = derivationStrategy;
Intent = ToAddressIntent(derivationFeature);
}
public static AddressIntent ToAddressIntent(DerivationFeature derivationFeature)
{
return derivationFeature switch
{
DerivationFeature.Change => AddressIntent.Change,
DerivationFeature.Deposit => AddressIntent.Deposit,
_ => throw new NotSupportedException("MiniscriptDerivationStrategy only support deposit and change features")
};
}
public PolicyDerivationStrategy DerivationStrategy { get; }
public AddressIntent Intent { get; }
public override Derivation Derive(uint index) => DerivationStrategy.GetDerivation(Intent, index);
}
#endif
public abstract class DerivationLine
{
protected DerivationLine(DerivationFeature feature)
{
Feature = feature;
}
public DerivationFeature Feature { get; }
public abstract Derivation Derive(uint index);
}
public class KeyPathTemplateDerivationLine : DerivationLine
{
public KeyPathTemplateDerivationLine(StandardDerivationStrategyBase derivationStrategyBase, KeyPathTemplates keyPathTemplates, DerivationFeature derivationFeature) : base(derivationFeature)
{
if (derivationStrategyBase == null)
throw new ArgumentNullException(nameof(derivationStrategyBase));
if (keyPathTemplates == null)
throw new ArgumentNullException(nameof(keyPathTemplates));
DerivationStrategyBase = derivationStrategyBase;
KeyPathTemplate = keyPathTemplates.GetKeyPathTemplate(derivationFeature);
}
public StandardDerivationStrategyBase DerivationStrategyBase { get; }
public KeyPathTemplate KeyPathTemplate { get; }
public override Derivation Derive(uint index)
{
var kp = KeyPathTemplate.GetKeyPath(index);
return DerivationStrategyBase.GetDerivation(kp);
}
}
}

View File

@ -1,34 +1,34 @@
using NBitcoin;
using System.Linq;
using NBXplorer.DerivationStrategy;
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using NBitcoin.Crypto;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
namespace NBXplorer.DerivationStrategy
{
public class MultisigDerivationStrategy : DerivationStrategyBase
public class MultisigDerivationStrategy : StandardDerivationStrategyBase
{
public bool LexicographicOrder
{
get; set;
get;
}
public int RequiredSignatures
{
get; set;
get;
}
static readonly Comparer<PubKey> LexicographicComparer = Comparer<PubKey>.Create((a, b) => Comparer<string>.Default.Compare(a?.ToHex(), b?.ToHex()));
public BitcoinExtPubKey[] Keys
public ReadOnlyCollection<BitcoinExtPubKey> Keys
{
get; set;
get;
}
protected override string StringValue
protected internal override string StringValueCore
{
get
{
@ -48,41 +48,38 @@ namespace NBXplorer.DerivationStrategy
}
}
internal MultisigDerivationStrategy(int reqSignature, BitcoinExtPubKey[] keys, bool isLegacy)
internal MultisigDerivationStrategy(int reqSignature, BitcoinExtPubKey[] keys, bool isLegacy, bool lexicographicOrder,
ReadOnlyDictionary<string, string> additionalOptions) : base(additionalOptions)
{
Keys = keys;
Keys = new ReadOnlyCollection<BitcoinExtPubKey>(keys);
RequiredSignatures = reqSignature;
LexicographicOrder = true;
LexicographicOrder = lexicographicOrder;
IsLegacy = isLegacy;
}
public bool IsLegacy
{
get; private set;
get;
}
private void WriteBytes(MemoryStream ms, byte[] v)
public override Derivation GetDerivation(KeyPath keyPath)
{
ms.Write(v, 0, v.Length);
}
public override Derivation Derive(KeyPath keyPath)
{
var pubKeys = this.Keys.Select(s => s.ExtPubKey.Derive(keyPath).PubKey).ToArray();
var pubKeys = new PubKey[this.Keys.Count];
Parallel.For(0, pubKeys.Length, i =>
{
pubKeys[i] = this.Keys[i].ExtPubKey.Derive(keyPath).PubKey;
});
if(LexicographicOrder)
{
Array.Sort(pubKeys, LexicographicComparer);
}
var redeem = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(RequiredSignatures, pubKeys);
return new Derivation() { ScriptPubKey = redeem };
return new KeyPathDerivation(keyPath, redeem);
}
public override DerivationStrategyBase GetLineFor(KeyPath keyPath)
public override IEnumerable<ExtPubKey> GetExtPubKeys()
{
return new MultisigDerivationStrategy(RequiredSignatures, Keys.Select(k => k.ExtPubKey.Derive(keyPath).GetWif(k.Network)).ToArray(), IsLegacy)
{
LexicographicOrder = LexicographicOrder
};
return Keys.Select(k => k.ExtPubKey);
}
}
}

View File

@ -1,17 +1,13 @@
using NBXplorer.DerivationStrategy;
using System.Linq;
using System;
using System;
using System.Collections.Generic;
using System.Text;
using NBitcoin;
using NBitcoin.Crypto;
namespace NBXplorer.DerivationStrategy
{
public class P2SHDerivationStrategy : DerivationStrategyBase
public class P2SHDerivationStrategy : StandardDerivationStrategyBase
{
bool addSuffix;
internal P2SHDerivationStrategy(DerivationStrategyBase inner, bool addSuffix)
internal P2SHDerivationStrategy(StandardDerivationStrategyBase inner, bool addSuffix):base(inner.AdditionalOptions)
{
if(inner == null)
throw new ArgumentNullException(nameof(inner));
@ -19,34 +15,33 @@ namespace NBXplorer.DerivationStrategy
this.addSuffix = addSuffix;
}
public DerivationStrategyBase Inner
public StandardDerivationStrategyBase Inner
{
get; set;
}
protected override string StringValue
protected internal override string StringValueCore
{
get
{
if(addSuffix)
return Inner.ToString() + "-[p2sh]";
return Inner.StringValueCore + "-[p2sh]";
return Inner.ToString();
}
}
public override Derivation Derive(KeyPath keyPath)
public override IEnumerable<ExtPubKey> GetExtPubKeys()
{
var derivation = Inner.Derive(keyPath);
return new Derivation()
{
ScriptPubKey = derivation.ScriptPubKey.Hash.ScriptPubKey,
Redeem = derivation.Redeem ?? derivation.ScriptPubKey
};
return Inner.GetExtPubKeys();
}
public override DerivationStrategyBase GetLineFor(KeyPath keyPath)
public override Derivation GetDerivation(KeyPath keyPath)
{
return new P2SHDerivationStrategy(Inner.GetLineFor(keyPath), addSuffix);
var derivation = Inner.GetDerivation(keyPath);
return new KeyPathDerivation(
keyPath,
derivation.ScriptPubKey.Hash.ScriptPubKey,
derivation.Redeem ?? derivation.ScriptPubKey);
}
}
}

View File

@ -1,42 +1,34 @@
using NBXplorer.DerivationStrategy;
using System.Linq;
using System;
using System;
using System.Collections.Generic;
using System.Text;
using NBitcoin;
using NBitcoin.Crypto;
namespace NBXplorer.DerivationStrategy
{
public class P2WSHDerivationStrategy : DerivationStrategyBase
public class P2WSHDerivationStrategy : StandardDerivationStrategyBase
{
internal P2WSHDerivationStrategy(DerivationStrategyBase inner)
internal P2WSHDerivationStrategy(StandardDerivationStrategyBase inner):base(inner.AdditionalOptions)
{
if(inner == null)
throw new ArgumentNullException(nameof(inner));
Inner = inner;
}
public DerivationStrategyBase Inner
public StandardDerivationStrategyBase Inner
{
get; set;
}
protected override string StringValue => Inner.ToString();
protected internal override string StringValueCore => Inner.ToString();
public override Derivation Derive(KeyPath keyPath)
public override IEnumerable<ExtPubKey> GetExtPubKeys()
{
var derivation = Inner.Derive(keyPath);
return new Derivation()
{
ScriptPubKey = derivation.ScriptPubKey.WitHash.ScriptPubKey,
Redeem = derivation.ScriptPubKey
};
return Inner.GetExtPubKeys();
}
public override DerivationStrategyBase GetLineFor(KeyPath keyPath)
public override Derivation GetDerivation(KeyPath keyPath)
{
return new P2WSHDerivationStrategy(Inner.GetLineFor(keyPath));
var redeem = Inner.GetDerivation(keyPath).ScriptPubKey;
return new KeyPathDerivation(keyPath, redeem.WitHash.ScriptPubKey, redeem);
}
}
}

View File

@ -0,0 +1,212 @@
#nullable enable
#if !NO_RECORD
using NBitcoin;
using NBitcoin.WalletPolicies;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.RegularExpressions;
namespace NBXplorer.DerivationStrategy
{
public class PolicyDerivationStrategy : DerivationStrategyBase
{
internal static readonly Regex _MaybeMiniscript = new("^(wsh|sh|pkh|tr|wpkh)\\(");
public static bool TryParse(
string str,
Network network,
[MaybeNullWhen(false)] out PolicyDerivationStrategy strategy)
{
strategy = null;
if (!_MaybeMiniscript.IsMatch(str))
return false;
if (!WalletPolicy.TryParse(str, network, out var policy) || !IsValidPolicy(policy, out _))
return false;
strategy = new PolicyDerivationStrategy(policy, false);
return true;
}
public static PolicyDerivationStrategy Parse(string str, Network network)
{
if (!_MaybeMiniscript.IsMatch(str))
throw new FormatException("The policy should start by either wsh, sh, pkh, tr, wpkh");
var policy = WalletPolicy.Parse(str, network);
if (!IsValidPolicy(policy, out var err))
throw new FormatException(err);
return new PolicyDerivationStrategy(policy, false);
}
public PolicyDerivationStrategy(WalletPolicy policy) : this(policy, true)
{
}
PolicyDerivationStrategy(WalletPolicy policy, bool check) : base(null)
{
if (check && !IsValidPolicy(policy, out var error))
throw new ArgumentException(paramName: nameof(policy), message: error);
Policy = policy;
}
/// <summary>
/// Check that the policy should have at least one multi path node ([12345678]xpub/**) and no xpriv
/// </summary>
/// <param name="policy"></param>
/// <param name="error"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public static bool IsValidPolicy(WalletPolicy policy, [MaybeNullWhen(true)] out string error)
{
var v = new ValidPolicyVisitor();
policy.FullDescriptor.Visit(v);
error = v.Error;
return error is null;
}
class ValidPolicyVisitor : MiniscriptVisitor
{
public string? Error {
get
{
if (hasSecretKey)
return "The policy should not contain any xpriv key";
if (!hasMultiPathNode)
return "The policy should contain at least one multi path node ([12345678]xpub/**)";
return null;
}
}
private bool hasMultiPathNode;
private bool hasSecretKey;
public override void Visit(MiniscriptNode node)
{
if (node is MiniscriptNode.MultipathNode)
hasMultiPathNode = true;
else if (node is MiniscriptNode.HDKeyNode { Key: BitcoinExtKey })
hasSecretKey = true;
else
base.Visit(node);
}
}
public WalletPolicy Policy { get; }
private readonly DerivationCache cache = new();
private string? _str;
protected internal override string StringValueCore => _str ??= Policy.ToString(true);
public override IEnumerable<ExtPubKey> GetExtPubKeys()
=> Policy.KeyInformationVector.Select(kv => GetExtPubKey(kv.Key));
private ExtPubKey GetExtPubKey(IHDKey key)
=> key switch
{
ExtPubKey extPubKey => extPubKey,
BitcoinExtPubKey bitcoinExtPubKey => bitcoinExtPubKey.ExtPubKey,
ExtKey extKey => extKey.Neuter(),
BitcoinExtKey bitcoinExtKey => bitcoinExtKey.ExtKey.Neuter(),
_ => throw new NotSupportedException($"Unsupported key type: {key.GetType()}")
};
public NBXplorer.DerivationStrategy.Derivation GetDerivation(DerivationFeature feature, uint index)
=> GetDerivation(MiniscriptDerivationLine.ToAddressIntent(feature), index);
public NBXplorer.DerivationStrategy.Derivation GetDerivation(AddressIntent addressIntent, uint index)
{
var derived = Policy.FullDescriptor.Derive(new(addressIntent, [(int)index]) { DervivationCache = cache });
var scripts = derived[0].Miniscript.ToScripts();
return new PolicyDerivation(derived[0], scripts.ScriptPubKey, scripts.RedeemScript);
}
public override DerivationLine GetLineFor(KeyPathTemplates keyPathTemplates, DerivationFeature feature) => new MiniscriptDerivationLine(this, feature);
// Extract the multipath node from the hdkey
class MultipathNodeVisitor : MiniscriptVisitor
{
private readonly ExtPubKey _target;
public MiniscriptNode.MultipathNode? Result { get; set; }
public MultipathNodeVisitor(IHDKey target)
{
ArgumentNullException.ThrowIfNull(target);
_target = Normalize(target);
}
private static ExtPubKey Normalize(IHDKey target)
=> target switch
{
BitcoinExtKey extKey => extKey.Neuter().ExtPubKey,
ExtKey extKey => extKey.Neuter(),
BitcoinExtPubKey bitcoinExtPubKey => bitcoinExtPubKey.ExtPubKey,
ExtPubKey a => a,
_ => throw new NotSupportedException(target.GetType().ToString())
};
public override void Visit(MiniscriptNode node)
{
if (Result is not null)
return;
if (node is MiniscriptNode.MultipathNode { Target: MiniscriptNode.HDKeyNode hd } mp)
{
if (Normalize(hd.Key).Equals(_target))
Result = mp;
}
else
base.Visit(node);
}
}
class MiniscriptScriptPubKey : IHDScriptPubKey
{
private readonly PolicyDerivationStrategy _policyDerivationStrategy;
private readonly MiniscriptNode.MultipathNode _multipathNode;
private readonly KeyPath _keyPath;
public MiniscriptScriptPubKey(
PolicyDerivationStrategy policyDerivationStrategy,
MiniscriptNode.MultipathNode multipathNode,
KeyPath? keyPath = null,
DerivationCache? cache = null)
{
_policyDerivationStrategy = policyDerivationStrategy;
_multipathNode = multipathNode;
_keyPath = keyPath ?? KeyPath.Empty;
_cache = cache ?? new();
}
private readonly DerivationCache _cache;
public IHDScriptPubKey? Derive(KeyPath keyPath) =>
_keyPath.Derive(keyPath) is { Length: <= 2 } kp
&& (kp.Length == 0 || GetAddressIntent(kp.Indexes[0]) is not null)
&& !kp.IsHardenedPath
? new MiniscriptScriptPubKey(_policyDerivationStrategy, _multipathNode, kp, _cache) : null;
public Script ScriptPubKey
{
get
{
if (_keyPath is not { Indexes: [var intentIdx, var index], IsHardenedPath: false }
|| GetAddressIntent(intentIdx) is not {} intent)
throw new InvalidOperationException("Invalid keypath (it should be non hardened with two component)");
var derived = _policyDerivationStrategy.Policy.FullDescriptor.Derive(new(intent, new[] { (int)index })
{
DervivationCache = _cache
});
return derived[0].Miniscript.ToScripts().ScriptPubKey;
}
}
private AddressIntent? GetAddressIntent(uint intentIdx)
=> intentIdx == _multipathNode.DepositIndex ? AddressIntent.Deposit :
intentIdx == _multipathNode.ChangeIndex ? AddressIntent.Change : null;
}
public IHDScriptPubKey? GetHDScriptPubKey(IHDKey accountKey)
{
ArgumentNullException.ThrowIfNull(accountKey);
var visitor = new MultipathNodeVisitor(accountKey);
visitor.Visit(Policy.FullDescriptor.RootNode);
if (visitor.Result is null)
return null;
return new MiniscriptScriptPubKey(this, visitor.Result);
}
}
}
#endif

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using NBitcoin;
namespace NBXplorer.DerivationStrategy
{
public class TaprootDerivationStrategy : StandardDerivationStrategyBase
{
BitcoinExtPubKey _Root;
public ExtPubKey Root
{
get
{
return _Root;
}
}
protected internal override string StringValueCore
{
get
{
StringBuilder builder = new StringBuilder();
builder.Append(_Root.ToString());
builder.Append("-[taproot]");
return builder.ToString();
}
}
public TaprootDerivationStrategy(BitcoinExtPubKey root, ReadOnlyDictionary<string, string> additionalOptions = null) : base(additionalOptions)
{
if (root == null)
throw new ArgumentNullException(nameof(root));
_Root = root;
}
public override Derivation GetDerivation(KeyPath keyPath)
{
#if NO_SPAN
throw new NotSupportedException("Deriving taproot address is not supported on this platform.");
#else
var pubKey = _Root.ExtPubKey.Derive(keyPath).PubKey.GetTaprootFullPubKey();
return new KeyPathDerivation(keyPath, pubKey.ScriptPubKey);
#endif
}
public override IEnumerable<ExtPubKey> GetExtPubKeys()
{
yield return _Root.ExtPubKey;
}
}
}

View File

@ -1,7 +1,5 @@
using NBitcoin;
using System.Linq;
using NBitcoin.DataEncoders;
using NBitcoin.JsonConverters;
using NBXplorer.DerivationStrategy;
using NBXplorer.Models;
using System;
@ -14,12 +12,15 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.WebSockets;
using NBitcoin.RPC;
using System.Runtime.CompilerServices;
using System.Linq;
namespace NBXplorer
{
public class ExplorerClient
{
internal interface IAuth
public interface IAuth
{
bool RefreshCache();
void SetAuthorization(HttpRequestMessage message);
@ -36,11 +37,13 @@ namespace NBXplorer
_CookieFilePath = path;
}
public string CookieFilePath => _CookieFilePath;
public bool RefreshCache()
{
try
{
var cookieData = File.ReadAllText(_CookieFilePath);
var cookieData = File.ReadAllText(CookieFilePath);
_CachedAuth = new AuthenticationHeaderValue("Basic", Encoders.Base64.EncodeData(Encoders.ASCII.DecodeData(cookieData)));
return true;
}
@ -54,7 +57,7 @@ namespace NBXplorer
public void SetWebSocketAuth(ClientWebSocket socket)
{
if(_CachedAuth != null)
if (_CachedAuth != null)
socket.Options.SetRequestHeader("Authorization", $"{_CachedAuth.Scheme} {_CachedAuth.Parameter}");
}
}
@ -74,33 +77,57 @@ namespace NBXplorer
}
}
public ExplorerClient(NBXplorerNetwork network, Uri serverAddress = null)
public ExplorerClient(NBXplorerNetwork network, Uri serverAddress = null) : this(network, serverAddress, null)
{
}
public ExplorerClient(NBXplorerNetwork network, Uri serverAddress, IAuth customAuth)
{
serverAddress = serverAddress ?? network.DefaultSettings.DefaultUrl;
if(network == null)
if (network == null)
throw new ArgumentNullException(nameof(network));
_Address = serverAddress;
_Network = network;
_Serializer = new Serializer(network.NBitcoinNetwork);
Serializer = new Serializer(network);
_CryptoCode = _Network.CryptoCode;
_Factory = new DerivationStrategy.DerivationStrategyFactory(Network.NBitcoinNetwork);
SetCookieAuth(network.DefaultSettings.DefaultCookieFile);
_Factory = Network.DerivationStrategyFactory;
if (customAuth == null)
{
SetCookieAuth(network.DefaultSettings.DefaultCookieFile);
}
else
{
_Auth = customAuth;
customAuth.RefreshCache();
}
}
public RPCClient RPCClient { get; private set; }
internal IAuth _Auth = new NullAuthentication();
public bool SetCookieAuth(string path)
{
if(path == null)
if (path == null)
throw new ArgumentNullException(nameof(path));
CookieAuthentication auth = new CookieAuthentication(path);
_Auth = auth;
Auth = auth;
return auth.RefreshCache();
}
public void SetNoAuth()
{
_Auth = new NullAuthentication();
Auth = new NullAuthentication();
}
private void ConstructRPCClient()
{
RPCClient = new RPCClient(Auth is CookieAuthentication cookieAuthentication
? new RPCCredentialString()
{
CookieFile = cookieAuthentication.CookieFilePath
}
: new RPCCredentialString(), GetFullUri($"v1/cryptos/{_CryptoCode}/rpc"), _Network.NBitcoinNetwork);
}
private readonly string _CryptoCode = "BTC";
@ -112,203 +139,479 @@ namespace NBXplorer
}
}
Serializer _Serializer;
DerivationStrategy.DerivationStrategyFactory _Factory;
public UTXOChanges GetUTXOs(DerivationStrategyBase extKey, UTXOChanges previousChange, bool longPolling = true, CancellationToken cancellation = default(CancellationToken))
public UTXOChanges GetUTXOs(DerivationStrategyBase extKey, CancellationToken cancellation = default)
{
return GetUTXOsAsync(extKey, previousChange, longPolling, cancellation).GetAwaiter().GetResult();
return GetUTXOsAsync(extKey, cancellation).GetAwaiter().GetResult();
}
public Task<UTXOChanges> GetUTXOsAsync(DerivationStrategyBase extKey, CancellationToken cancellation = default)
{
if (extKey == null)
throw new ArgumentNullException(nameof(extKey));
return GetUTXOsAsync(TrackedSource.Create(extKey), cancellation);
}
public async Task<TransactionResult> GetTransactionAsync(uint256 txId, CancellationToken cancellation = default)
{
return await SendAsync<TransactionResult>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/transactions/{txId}", cancellation).ConfigureAwait(false);
}
public async Task<TransactionResult> GetTransactionAsync(uint256 txId, CancellationToken cancellation = default(CancellationToken))
{
return await SendAsync<TransactionResult>(HttpMethod.Get, null, "v1/cryptos/{0}/transactions/" + txId, new[] { CryptoCode }, cancellation).ConfigureAwait(false);
}
public TransactionResult GetTransaction(uint256 txId, CancellationToken cancellation = default(CancellationToken))
public TransactionResult GetTransaction(uint256 txId, CancellationToken cancellation = default)
{
return GetTransactionAsync(txId, cancellation).GetAwaiter().GetResult();
}
public Task<UTXOChanges> GetUTXOsAsync(DerivationStrategyBase extKey, UTXOChanges previousChange, bool longPolling = true, CancellationToken cancellation = default(CancellationToken))
public async Task<PruneResponse> PruneAsync(DerivationStrategyBase extKey, PruneRequest pruneRequest, CancellationToken cancellation = default)
{
return GetUTXOsAsync(extKey, previousChange?.Confirmed?.Bookmark, previousChange?.Unconfirmed?.Bookmark, longPolling, cancellation);
if (extKey == null)
throw new ArgumentNullException(nameof(extKey));
return await SendAsync<PruneResponse>(HttpMethod.Post, pruneRequest, $"v1/cryptos/{CryptoCode}/derivations/{extKey}/prune", cancellation).ConfigureAwait(false);
}
public UTXOChanges GetUTXOs(DerivationStrategyBase extKey, Bookmark confirmedBookmark, Bookmark unconfirmedBookmark, bool longPolling = true, CancellationToken cancellation = default(CancellationToken))
public PruneResponse Prune(DerivationStrategyBase extKey, PruneRequest pruneRequest, CancellationToken cancellation = default)
{
return GetUTXOsAsync(extKey, confirmedBookmark, unconfirmedBookmark, longPolling, cancellation).GetAwaiter().GetResult();
return PruneAsync(extKey, pruneRequest, cancellation).GetAwaiter().GetResult();
}
public NotificationSession CreateNotificationSession(CancellationToken cancellation = default(CancellationToken))
internal class RawStr
{
return CreateNotificationSessionAsync(cancellation).GetAwaiter().GetResult();
private string str;
public RawStr(string str)
{
this.str = str;
}
public override string ToString() => str;
};
internal static RawStr Raw(string str) => new RawStr(str);
public async Task ScanUTXOSetAsync(DerivationStrategyBase extKey, int? batchSize = null, int? gapLimit = null, int? fromIndex = null, CancellationToken cancellation = default)
{
if (extKey == null)
throw new ArgumentNullException(nameof(extKey));
List<string> args = new List<string>();
if (batchSize != null)
args.Add($"batchsize={batchSize.Value}");
if (gapLimit != null)
args.Add($"gaplimit={gapLimit.Value}");
if (fromIndex != null)
args.Add($"from={fromIndex.Value}");
var argsString = string.Join("&", args.ToArray());
if (argsString != string.Empty)
argsString = $"?{argsString}";
await SendAsync<bool>(HttpMethod.Post, null, $"v1/cryptos/{CryptoCode}/derivations/{extKey}/utxos/scan{Raw(argsString)}", cancellation).ConfigureAwait(false);
}
public void ScanUTXOSet(DerivationStrategyBase extKey, int? batchSize = null, int? gapLimit = null, int? fromIndex = null, CancellationToken cancellation = default)
{
ScanUTXOSetAsync(extKey, batchSize, gapLimit, fromIndex, cancellation).GetAwaiter().GetResult();
}
public async Task<NotificationSession> CreateNotificationSessionAsync(CancellationToken cancellation = default(CancellationToken))
public async Task<ScanUTXOInformation> GetScanUTXOSetInformationAsync(DerivationStrategyBase extKey, CancellationToken cancellation = default)
{
var session = new NotificationSession(this);
return await SendAsync<ScanUTXOInformation>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/derivations/{extKey}/utxos/scan", cancellation).ConfigureAwait(false);
}
public ScanUTXOInformation GetScanUTXOSetInformation(DerivationStrategyBase extKey, CancellationToken cancellation = default)
{
return GetScanUTXOSetInformationAsync(extKey, cancellation).GetAwaiter().GetResult();
}
public LongPollingNotificationSession CreateLongPollingNotificationSession(long lastEventId = 0)
{
return new LongPollingNotificationSession(lastEventId, this);
}
public WebsocketNotificationSession CreateWebsocketNotificationSession(CancellationToken cancellation = default)
{
return CreateWebsocketNotificationSessionAsync(cancellation).GetAwaiter().GetResult();
}
public async Task<WebsocketNotificationSession> CreateWebsocketNotificationSessionAsync(CancellationToken cancellation = default)
{
var session = new WebsocketNotificationSession(this);
await session.ConnectAsync(cancellation).ConfigureAwait(false);
return session;
}
public WebsocketNotificationSessionLegacy CreateWebsocketNotificationSessionLegacy(CancellationToken cancellation = default)
{
return CreateWebsocketNotificationSessionLegacyAsync(cancellation).GetAwaiter().GetResult();
}
public async Task<WebsocketNotificationSessionLegacy> CreateWebsocketNotificationSessionLegacyAsync(CancellationToken cancellation = default)
{
var session = new WebsocketNotificationSessionLegacy(this);
await session.ConnectAsync(cancellation).ConfigureAwait(false);
return session;
}
public Task<UTXOChanges> GetUTXOsAsync(DerivationStrategyBase extKey, Bookmark confirmedBookmark, Bookmark unconfirmedBookmark, bool longPolling = true, CancellationToken cancellation = default(CancellationToken))
public UTXOChanges GetUTXOs(TrackedSource trackedSource, CancellationToken cancellation = default)
{
return GetUTXOsAsync(extKey,
confirmedBookmark == null ? null as Bookmark[] : new Bookmark[] { confirmedBookmark },
unconfirmedBookmark == null ? null as Bookmark[] : new Bookmark[] { unconfirmedBookmark }, longPolling, cancellation);
return GetUTXOsAsync(trackedSource, cancellation).GetAwaiter().GetResult();
}
public Task<UTXOChanges> GetUTXOsAsync(TrackedSource trackedSource, CancellationToken cancellation = default)
{
if (trackedSource == null)
throw new ArgumentNullException(nameof(trackedSource));
return SendAsync<UTXOChanges>(HttpMethod.Get, null, $"{GetBasePath(trackedSource)}/utxos", cancellation);
}
public async Task<UTXOChanges> GetUTXOsAsync(DerivationStrategyBase extKey, Bookmark[] confirmedBookmarks, Bookmark[] unconfirmedBookmarks, bool longPolling = true, CancellationToken cancellation = default(CancellationToken))
{
Dictionary<string, string> parameters = new Dictionary<string, string>();
if(confirmedBookmarks != null)
parameters.Add("confirmedBookmarks", String.Join(",", confirmedBookmarks.Select(b => b.ToString())));
if(unconfirmedBookmarks != null)
parameters.Add("unconfirmedBookmarks", String.Join(",", unconfirmedBookmarks.Select(b => b.ToString())));
parameters.Add("longPolling", longPolling.ToString());
var query = String.Join("&", parameters.Select(p => p.Key + "=" + p.Value).ToArray());
return await SendAsync<UTXOChanges>(HttpMethod.Get, null, "v1/cryptos/{0}/derivations/{1}/utxos?" + query, new object[] { CryptoCode, extKey.ToString() }, cancellation).ConfigureAwait(false);
}
public void WaitServerStarted(CancellationToken cancellation = default(CancellationToken))
public void WaitServerStarted(CancellationToken cancellation = default)
{
WaitServerStartedAsync(cancellation).GetAwaiter().GetResult();
}
public async Task WaitServerStartedAsync(CancellationToken cancellation = default(CancellationToken))
public async Task WaitServerStartedAsync(CancellationToken cancellation = default)
{
while(true)
while (true)
{
try
{
var status = await GetStatusAsync(cancellation).ConfigureAwait(false);
if(status.IsFullySynched)
if (status.IsFullySynched)
break;
await Task.Delay(10);
}
catch(OperationCanceledException) { throw; }
catch (OperationCanceledException) { throw; }
catch { }
cancellation.ThrowIfCancellationRequested();
}
}
public void Track(DerivationStrategyBase strategy, CancellationToken cancellation = default(CancellationToken))
public void Track(DerivationStrategyBase strategy, CancellationToken cancellation = default)
{
TrackAsync(strategy, cancellation).GetAwaiter().GetResult();
}
public Task TrackAsync(DerivationStrategyBase strategy, CancellationToken cancellation = default(CancellationToken))
public Task TrackAsync(DerivationStrategyBase strategy, CancellationToken cancellation = default)
{
return SendAsync<string>(HttpMethod.Post, null, "v1/cryptos/{0}/derivations/{1}", new[] { CryptoCode, strategy.ToString() }, cancellation);
return TrackAsync(TrackedSource.Create(strategy), cancellation: cancellation);
}
public void CancelReservation(DerivationStrategyBase strategy, KeyPath[] keyPaths, CancellationToken cancellation = default(CancellationToken))
public void Track(DerivationStrategyBase strategy, TrackWalletRequest trackDerivationRequest, CancellationToken cancellation = default)
{
TrackAsync(strategy, trackDerivationRequest, cancellation).GetAwaiter().GetResult();
}
public async Task TrackAsync(DerivationStrategyBase strategy, TrackWalletRequest trackDerivationRequest, CancellationToken cancellation = default)
{
if (strategy == null)
throw new ArgumentNullException(nameof(strategy));
await SendAsync<string>(HttpMethod.Post, trackDerivationRequest, $"v1/cryptos/{CryptoCode}/derivations/{strategy}", cancellation).ConfigureAwait(false);
}
public void Track(TrackedSource trackedSource, CancellationToken cancellation = default)
{
TrackAsync(trackedSource, cancellation: cancellation).GetAwaiter().GetResult();
}
public Task TrackAsync(TrackedSource trackedSource, TrackWalletRequest trackDerivationRequest = null, CancellationToken cancellation = default)
{
if (trackedSource == null)
throw new ArgumentNullException(nameof(trackedSource));
return SendAsync<string>(HttpMethod.Post, trackDerivationRequest, GetBasePath(trackedSource), cancellation);
}
private Exception UnSupported(TrackedSource trackedSource)
{
return new NotSupportedException($"Unsupported {trackedSource.GetType().Name}");
}
public void CancelReservation(DerivationStrategyBase strategy, KeyPath[] keyPaths, CancellationToken cancellation = default)
{
CancelReservationAsync(strategy, keyPaths, cancellation).GetAwaiter().GetResult();
}
public Task CancelReservationAsync(DerivationStrategyBase strategy, KeyPath[] keyPaths, CancellationToken cancellation = default(CancellationToken))
public GetBalanceResponse GetBalance(DerivationStrategyBase userDerivationScheme, CancellationToken cancellation = default)
{
return SendAsync<string>(HttpMethod.Post, keyPaths, "v1/cryptos/{0}/derivations/{1}/addresses/cancelreservation", new[] { CryptoCode, strategy.ToString() }, cancellation);
return GetBalanceAsync(userDerivationScheme, cancellation).GetAwaiter().GetResult();
}
public Task<GetBalanceResponse> GetBalanceAsync(DerivationStrategyBase userDerivationScheme, CancellationToken cancellation = default)
{
return GetBalanceAsync(TrackedSource.Create(userDerivationScheme), cancellation);
}
public StatusResult GetStatus(CancellationToken cancellation = default(CancellationToken))
public GetBalanceResponse GetBalance(BitcoinAddress address, CancellationToken cancellation = default)
{
return GetBalanceAsync(address, cancellation).GetAwaiter().GetResult();
}
public Task<GetBalanceResponse> GetBalanceAsync(BitcoinAddress address, CancellationToken cancellation = default)
{
return GetBalanceAsync(TrackedSource.Create(address), cancellation);
}
public Task<GetBalanceResponse> GetBalanceAsync(TrackedSource trackedSource, CancellationToken cancellation = default)
{
return SendAsync<GetBalanceResponse>(HttpMethod.Get, null, $"{GetBasePath(trackedSource)}/balance", cancellation);
}
public async Task<bool> IsTrackedAsync(TrackedSource trackedSource, CancellationToken cancellation = default)
{
var responseMessage = await SendAsync(HttpMethod.Get, null, $"{GetBasePath(trackedSource)}", cancellation);
switch (responseMessage.StatusCode)
{
case HttpStatusCode.OK:
return true;
case HttpStatusCode.NotFound:
return false;
default:
await ParseResponse(responseMessage);
return false;
}
}
public Task CancelReservationAsync(DerivationStrategyBase strategy, KeyPath[] keyPaths, CancellationToken cancellation = default)
{
return SendAsync<string>(HttpMethod.Post, keyPaths, $"v1/cryptos/{CryptoCode}/derivations/{strategy}/addresses/cancelreservation", cancellation);
}
public StatusResult GetStatus(CancellationToken cancellation = default)
{
return GetStatusAsync(cancellation).GetAwaiter().GetResult();
}
public Task<StatusResult> GetStatusAsync(CancellationToken cancellation = default(CancellationToken))
public void Wipe(DerivationStrategyBase strategy, CancellationToken cancellation = default)
{
return SendAsync<StatusResult>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/status", null, cancellation);
WipeAsync(strategy, cancellation).GetAwaiter().GetResult();
}
public GetTransactionsResponse GetTransactions(DerivationStrategyBase strategy, GetTransactionsResponse previous, bool longPolling = true, CancellationToken cancellation = default(CancellationToken))
public Task WipeAsync(DerivationStrategyBase strategy, CancellationToken cancellation = default)
{
return GetTransactionsAsync(strategy, previous, longPolling, cancellation).GetAwaiter().GetResult();
}
public GetTransactionsResponse GetTransactions(DerivationStrategyBase strategy, Bookmark[] confirmedBookmarks, Bookmark[] unconfirmedBookmarks, Bookmark[] replacedBookmarks, bool longPolling = true, CancellationToken cancellation = default(CancellationToken))
{
return GetTransactionsAsync(strategy, confirmedBookmarks, unconfirmedBookmarks, replacedBookmarks, longPolling, cancellation).GetAwaiter().GetResult();
}
public Task<GetTransactionsResponse> GetTransactionsAsync(DerivationStrategyBase strategy, GetTransactionsResponse previous, bool longPolling, CancellationToken cancellation = default(CancellationToken))
{
return GetTransactionsAsync(strategy,
previous == null ? null : new[] { previous.ConfirmedTransactions.Bookmark },
previous == null ? null : new[] { previous.UnconfirmedTransactions.Bookmark },
previous == null ? null : new[] { previous.ReplacedTransactions.Bookmark }, longPolling, cancellation);
}
public Task<GetTransactionsResponse> GetTransactionsAsync(DerivationStrategyBase strategy, Bookmark[] confirmedBookmarks, Bookmark[] unconfirmedBookmarks, Bookmark[] replacedBookmarks, bool longPolling, CancellationToken cancellation = default(CancellationToken))
{
Dictionary<string, string> parameters = new Dictionary<string, string>();
if(confirmedBookmarks != null)
parameters.Add("confirmedBookmarks", String.Join(",", confirmedBookmarks.Select(b => b.ToString())));
if(unconfirmedBookmarks != null)
parameters.Add("unconfirmedBookmarks", String.Join(",", unconfirmedBookmarks.Select(b => b.ToString())));
if(replacedBookmarks != null)
parameters.Add("replacedBookmarks", String.Join(",", replacedBookmarks.Select(b => b.ToString())));
parameters.Add("longPolling", longPolling.ToString());
var query = String.Join("&", parameters.Select(p => p.Key + "=" + p.Value).ToArray());
return SendAsync<GetTransactionsResponse>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/derivations/{strategy}/transactions?" + query, null, cancellation);
if (strategy is null)
throw new ArgumentNullException(nameof(strategy));
return SendAsync<bool>(HttpMethod.Post, null, $"v1/cryptos/{CryptoCode}/derivations/{strategy}/utxos/wipe", cancellation);
}
public KeyPathInformation GetUnused(DerivationStrategyBase strategy, DerivationFeature feature, int skip = 0, bool reserve = false, CancellationToken cancellation = default(CancellationToken))
public Task<StatusResult> GetStatusAsync(CancellationToken cancellation = default)
{
return SendAsync<StatusResult>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/status", cancellation);
}
public GetTransactionsResponse GetTransactions(DerivationStrategyBase strategy, DateTimeOffset? from = null, DateTimeOffset? to = null, CancellationToken cancellation = default)
{
return GetTransactionsAsync(strategy, from, to, cancellation).GetAwaiter().GetResult();
}
public GetTransactionsResponse GetTransactions(TrackedSource trackedSource, DateTimeOffset? from = null, DateTimeOffset? to = null, CancellationToken cancellation = default)
{
return GetTransactionsAsync(trackedSource, from, to, cancellation).GetAwaiter().GetResult();
}
public Task<GetTransactionsResponse> GetTransactionsAsync(DerivationStrategyBase strategy, DateTimeOffset? from = null, DateTimeOffset? to = null, CancellationToken cancellation = default)
{
return GetTransactionsAsync(TrackedSource.Create(strategy), from, to, cancellation);
}
public Task<GetTransactionsResponse> GetTransactionsAsync(TrackedSource trackedSource, DateTimeOffset? from = null, DateTimeOffset? to = null, CancellationToken cancellation = default)
{
string fromV = string.Empty;
string toV = string.Empty;
if (from is DateTimeOffset f)
{
fromV = NBitcoin.Utils.DateTimeToUnixTime(f).ToString();
}
if (to is DateTimeOffset t)
{
toV = NBitcoin.Utils.DateTimeToUnixTime(t).ToString();
}
return SendAsync<GetTransactionsResponse>(HttpMethod.Get, null, $"{GetBasePath(trackedSource)}/transactions?from={fromV}&to={toV}", cancellation);
}
public TransactionInformation GetTransaction(TrackedSource trackedSource, uint256 txId, CancellationToken cancellation = default)
{
return this.GetTransactionAsync(trackedSource, txId, cancellation).GetAwaiter().GetResult();
}
public TransactionInformation GetTransaction(DerivationStrategyBase derivationStrategyBase, uint256 txId, CancellationToken cancellation = default)
{
return this.GetTransactionAsync(derivationStrategyBase, txId, cancellation).GetAwaiter().GetResult();
}
public Task<TransactionInformation> GetTransactionAsync(DerivationStrategyBase derivationStrategyBase, uint256 txId, CancellationToken cancellation = default)
{
if (derivationStrategyBase == null)
throw new ArgumentNullException(nameof(derivationStrategyBase));
return GetTransactionAsync(new DerivationSchemeTrackedSource(derivationStrategyBase), txId, cancellation);
}
public Task<TransactionInformation> GetTransactionAsync(TrackedSource trackedSource, uint256 txId, CancellationToken cancellation = default)
{
if (txId == null)
throw new ArgumentNullException(nameof(txId));
if (trackedSource == null)
throw new ArgumentNullException(nameof(trackedSource));
return SendAsync<TransactionInformation>(HttpMethod.Get, null, $"{GetBasePath(trackedSource)}/transactions/{txId}", cancellation);
}
public Task RescanAsync(RescanRequest rescanRequest, CancellationToken cancellation = default)
{
if (rescanRequest == null)
throw new ArgumentNullException(nameof(rescanRequest));
return SendAsync<byte[]>(HttpMethod.Post, rescanRequest, $"v1/cryptos/{CryptoCode}/rescan", cancellation);
}
public void Rescan(RescanRequest rescanRequest, CancellationToken cancellation = default)
{
RescanAsync(rescanRequest, cancellation).GetAwaiter().GetResult();
}
public KeyPathInformation GetUnused(DerivationStrategyBase strategy, DerivationFeature feature, int skip = 0, bool reserve = false, CancellationToken cancellation = default)
{
return GetUnusedAsync(strategy, feature, skip, reserve, cancellation).GetAwaiter().GetResult();
}
public async Task<KeyPathInformation> GetUnusedAsync(DerivationStrategyBase strategy, DerivationFeature feature, int skip = 0, bool reserve = false, CancellationToken cancellation = default(CancellationToken))
public async Task<KeyPathInformation> GetUnusedAsync(DerivationStrategyBase strategy, DerivationFeature feature, int skip = 0, bool reserve = false, CancellationToken cancellation = default)
{
try
{
return await GetAsync<KeyPathInformation>($"v1/cryptos/{CryptoCode}/derivations/{strategy}/addresses/unused?feature={feature}&skip={skip}&reserve={reserve}", null, cancellation).ConfigureAwait(false);
return await GetAsync<KeyPathInformation>($"v1/cryptos/{CryptoCode}/derivations/{strategy}/addresses/unused?feature={feature}&skip={skip}&reserve={reserve}", cancellation).ConfigureAwait(false);
}
catch(NBXplorerException ex) when(ex.Error?.HttpCode == 404)
catch (NBXplorerException ex) when (ex.Error?.HttpCode == 404)
{
return null;
}
}
public async Task<KeyPathInformation[]> GetKeyInformationsAsync(Script script, CancellationToken cancellation = default(CancellationToken))
public KeyPathInformation GetKeyInformation(DerivationStrategyBase strategy, Script script, CancellationToken cancellation = default)
{
return await SendAsync<KeyPathInformation[]>(HttpMethod.Get, null, "v1/cryptos/{0}/scripts/" + script.ToHex(), new[] { CryptoCode }, cancellation).ConfigureAwait(false);
return GetKeyInformationAsync(strategy, script, cancellation).GetAwaiter().GetResult();
}
public KeyPathInformation[] GetKeyInformations(Script script, CancellationToken cancellation = default(CancellationToken))
public async Task<KeyPathInformation> GetKeyInformationAsync(DerivationStrategyBase strategy, Script script, CancellationToken cancellation = default)
{
return await GetKeyInformationAsync(new DerivationSchemeTrackedSource(strategy), script, cancellation).ConfigureAwait(false);
}
public async Task<KeyPathInformation> GetKeyInformationAsync(TrackedSource trackedSource, Script script, CancellationToken cancellation = default)
{
return await SendAsync<KeyPathInformation>(HttpMethod.Get, null, $"{GetBasePath(trackedSource)}/scripts/{script.ToHex()}", cancellation).ConfigureAwait(false);
}
public async Task<KeyPathInformation[]> GetKeyInformationsAsync(Script script, CancellationToken cancellation = default)
{
return await SendAsync<KeyPathInformation[]>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/scripts/{script.ToHex()}", cancellation).ConfigureAwait(false);
}
public KeyPathInformation[] GetKeyInformations(Script script, CancellationToken cancellation = default)
{
return GetKeyInformationsAsync(script, cancellation).GetAwaiter().GetResult();
}
public GetFeeRateResult GetFeeRate(int blockCount, CancellationToken cancellation = default(CancellationToken))
public GetFeeRateResult GetFeeRate(int blockCount, CancellationToken cancellation = default)
{
return GetFeeRateAsync(blockCount, cancellation).GetAwaiter().GetResult();
}
public GetFeeRateResult GetFeeRate(int blockCount, FeeRate fallbackFeeRate, CancellationToken cancellation = default(CancellationToken))
public GetFeeRateResult GetFeeRate(int blockCount, FeeRate fallbackFeeRate, CancellationToken cancellation = default)
{
return GetFeeRateAsync(blockCount, fallbackFeeRate, cancellation).GetAwaiter().GetResult();
}
public async Task<GetFeeRateResult> GetFeeRateAsync(int blockCount, FeeRate fallbackFeeRate, CancellationToken cancellation = default(CancellationToken))
public async Task<GetFeeRateResult> GetFeeRateAsync(int blockCount, FeeRate fallbackFeeRate, CancellationToken cancellation = default)
{
try
{
return await GetAsync<GetFeeRateResult>("v1/cryptos/{0}/fees/{1}", new object[] { CryptoCode, blockCount }, cancellation).ConfigureAwait(false);
return await GetAsync<GetFeeRateResult>($"v1/cryptos/{CryptoCode}/fees/{blockCount}", cancellation).ConfigureAwait(false);
}
catch(NBXplorerException ex) when (fallbackFeeRate != null && ex.Error.Code == "fee-estimation-unavailable")
catch (NBXplorerException ex) when (fallbackFeeRate != null && ex.Error.Code == "fee-estimation-unavailable")
{
return new GetFeeRateResult() { BlockCount = blockCount, FeeRate = fallbackFeeRate };
}
}
public Task<GetFeeRateResult> GetFeeRateAsync(int blockCount, CancellationToken cancellation = default(CancellationToken))
public Task<GetFeeRateResult> GetFeeRateAsync(int blockCount, CancellationToken cancellation = default)
{
return GetAsync<GetFeeRateResult>("v1/cryptos/{0}/fees/{1}", new object[] { CryptoCode, blockCount }, cancellation);
return GetAsync<GetFeeRateResult>($"v1/cryptos/{CryptoCode}/fees/{blockCount}", cancellation);
}
public CreatePSBTResponse CreatePSBT(DerivationStrategyBase derivationStrategy, CreatePSBTRequest request, CancellationToken cancellation = default)
{
return CreatePSBTAsync(derivationStrategy, request, cancellation).GetAwaiter().GetResult();
}
public Task<CreatePSBTResponse> CreatePSBTAsync(DerivationStrategyBase derivationStrategy, CreatePSBTRequest request, CancellationToken cancellation = default)
{
if (derivationStrategy == null)
throw new ArgumentNullException(nameof(derivationStrategy));
if (request == null)
throw new ArgumentNullException(nameof(request));
return this.SendAsync<CreatePSBTResponse>(HttpMethod.Post, request, $"v1/cryptos/{CryptoCode}/derivations/{derivationStrategy}/psbt/create", cancellation);
}
public BroadcastResult Broadcast(Transaction tx, CancellationToken cancellation = default(CancellationToken))
public UpdatePSBTResponse UpdatePSBT(UpdatePSBTRequest request, CancellationToken cancellation = default)
{
return BroadcastAsync(tx, cancellation).GetAwaiter().GetResult();
return UpdatePSBTAsync(request, cancellation).GetAwaiter().GetResult();
}
public Task<UpdatePSBTResponse> UpdatePSBTAsync(UpdatePSBTRequest request, CancellationToken cancellation = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
return this.SendAsync<UpdatePSBTResponse>(HttpMethod.Post, request, $"v1/cryptos/{CryptoCode}/psbt/update", cancellation);
}
public BroadcastResult Broadcast(Transaction tx, CancellationToken cancellation = default)
{
return Broadcast(tx, false, cancellation);
}
public BroadcastResult Broadcast(Transaction tx, bool testMempoolAccept, CancellationToken cancellation = default)
{
return BroadcastAsync(tx, testMempoolAccept, cancellation).GetAwaiter().GetResult();
}
public Task<BroadcastResult> BroadcastAsync(Transaction tx, CancellationToken cancellation = default(CancellationToken))
public Task<BroadcastResult> BroadcastAsync(Transaction tx, CancellationToken cancellation = default)
{
return SendAsync<BroadcastResult>(HttpMethod.Post, tx.ToBytes(), "v1/cryptos/{0}/transactions", new[] { CryptoCode }, cancellation);
return BroadcastAsync(tx, false, cancellation);
}
public Task<BroadcastResult> BroadcastAsync(Transaction tx, bool testMempoolAccept, CancellationToken cancellation = default)
{
return SendAsync<BroadcastResult>(HttpMethod.Post, tx.ToBytes(), $"v1/cryptos/{CryptoCode}/transactions?testMempoolAccept={testMempoolAccept}", cancellation);
}
public TMetadata GetMetadata<TMetadata>(DerivationStrategyBase derivationScheme, string key, CancellationToken cancellationToken = default)
{
return GetMetadataAsync<TMetadata>(derivationScheme, key, cancellationToken).GetAwaiter().GetResult();
}
public Task<TMetadata> GetMetadataAsync<TMetadata>(DerivationStrategyBase derivationScheme, string key, CancellationToken cancellationToken = default)
{
if (derivationScheme == null)
throw new ArgumentNullException(nameof(derivationScheme));
if (key == null)
throw new ArgumentNullException(nameof(key));
return GetAsync<TMetadata>($"v1/cryptos/{CryptoCode}/derivations/{derivationScheme}/metadata/{key}", cancellationToken);
}
public void SetMetadata<TMetadata>(DerivationStrategyBase derivationScheme, string key, TMetadata value, CancellationToken cancellationToken = default)
{
SetMetadataAsync<TMetadata>(derivationScheme, key, value, cancellationToken).GetAwaiter().GetResult();
}
public Task SetMetadataAsync<TMetadata>(DerivationStrategyBase derivationScheme, string key, TMetadata value, CancellationToken cancellationToken = default)
{
return SendAsync<string>(HttpMethod.Post, value, $"v1/cryptos/{CryptoCode}/derivations/{derivationScheme}/metadata/{key}", cancellationToken);
}
public Task<GenerateWalletResponse> GenerateWalletAsync(GenerateWalletRequest request = null, CancellationToken cancellationToken = default)
{
request ??= new GenerateWalletRequest();
return SendAsync<GenerateWalletResponse>(HttpMethod.Post, request, $"v1/cryptos/{CryptoCode}/derivations", cancellationToken);
}
public GenerateWalletResponse GenerateWallet(GenerateWalletRequest request = null, CancellationToken cancellationToken = default)
{
request ??= new GenerateWalletRequest();
return GenerateWalletAsync(request, cancellationToken).GetAwaiter().GetResult();
}
public Task<GroupInformation> CreateGroupAsync(CancellationToken cancellationToken = default)
{
return SendAsync<GroupInformation>(HttpMethod.Post, null, $"v1/groups", cancellationToken);
}
public Task<GroupInformation> GetGroupAsync(string groupId, CancellationToken cancellationToken = default)
{
return SendAsync<GroupInformation>(HttpMethod.Get, null, $"v1/groups/{groupId}", cancellationToken);
}
public Task<GroupInformation> AddGroupChildrenAsync(string groupId, GroupChild[] children, CancellationToken cancellationToken = default)
{
return SendAsync<GroupInformation>(HttpMethod.Post, children, $"v1/groups/{groupId}/children", cancellationToken);
}
public Task<GroupInformation> RemoveGroupChildrenAsync(string groupId, GroupChild[] children, CancellationToken cancellationToken = default)
{
return SendAsync<GroupInformation>(HttpMethod.Delete, children, $"v1/groups/{groupId}/children", cancellationToken);
}
public Task AddGroupAddressAsync(string cryptoCode, string groupId, string[] addresses, CancellationToken cancellationToken = default)
{
return SendAsync<GroupInformation>(HttpMethod.Post, addresses, $"v1/cryptos/{cryptoCode}/groups/{groupId}/addresses", cancellationToken);
}
public async Task ImportUTXOs(string cryptoCode, ImportUTXORequest request, CancellationToken cancellation = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (cryptoCode == null)
throw new ArgumentNullException(nameof(cryptoCode));
await SendAsync(HttpMethod.Post, request, $"v1/cryptos/{cryptoCode}/rescan-utxos", cancellation);
}
private static readonly HttpClient SharedClient = new HttpClient();
@ -342,80 +645,126 @@ namespace NBXplorer
{
get; set;
} = true;
public Serializer Serializer { get; private set; }
internal string GetFullUri(string relativePath, params object[] parameters)
internal IAuth Auth
{
relativePath = String.Format(relativePath, parameters ?? new object[0]);
var uri = Address.AbsoluteUri;
if(!uri.EndsWith("/", StringComparison.Ordinal))
uri += "/";
uri += relativePath;
if(!IncludeTransaction)
get => _Auth;
set
{
if(uri.IndexOf('?') == -1)
_Auth = value;
ConstructRPCClient();
}
}
static FormattableString EncodeUrlParameters(FormattableString url)
{
return FormattableStringFactory.Create(
url.Format,
url.GetArguments()
.Select(a =>
a is RawStr ? a :
a is FormattableString o ? EncodeUrlParameters(o) :
Uri.EscapeDataString(a?.ToString() ?? ""))
.ToArray());
}
internal string GetFullUri(FormattableString relativePath)
{
var uri = Address.AbsoluteUri;
if (!uri.EndsWith("/", StringComparison.Ordinal))
uri += "/";
uri += EncodeUrlParameters(relativePath).ToString();
if (!IncludeTransaction)
{
if (uri.IndexOf('?') == -1)
uri += $"?includeTransaction=false";
else
uri += $"&includeTransaction=false";
}
return uri;
}
private Task<T> GetAsync<T>(string relativePath, object[] parameters, CancellationToken cancellation)
private Task<T> GetAsync<T>(FormattableString relativePath, CancellationToken cancellation)
{
return SendAsync<T>(HttpMethod.Get, null, relativePath, parameters, cancellation);
return SendAsync<T>(HttpMethod.Get, null, relativePath, cancellation);
}
private async Task<T> SendAsync<T>(HttpMethod method, object body, string relativePath, object[] parameters, CancellationToken cancellation)
internal async Task<T> SendAsync<T>(HttpMethod method, object body, FormattableString relativePath, CancellationToken cancellation)
{
HttpRequestMessage message = CreateMessage(method, body, relativePath, parameters);
HttpRequestMessage message = CreateMessage(method, body, relativePath);
var result = await Client.SendAsync(message, cancellation).ConfigureAwait(false);
if((int)result.StatusCode == 404)
if ((int)result.StatusCode == 404)
{
return default(T);
}
if((int)result.StatusCode == 401)
if (result.StatusCode == HttpStatusCode.GatewayTimeout || result.StatusCode == HttpStatusCode.RequestTimeout)
{
if(_Auth.RefreshCache())
throw new HttpRequestException($"HTTP error {(int)result.StatusCode}", new TimeoutException());
}
if ((int)result.StatusCode == 401)
{
if (Auth.RefreshCache())
{
message = CreateMessage(method, body, relativePath, parameters);
result = await Client.SendAsync(message).ConfigureAwait(false);
message = CreateMessage(method, body, relativePath);
result = await Client.SendAsync(message, cancellation).ConfigureAwait(false);
}
}
return await ParseResponse<T>(result).ConfigureAwait(false);
}
internal HttpRequestMessage CreateMessage(HttpMethod method, object body, string relativePath, object[] parameters)
internal async Task<HttpResponseMessage> SendAsync(HttpMethod method, object body, FormattableString relativePath, CancellationToken cancellation)
{
var uri = GetFullUri(relativePath, parameters);
var message = new HttpRequestMessage(method, uri);
_Auth.SetAuthorization(message);
if(body != null)
{
if(body is byte[])
message.Content = new ByteArrayContent((byte[])body);
else
message.Content = new StringContent(_Serializer.ToString(body), Encoding.UTF8, "application/json");
}
var message = CreateMessage(method, body, relativePath);
var result = await Client.SendAsync(message, cancellation).ConfigureAwait(false);
if (result.StatusCode == HttpStatusCode.GatewayTimeout || result.StatusCode == HttpStatusCode.RequestTimeout)
{
throw new HttpRequestException($"HTTP error {(int)result.StatusCode}", new TimeoutException());
}
if ((int)result.StatusCode == 401)
{
if (Auth.RefreshCache())
{
message = CreateMessage(method, body, relativePath);
result = await Client.SendAsync(message, cancellation).ConfigureAwait(false);
}
}
return result;
}
internal HttpRequestMessage CreateMessage(HttpMethod method, object body, FormattableString relativePath)
{
var uri = GetFullUri(relativePath);
var message = new HttpRequestMessage(method, uri);
Auth.SetAuthorization(message);
if (body != null)
{
if (body is byte[])
{
var content = new ByteArrayContent((byte[])body);
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
message.Content = content;
}
else
message.Content = new StringContent(Serializer.ToString(body), Encoding.UTF8, "application/json");
}
return message;
}
private async Task<T> ParseResponse<T>(HttpResponseMessage response)
{
using(response)
using (response)
{
if(response.IsSuccessStatusCode)
if(response.Content.Headers.ContentLength == 0)
if (response.IsSuccessStatusCode)
if (response.Content.Headers.ContentLength == 0)
return default(T);
else if(response.Content.Headers.ContentType.MediaType.Equals("application/json", StringComparison.Ordinal))
return _Serializer.ToObject<T>(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
else if(response.Content.Headers.ContentType.MediaType.Equals("application/octet-stream", StringComparison.Ordinal))
else if (response.Content.Headers.ContentType.MediaType.Equals("application/json", StringComparison.Ordinal))
return Serializer.ToObject<T>(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
else if (response.Content.Headers.ContentType.MediaType.Equals("application/octet-stream", StringComparison.Ordinal))
{
return (T)(object)await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
}
if(response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
response.EnsureSuccessStatusCode();
var error = _Serializer.ToObject<NBXplorerError>(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
if(error == null)
var error = Serializer.ToObject<NBXplorerError>(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
if (error == null)
response.EnsureSuccessStatusCode();
throw error.AsException();
}
@ -423,17 +772,30 @@ namespace NBXplorer
private async Task ParseResponse(HttpResponseMessage response)
{
using(response)
using (response)
{
if(response.IsSuccessStatusCode)
if (response.IsSuccessStatusCode)
return;
if(response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
response.EnsureSuccessStatusCode();
var error = _Serializer.ToObject<NBXplorerError>(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
if(error == null)
var error = Serializer.ToObject<NBXplorerError>(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
if (error == null)
response.EnsureSuccessStatusCode();
throw error.AsException();
}
}
private FormattableString GetBasePath(TrackedSource trackedSource)
{
if (trackedSource is null)
throw new ArgumentNullException(nameof(trackedSource));
return trackedSource switch
{
DerivationSchemeTrackedSource dsts => $"v1/cryptos/{CryptoCode}/derivations/{dsts.DerivationStrategy}",
AddressTrackedSource asts => $"v1/cryptos/{CryptoCode}/addresses/{asts.Address}",
GroupTrackedSource wts => $"v1/cryptos/{CryptoCode}/groups/{wts.GroupId}",
_ => $"v1/cryptos/{CryptoCode}/tracked-sources/{trackedSource}"
};
}
}
}

View File

@ -1,10 +1,8 @@
using NBitcoin;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NBitcoin.RPC;
using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -12,84 +10,34 @@ namespace NBXplorer
{
public static class ExtensionsClient
{
static ExtensionsClient()
public static IEnumerable<IList<T>> Batch<T>(this IEnumerable<T> values, int size)
{
_TypeByName = new Dictionary<string, Type>();
_NameByType = new Dictionary<Type, string>();
Add("newblock", typeof(Models.NewBlockEvent));
Add("subscribeblock", typeof(Models.NewBlockEventRequest));
Add("subscribetransaction", typeof(Models.NewTransactionEventRequest));
Add("newtransaction", typeof(Models.NewTransactionEvent));
}
static Dictionary<string, Type> _TypeByName;
static Dictionary<Type, string> _NameByType;
private static void Add(string typeName, Type type)
{
_TypeByName.Add(typeName, type);
_NameByType.Add(type, typeName);
}
public static IEnumerable<T[]> Batch<T>(this IEnumerable<T> values, int size)
{
var batch = new T[size];
int index = 0;
if (size <= 0)
throw new ArgumentOutOfRangeException(nameof(size));
if (values == null)
throw new ArgumentNullException(nameof(values));
if (values is IList<T> l && l.Count <= size)
{
yield return l;
yield break;
}
var batch = new List<T>();
foreach(var v in values)
{
batch[index++] = v;
if(index == batch.Length)
batch.Add(v);
if(size == batch.Count)
{
yield return batch;
batch = new T[size];
index = 0;
batch = new List<T>();
}
}
if(index != 0)
if(batch.Count != 0)
{
Array.Resize(ref batch, index);
yield return batch;
}
}
public static ArraySegment<T> Slice<T>(this ArraySegment<T> array, int index)
{
if((uint)index > (uint)array.Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return new ArraySegment<T>(array.Array, array.Offset + index, array.Count - index);
}
public static ArraySegment<T> Slice<T>(this ArraySegment<T> array, int index, int count)
{
if((uint)index > (uint)array.Count || (uint)count > (uint)(array.Count - index))
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return new ArraySegment<T>(array.Array, array.Offset + index, count);
}
public static object ParseNotificationMessage(string str, JsonSerializerSettings settings)
{
if(str == null)
throw new ArgumentNullException(nameof(str));
JObject jobj = JObject.Parse(str);
var type = (jobj["type"] as JValue)?.Value<string>();
if(type == null)
throw new FormatException("'type' property not found");
if(!_TypeByName.TryGetValue(type, out Type typeObject))
throw new FormatException("unknown 'type'");
var data = (jobj["data"] as JObject);
if(data == null)
throw new FormatException("'data' property not found");
return JsonConvert.DeserializeObject(data.ToString(), typeObject, settings);
}
public static async Task CloseSocket(this WebSocket socket, WebSocketCloseStatus status, string statusDescription, CancellationToken cancellation = default(CancellationToken))
public static async Task CloseSocket(this WebSocket socket, WebSocketCloseStatus status, string statusDescription, CancellationToken cancellation = default)
{
try
{
@ -113,10 +61,18 @@ namespace NBXplorer
finally { socket.Dispose(); }
}
public static string GetNotificationMessageTypeName(Type type)
public static async Task<uint256[]> EnsureGenerateAsync(this RPCClient client, int blockCount)
{
_NameByType.TryGetValue(type, out string name);
return name;
uint256[] blockIds = new uint256[blockCount];
int generated = 0;
while(generated < blockCount)
{
foreach(var id in await client.GenerateAsync(blockCount - generated).ConfigureAwait(false))
{
blockIds[generated++] = id;
}
}
return blockIds;
}
}
}

View File

@ -1,36 +0,0 @@
using NBitcoin;
using NBXplorer.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace NBXplorer.JsonConverters
{
public class BookmarkJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return
typeof(Bookmark).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if(reader.TokenType == JsonToken.Null)
return null;
return new Bookmark(new uint160(reader.Value.ToString()));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var b = value as Bookmark;
if(b != null)
{
writer.WriteValue(b.ToString());
}
}
}
}

View File

@ -0,0 +1,88 @@
using System.Linq;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace NBXplorer.JsonConverters
{
/// <summary>
/// Cache serialization of costly base58 structures
/// </summary>
public class CachedSerializer : JsonConverter
{
class CachedConverter
{
public CachedConverter(JsonConverter converter)
{
Converter = converter;
}
public JsonConverter Converter { get; }
ConcurrentDictionary<string, object> cachedStrings = new ConcurrentDictionary<string, object>();
int total = 0;
public object ReadJson(string str, JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (cachedStrings.TryGetValue(str, out var v))
return v;
v = Converter.ReadJson(reader, objectType, existingValue, serializer);
if (cachedStrings.TryAdd(str, v))
{
Interlocked.Increment(ref total);
if (total > 20)
{
cachedStrings.Clear();
total = 0;
}
}
return v;
}
public void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Converter.WriteJson(writer, value, serializer);
}
internal bool CanConvert(Type objectType)
{
return Converter.CanConvert(objectType);
}
}
public CachedSerializer(NBXplorerNetwork network)
{
if (network == null)
throw new ArgumentNullException(nameof(network));
cachedConverter.Add(new CachedConverter(new DerivationStrategyJsonConverter(network.DerivationStrategyFactory)));
cachedConverter.Add(new CachedConverter(new TrackedSourceJsonConverter(network)));
}
public override bool CanConvert(Type objectType)
{
return GetConverter(objectType) != null;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var str = reader.Value.ToString();
return GetConverter(objectType).ReadJson(str, reader, objectType, existingValue, serializer);
}
private CachedConverter GetConverter(Type objectType)
{
return cachedConverter.FirstOrDefault(s => s.CanConvert(objectType));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
return;
var converter = GetConverter(value.GetType());
converter.WriteJson(writer, value, serializer);
}
List<CachedConverter> cachedConverter = new List<CachedConverter>();
}
}

View File

@ -1,10 +1,7 @@
using NBitcoin;
using System.Reflection;
using System.Reflection;
using NBXplorer.DerivationStrategy;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using NBitcoin.JsonConverters;
namespace NBXplorer.JsonConverters

View File

@ -1,40 +0,0 @@
using NBitcoin;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace NBXplorer.JsonConverters
{
public class FeeRateJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return
typeof(FeeRate).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if(reader.TokenType == JsonToken.Null)
return null;
if(reader.TokenType != JsonToken.Integer)
return null;
var value = (long)reader.Value;
return new FeeRate(Money.Satoshis(value), 1);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var feeRate = value as FeeRate;
if(feeRate != null)
{
writer.WriteValue(feeRate.GetFee(1).Satoshi);
}
}
}
}

View File

@ -0,0 +1,25 @@
using Newtonsoft.Json;
using System;
namespace NBXplorer.JsonConverters
{
public class KeyPathTemplateJsonConverter : JsonConverter<KeyPathTemplate>
{
public override KeyPathTemplate ReadJson(JsonReader reader, Type objectType, KeyPathTemplate existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
if (reader.TokenType != JsonToken.String)
throw new NBitcoin.JsonConverters.JsonObjectException($"Unexpected json token type, expected String, actual {reader.TokenType}", reader);
if (!KeyPathTemplate.TryParse((string)reader.Value, out var template))
throw new NBitcoin.JsonConverters.JsonObjectException($"Invalid KeyPathTemplate", reader);
return template;
}
public override void WriteJson(JsonWriter writer, KeyPathTemplate value, JsonSerializer serializer)
{
if (value is KeyPathTemplate kt)
writer.WriteValue(value.ToString());
}
}
}

View File

@ -0,0 +1,76 @@
using Newtonsoft.Json;
using System.Linq;
using System.Reflection;
using System;
using NBitcoin;
using NBitcoin.JsonConverters;
namespace NBXplorer.JsonConverters
{
public class MoneyJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(IMoney).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
class AssetCoinJson
{
public uint256 AssetId { get; set; }
public long? Value { get; set; }
public AssetMoney ToAssetMoney(string path)
{
if (AssetId == null)
throw new JsonObjectException("'assetId' is missing", path);
if (Value is null)
throw new JsonObjectException("'value' is missing", path);
return new AssetMoney(AssetId, Value.Value);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
try
{
if (reader.TokenType == JsonToken.Null)
return null;
AssertJsonType(reader, new[] { JsonToken.Integer, JsonToken.StartObject, JsonToken.StartArray });
if (reader.TokenType == JsonToken.Integer)
{
return new Money((long)reader.Value);
}
else if (reader.TokenType == JsonToken.StartObject)
{
return serializer.Deserialize<AssetCoinJson>(reader).ToAssetMoney(reader.Path);
}
else
{
return new MoneyBag(serializer.Deserialize<AssetCoinJson[]>(reader).Select(c => c.ToAssetMoney(reader.Path)).ToArray());
}
}
catch (InvalidCastException)
{
throw new JsonObjectException("Money amount should be in satoshi", reader);
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is Money v)
writer.WriteValue(v.Satoshi);
else if (value is AssetMoney av)
{
serializer.Serialize(writer, new AssetCoinJson() { Value = av.Quantity, AssetId = av.AssetId });
}
else if (value is MoneyBag mb)
{
serializer.Serialize(writer, mb.OfType<AssetMoney>().Select(av2 => new AssetCoinJson() { Value = av2.Quantity, AssetId = av2.AssetId }).ToArray());
}
}
static void AssertJsonType(JsonReader reader, JsonToken[] anyExpectedTypes)
{
if (!anyExpectedTypes.Contains(reader.TokenType))
throw new JsonObjectException($"Unexpected json token type, expected are {string.Join(", ", anyExpectedTypes)} and actual is {reader.TokenType}", reader);
}
}
}

View File

@ -0,0 +1,54 @@
using NBitcoin;
using NBitcoin.DataEncoders;
using NBitcoin.JsonConverters;
using NBXplorer.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace NBXplorer.JsonConverters
{
public class PSBTDestinationJsonConverter : JsonConverter
{
public PSBTDestinationJsonConverter(Network network)
{
Network = network;
}
public Network Network { get; }
public override bool CanConvert(Type objectType)
{
return typeof(PSBTDestination).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
if (reader.TokenType != JsonToken.String)
{
throw new JsonObjectException($"Unexpected json token type, expected is {JsonToken.String} and actual is {reader.TokenType}", reader);
}
var str = reader.Value.ToString();
try
{
return PSBTDestination.Parse(str, Network);
}
catch (FormatException ex)
{
throw new JsonObjectException(ex.Message, reader);
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is not null)
{
writer.WriteValue(value.ToString());
}
}
}
}

View File

@ -0,0 +1,50 @@
using NBitcoin;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
namespace NBXplorer.JsonConverters
{
public class ScriptPubKeyTypeConverter : JsonConverter
{
static ScriptPubKeyTypeConverter()
{
_ScriptPubKeyType = new Dictionary<string, ScriptPubKeyType>()
{
{ "Legacy", ScriptPubKeyType.Legacy },
{ "Segwit", ScriptPubKeyType.Segwit },
{ "SegwitP2SH", ScriptPubKeyType.SegwitP2SH },
#pragma warning disable CS0618 // Type or member is obsolete
{ "Taproot", ScriptPubKeyType.TaprootBIP86 }
#pragma warning restore CS0618 // Type or member is obsolete
};
_ScriptPubKeyTypeReverse = _ScriptPubKeyType.ToDictionary(kv => kv.Value, kv => kv.Key);
}
public override bool CanConvert(Type objectType)
{
return typeof(NBitcoin.WordCount).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
if (reader.TokenType != JsonToken.String)
throw new NBitcoin.JsonConverters.JsonObjectException($"Unexpected json token type, expected String, actual {reader.TokenType}", reader);
if (!_ScriptPubKeyType.TryGetValue((string)reader.Value, out var result))
throw new NBitcoin.JsonConverters.JsonObjectException($"Invalid ScriptPubKeyType, possible values {string.Join(", ", _ScriptPubKeyType.Keys.ToArray())} (defaut: 12)", reader);
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is ScriptPubKeyType t)
writer.WriteValue(_ScriptPubKeyTypeReverse[t]);
}
readonly static Dictionary<string, ScriptPubKeyType> _ScriptPubKeyType;
readonly static Dictionary<ScriptPubKeyType, string> _ScriptPubKeyTypeReverse;
}
}

View File

@ -0,0 +1,46 @@
using Newtonsoft.Json;
using System;
using System.Reflection;
using NBXplorer.Models;
using NBitcoin.JsonConverters;
namespace NBXplorer.JsonConverters
{
public class TrackedSourceJsonConverter : JsonConverter
{
public TrackedSourceJsonConverter(NBXplorerNetwork network)
{
Network = network;
}
public NBXplorerNetwork Network { get; }
public override bool CanConvert(Type objectType)
{
return
typeof(TrackedSource).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
if (reader.TokenType != JsonToken.String)
return null;
if (TrackedSource.TryParse(reader.Value.ToString(), out var v, Network))
return v;
throw new JsonObjectException("Invalid TrackedSource", reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var trackedSource = value as TrackedSource;
if (trackedSource != null)
{
writer.WriteValue(trackedSource.ToString());
}
}
}
}

View File

@ -0,0 +1,57 @@
using NBitcoin;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
namespace NBXplorer.JsonConverters
{
public class WordcountJsonConverter : JsonConverter
{
static WordcountJsonConverter()
{
_Wordcount = new Dictionary<long, WordCount>()
{
{ 18, WordCount.Eighteen },
{ 15, WordCount.Fifteen },
{ 12, WordCount.Twelve },
{ 24, WordCount.TwentyFour },
{ 21, WordCount.TwentyOne }
};
_WordcountReverse = _Wordcount.ToDictionary(kv => kv.Value, kv => kv.Key);
}
public override bool CanConvert(Type objectType)
{
return typeof(NBitcoin.WordCount).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo()) ||
typeof(NBitcoin.WordCount?).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return default;
if (reader.TokenType != JsonToken.Integer)
throw new NBitcoin.JsonConverters.JsonObjectException($"Unexpected json token type, expected Integer, actual {reader.TokenType}", reader);
if (!_Wordcount.TryGetValue((long)reader.Value, out var result))
throw new NBitcoin.JsonConverters.JsonObjectException($"Invalid WordCount, possible values {string.Join(", ", _Wordcount.Keys.ToArray())} (defaut: 12)", reader);
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is WordCount wc)
writer.WriteValue(_WordcountReverse[wc]);
}
readonly static Dictionary<long, WordCount> _Wordcount = new Dictionary<long, WordCount>()
{
{ 18, WordCount.Eighteen },
{ 15, WordCount.Fifteen },
{ 12, WordCount.Twelve },
{ 24, WordCount.TwentyFour },
{ 21, WordCount.TwentyOne }
};
readonly static Dictionary<WordCount, long> _WordcountReverse;
}
}

View File

@ -0,0 +1,49 @@
using NBitcoin;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
namespace NBXplorer.JsonConverters
{
public class WordlistJsonConverter : JsonConverter
{
static WordlistJsonConverter()
{
_Wordlists = new Dictionary<string, Wordlist>(StringComparer.OrdinalIgnoreCase)
{
{ "English", Wordlist.English },
{ "French", Wordlist.French },
{ "Japanese", Wordlist.Japanese },
{ "Spanish", Wordlist.Spanish },
{ "ChineseSimplified", Wordlist.ChineseSimplified }
};
_WordlistsReverse = _Wordlists.ToDictionary(kv => kv.Value, kv => kv.Key);
}
public override bool CanConvert(Type objectType)
{
return typeof(Wordlist).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
if (reader.TokenType != JsonToken.String)
throw new NBitcoin.JsonConverters.JsonObjectException($"Unexpected json token type, expected String, actual {reader.TokenType}", reader);
if (!_Wordlists.TryGetValue((string)reader.Value, out var result))
throw new NBitcoin.JsonConverters.JsonObjectException($"Invalid wordlist, possible values {string.Join(", ", _Wordlists.Keys.ToArray())} (defaut: English)", reader);
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is Wordlist wl)
writer.WriteValue(_WordlistsReverse[wl]);
}
readonly static Dictionary<string, Wordlist> _Wordlists;
readonly static Dictionary<Wordlist, string> _WordlistsReverse;
}
}

View File

@ -0,0 +1,105 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Http;
using Newtonsoft.Json.Linq;
using NBXplorer.Models;
namespace NBXplorer
{
public class LongPollingNotificationSession : NotificationSessionBase
{
public LongPollingNotificationSession(long lastEventId, ExplorerClient client)
{
LastEventId = lastEventId;
Client = client;
}
public long LastEventId { get; private set; }
public ExplorerClient Client { get; }
Queue<NewEventBase> _EventsToProcess = new Queue<NewEventBase>();
public override async Task<NewEventBase> NextEventAsync(CancellationToken cancellation = default)
{
retry:
long evtId = 0;
lock (_EventsToProcess)
{
evtId = LastEventId;
if (_EventsToProcess.Count > 0)
{
var evt = _EventsToProcess.Dequeue();
LastEventId = evt.EventId;
return evt;
}
}
NewEventBase[] evts = null;
try
{
evts = await GetEventsAsync(evtId, 30, true, cancellation);
}
catch(HttpRequestException ex) when (ex.InnerException is TimeoutException)
{
goto retry;
}
catch(OperationCanceledException) when (!cancellation.IsCancellationRequested)
{
goto retry;
}
lock (_EventsToProcess)
{
if (_EventsToProcess.Count != 0)
goto retry;
foreach (var item in evts)
{
_EventsToProcess.Enqueue(item);
}
}
goto retry;
}
public NewEventBase[] GetEvents(long lastEventId = 0, int? limit = null, bool longPolling = false, CancellationToken cancellation = default)
{
return GetEventsAsync(lastEventId, limit, longPolling, cancellation).GetAwaiter().GetResult();
}
public async Task<NewEventBase[]> GetEventsAsync(long lastEventId = 0, int? limit = null, bool longPolling = false, CancellationToken cancellation = default)
{
List<string> parameters = new List<string>();
if (lastEventId != 0)
parameters.Add($"lastEventId={lastEventId}");
if (limit != null)
parameters.Add($"limit={limit.Value}");
if (longPolling)
parameters.Add($"longPolling={longPolling}");
var parametersString = parameters.Count == 0 ? string.Empty : $"?{String.Join("&", parameters.ToArray<object>())}";
var evts = await Client.SendAsync<JArray>(HttpMethod.Get, null, $"v1/cryptos/{Client.CryptoCode}/events{ExplorerClient.Raw(parametersString)}", cancellation);
var evtsObj = evts.Select(ev => NewEventBase.ParseEvent((JObject)ev, Client.Serializer.Settings))
.OfType<NewEventBase>()
.ToArray();
return evtsObj;
}
public NewEventBase[] GetLatestEvents(int limit = 10, CancellationToken cancellation = default)
{
return GetLatestEventsAsync(limit, cancellation).GetAwaiter().GetResult();
}
public async Task<NewEventBase[]> GetLatestEventsAsync(int limit = 10, CancellationToken cancellation = default)
{
List<string> parameters = new List<string>();
if (limit != 10)
parameters.Add($"limit={limit}");
var parametersString = parameters.Count == 0 ? string.Empty : $"?{String.Join("&", parameters.ToArray<object>())}";
var evts = await Client.SendAsync<JArray>(HttpMethod.Get, null, $"v1/cryptos/{Client.CryptoCode}/events/latest{ExplorerClient.Raw(parametersString)}", cancellation);
var evtsObj = evts.Select(ev => NewEventBase.ParseEvent((JObject)ev, Client.Serializer.Settings))
.OfType<NewEventBase>()
.ToArray();
return evtsObj;
}
}
}

View File

@ -1,60 +0,0 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
{
public class Bookmark
{
public Bookmark(uint160 value)
{
if(value == null)
throw new ArgumentNullException(nameof(value));
_Value = value;
}
private uint160 _Value;
private static readonly Bookmark _Start = new Bookmark(uint160.Zero);
public static Bookmark Start
{
get
{
return _Start;
}
}
public override bool Equals(object obj)
{
Bookmark item = obj as Bookmark;
if(item == null)
return false;
return _Value.Equals(item._Value);
}
public static bool operator ==(Bookmark a, Bookmark b)
{
if(System.Object.ReferenceEquals(a, b))
return true;
if(((object)a == null) || ((object)b == null))
return false;
return a._Value == b._Value;
}
public static bool operator !=(Bookmark a, Bookmark b)
{
return !(a == b);
}
public override int GetHashCode()
{
return _Value.GetHashCode();
}
public override string ToString()
{
return _Value.ToString();
}
}
}

View File

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
using NBitcoin.RPC;
using NBitcoin.RPC;
namespace NBXplorer.Models
{

View File

@ -0,0 +1,206 @@
using NBitcoin;
using NBitcoin.DataEncoders;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace NBXplorer.Models
{
public class CreatePSBTRequest
{
[JsonProperty("PSBTVersion")]
public int? PSBTVersion { get; set; }
/// <summary>
/// A seed to specific to get a deterministic PSBT (useful for tests)
/// </summary>
public int? Seed { get; set; }
/// <summary>
/// The version of the transaction (Optional, default to 1)
/// </summary>
public uint? Version { get; set; }
/// <summary>
/// The timelock of the transaction, activate RBF if not null (Optional: null, nLockTime to 0)
/// </summary>
public LockTime? LockTime { get; set; }
/// <summary>
/// Discourage fee sniping (Default: true)
/// </summary>
public bool? DiscourageFeeSniping { get; set; }
/// <summary>
/// Whether the include the global xpub in the PSBT (default: false)
/// </summary>
public bool? IncludeGlobalXPub { get; set; }
/// <summary>
/// Whether this transaction should use RBF or not.
/// </summary>
public bool? RBF { get; set; }
/// <summary>
/// Whether this transaction should merge the outputs.
/// </summary>
public bool? MergeOutputs { get; set; }
/// <summary>
/// The destinations where to send the money
/// </summary>
public List<CreatePSBTDestination> Destinations { get; set; } = new List<CreatePSBTDestination>();
/// <summary>
/// Fee settings
/// </summary>
public FeePreference FeePreference { get; set; }
/// <summary>
/// Whether the creation of this PSBT will reserve a new change address
/// </summary>
public bool ReserveChangeAddress { get; set; }
/// <summary>
/// Default to 0, the minimum confirmations a UTXO need to be selected. (by default unconfirmed and confirmed UTXO will be used)
/// </summary>
public int MinConfirmations { get; set; }
/// <summary>
/// Do not select the following outpoints for creating the PSBT (default to empty)
/// </summary>
public List<OutPoint> ExcludeOutpoints { get; set; }
/// <summary>
/// Only select the following outpoints for creating the PSBT (default to null)
/// </summary>
public List<OutPoint> IncludeOnlyOutpoints { get; set; }
/// <summary>
/// If `true`, all the UTXOs that have been selected will be used as input in the PSBT. (default to false)
/// </summary>
public bool? SpendAllMatchingOutpoints { get; set; }
/// <summary>
/// Use a specific change address (Optional, default: null, mutually exclusive with ReserveChangeAddress)
/// </summary>
public PSBTDestination ExplicitChangeAddress { get; set; }
/// <summary>
/// Rebase the hdkey paths (if no rebase, the key paths are relative to the xpub that NBXplorer knows about)
/// This transform (PubKey0, 0/0, accountFingerprint) by (PubKey0, m/49'/0'/0/0, masterFingerprint)
/// </summary>
public List<PSBTRebaseKeyRules> RebaseKeyPaths { get; set; }
/// <summary>
/// Under this value, UTXO's will be ignored.
/// </summary>
public Money MinValue { get; set; }
/// <summary>
/// Disabling the randomization of unspecified parameters to match the network's fingerprint distribution
/// </summary>
public bool? DisableFingerprintRandomization { get; set; }
/// <summary>
/// Attempt setting non_witness_utxo for all inputs even if they are segwit.
/// </summary>
public bool AlwaysIncludeNonWitnessUTXO { get; set; }
}
public class PSBTRebaseKeyRules
{
/// <summary>
/// The account key to rebase
/// </summary>
public BitcoinExtPubKey AccountKey { get; set; }
/// <summary>
/// The path from the root to the account key
/// </summary>
public RootedKeyPath AccountKeyPath { get; set; }
}
public class ScriptDestination : IDestination
{
public ScriptDestination(Script scriptPubKey)
{
ScriptPubKey = scriptPubKey;
}
public Script ScriptPubKey { get; }
}
public abstract class PSBTDestination
{
public static implicit operator PSBTDestination(Script script) => new ScriptType(script);
public static implicit operator PSBTDestination(BitcoinAddress address) => new AddressType(address);
public class ScriptType : PSBTDestination
{
public ScriptType(Script scriptPubKey)
{
if (scriptPubKey is null)
throw new ArgumentNullException(nameof(scriptPubKey));
ScriptPubKey = scriptPubKey;
}
public override Script ScriptPubKey { get; }
public override string ToString() => ScriptPubKey.ToHex();
}
public class AddressType : PSBTDestination
{
public AddressType(BitcoinAddress address)
{
if (address is null)
throw new ArgumentNullException(nameof(address));
Address = address;
}
public BitcoinAddress Address { get; }
public override Script ScriptPubKey => Address.ScriptPubKey;
public override string ToString() => Address.ToString();
}
public abstract Script ScriptPubKey { get; }
public static PSBTDestination Create(Script script) => new ScriptType(script);
public static PSBTDestination Create(BitcoinAddress address) => new AddressType(address);
public static PSBTDestination Parse(string str, Network network)
{
if (str is null)
throw new ArgumentNullException(nameof(str));
if (network is null)
throw new ArgumentNullException(nameof(network));
if (HexEncoder.IsWellFormed(str))
return new ScriptType(Script.FromHex(str));
else
{
return new AddressType(BitcoinAddress.Create(str, network));
}
}
}
public class CreatePSBTDestination
{
/// <summary>
/// The destination as an address or a script. (in hex)
/// </summary>
public PSBTDestination Destination { get; set; }
/// <summary>
/// Will Send this amount to this destination (Mutually exclusive with: SweepAll)
/// </summary>
public Money Amount { get; set; }
/// <summary>
/// Will substract the fees of this transaction to this destination (Mutually exclusive with: SweepAll)
/// </summary>
public bool SubstractFees { get; set; }
/// <summary>
/// Will sweep all the balance of your wallet to this destination (Mutually exclusive with: Amount, SubstractFees)
/// </summary>
public bool SweepAll { get; set; }
}
public class FeePreference
{
/// <summary>
/// An explicit fee rate for the transaction in Satoshi per vBytes (Mutually exclusive with: BlockTarget, FallbackFeeRate)
/// </summary>
public FeeRate ExplicitFeeRate { get; set; }
/// <summary>
/// An explicit fee for the transaction in Satoshi (Mutually exclusive with: BlockTarget, FallbackFeeRate)
/// </summary>
public Money ExplicitFee { get; set; }
/// <summary>
/// A number of blocks after which the user expect one confirmation (Mutually exclusive with: ExplicitFeeRate, ExplicitFee)
/// </summary>
public int? BlockTarget { get; set; }
/// <summary>
/// If the NBXplorer's node does not have proper fee estimation, this specific rate will be use in Satoshi per vBytes. (Mutually exclusive with: ExplicitFeeRate, ExplicitFee)
/// </summary>
public FeeRate FallbackFeeRate { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using NBitcoin;
using Newtonsoft.Json;
namespace NBXplorer.Models
{
public class CreatePSBTResponse
{
[JsonProperty("psbt")]
public PSBT PSBT { get; set; }
public BitcoinAddress ChangeAddress { get; set; }
public CreatePSBTSuggestions Suggestions { get; set; }
}
public class CreatePSBTSuggestions
{
public bool ShouldEnforceLowR { get; set; }
}
}

View File

@ -0,0 +1,23 @@
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace NBXplorer.Models
{
public class GenerateWalletRequest
{
public int AccountNumber { get; set; }
public string ExistingMnemonic { get; set; }
[JsonConverter(typeof(NBXplorer.JsonConverters.WordlistJsonConverter))]
public NBitcoin.Wordlist WordList { get; set; }
[JsonConverter(typeof(NBXplorer.JsonConverters.WordcountJsonConverter))]
public NBitcoin.WordCount? WordCount { get; set; }
[JsonConverter(typeof(NBXplorer.JsonConverters.ScriptPubKeyTypeConverter))]
public NBitcoin.ScriptPubKeyType? ScriptPubKeyType { get; set; }
public string Passphrase { get; set; }
[Obsolete("We will remove this feature in a future release.")]
public bool ImportKeysToRPC { get; set; }
public bool SavePrivateKeys { get; set; }
public Dictionary<string, string> AdditionalOptions { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using NBitcoin;
using NBXplorer.DerivationStrategy;
using Newtonsoft.Json;
namespace NBXplorer.Models
{
public class GenerateWalletResponse
{
public string TrackedSource { get; set; }
public string Mnemonic { get; set; }
public string Passphrase { get; set; }
[JsonConverter(typeof(NBXplorer.JsonConverters.WordlistJsonConverter))]
public NBitcoin.Wordlist WordList { get; set; }
[JsonConverter(typeof(NBXplorer.JsonConverters.WordcountJsonConverter))]
public NBitcoin.WordCount WordCount { get; set; }
public BitcoinExtKey MasterHDKey { get; set; }
public BitcoinExtKey AccountHDKey { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.KeyPathJsonConverter))]
public NBitcoin.RootedKeyPath AccountKeyPath { get; set; }
public string AccountDescriptor { get; set; }
public StandardDerivationStrategyBase DerivationScheme { get; set; }
public Mnemonic GetMnemonic()
{
return new Mnemonic(Mnemonic, WordList);
}
}
}

View File

@ -0,0 +1,29 @@
using NBitcoin;
namespace NBXplorer.Models
{
public class GetBalanceResponse
{
/// <summary>
/// How the confirmed balance would be updated once all the unconfirmed transactions were confirmed.
/// </summary>
public IMoney Unconfirmed { get; set; }
/// <summary>
/// The balance of all funds in confirmed transactions.
/// </summary>
public IMoney Confirmed { get; set; }
/// <summary>
/// The total of funds owned (ie, `confirmed + unconfirmed`)
/// </summary>
public IMoney Total { get; set; }
/// <summary>
/// The total unspendable funds (ie, coinbase reward which need 100 confirmations before being spendable)
/// </summary>
public IMoney Immature { get; set; }
/// <summary>
/// The total spendable balance. (ie, `total - immature`)
/// </summary>
public IMoney Available { get; set; }
}
}

View File

@ -1,7 +1,4 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
{

View File

@ -2,22 +2,16 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
{
public class GetTransactionsResponse
{
public class GetTransactionsResponse
{
public int Height
{
get; set;
}
public bool HasChanges()
{
return ConfirmedTransactions.HasChanges() || UnconfirmedTransactions.HasChanges() || ReplacedTransactions.HasChanges();
}
public TransactionInformationSet ConfirmedTransactions
{
get; set;
@ -28,6 +22,11 @@ namespace NBXplorer.Models
get; set;
} = new TransactionInformationSet();
public TransactionInformationSet ImmatureTransactions
{
get; set;
} = new TransactionInformationSet();
public TransactionInformationSet ReplacedTransactions
{
get; set;
@ -36,27 +35,15 @@ namespace NBXplorer.Models
public class TransactionInformationSet
{
public Bookmark KnownBookmark
{
get; set;
}
public Bookmark Bookmark
{
get; set;
}
public List<TransactionInformation> Transactions
{
get; set;
} = new List<TransactionInformation>();
public bool HasChanges()
{
return KnownBookmark != Bookmark;
}
}
public class TransactionInformationMatch
{
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public KeyPath KeyPath
{
get; set;
@ -76,11 +63,16 @@ namespace NBXplorer.Models
{
get; set;
}
public int Confirmations
public long Confirmations
{
get; set;
}
public int? Height
public long? Height
{
get; set;
}
public bool IsMature
{
get; set;
}
@ -92,24 +84,28 @@ namespace NBXplorer.Models
{
get; set;
}
public List<TransactionInformationMatch> Outputs
public List<MatchedOutput> Outputs
{
get; set;
} = new List<TransactionInformationMatch>();
} = new List<MatchedOutput>();
public List<TransactionInformationMatch> Inputs
public List<MatchedInput> Inputs
{
get; set;
} = new List<TransactionInformationMatch>();
} = new List<MatchedInput>();
public DateTimeOffset Timestamp
{
get;
set;
}
public Money BalanceChange
public IMoney BalanceChange
{
get;
set;
}
public uint256 ReplacedBy { get; set; }
public uint256 Replacing { get; set; }
public bool Replaceable { get; set; }
public TransactionMetadata Metadata { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
{
public class GroupChild
{
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string CryptoCode { get; set; }
public string TrackedSource { get; set; }
}
public class GroupInformation
{
public string TrackedSource { get; set; }
public string GroupId { get; set; }
public GroupChild[] Children { get; set; }
public GroupChild AsGroupChild() => new () { TrackedSource = TrackedSource };
}
}

View File

@ -0,0 +1,10 @@
using NBitcoin;
using Newtonsoft.Json;
namespace NBXplorer.Models;
public class ImportUTXORequest
{
[JsonProperty("UTXOs")]
public OutPoint[] Utxos { get; set; }
}

View File

@ -1,23 +1,25 @@
using NBitcoin;
using NBXplorer.DerivationStrategy;
using Newtonsoft.Json;
using System;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
{
public class KeyPathInformation
{
public TrackedSource TrackedSource { get; set; }
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public DerivationFeature Feature
{
get; set;
}
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public DerivationStrategyBase DerivationStrategy
{
get; set;
}
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public KeyPath KeyPath
{
get; set;
@ -26,9 +28,17 @@ namespace NBXplorer.Models
{
get; set;
}
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public BitcoinAddress Address { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public Script Redeem
{
get; set;
}
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Index { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; } = new Dictionary<string, JToken>();
}
}

View File

@ -0,0 +1,132 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NBXplorer
{
public class KeyPathTemplate
{
private readonly KeyPath preIndexes;
private readonly KeyPath postIndexes;
public KeyPath PostIndexes => postIndexes;
public KeyPath PreIndexes => preIndexes;
public static KeyPathTemplate Parse(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (!TryParse(path, out var v))
throw new FormatException("Invalid keypath template");
return v;
}
public static bool TryParse(string path, out KeyPathTemplate keyPathTemplate)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
bool isValid = true;
uint[] preIndices = null, postIndices = null;
bool isPreIndex = true;
int count = 0;
List<uint> indices = new List<uint>();
foreach (var p in path
.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
.Where(p => p != "m"))
{
if (p == "*")
{
if (isPreIndex)
{
isPreIndex = false;
count++;
preIndices = indices.ToArray();
indices.Clear();
}
else
{
isValid = false;
}
}
else
{
isValid &= TryParseCore(p, out var i);
if(isValid)
indices.Add(i);
count++;
}
}
if (count > 255)
isValid = false;
isValid &= preIndices != null;
if (!isValid)
{
keyPathTemplate = null;
return false;
}
postIndices = indices.ToArray();
keyPathTemplate = new KeyPathTemplate(preIndices, postIndices);
return true;
}
private static bool TryParseCore(string i, out uint index)
{
if (i.Length == 0)
{
index = 0;
return false;
}
bool hardened = i[i.Length - 1] == '\'' || i[i.Length - 1] == 'h';
var nonhardened = hardened ? i.Substring(0, i.Length - 1) : i;
if (!uint.TryParse(nonhardened, out index))
return false;
if (hardened)
{
if (index >= 0x80000000u)
{
index = 0;
return false;
}
index = index | 0x80000000u;
return true;
}
else
{
return true;
}
}
public KeyPathTemplate(uint[] preIndexes, uint[] postIndexes) : this(new KeyPath(preIndexes ?? Array.Empty<uint>()),
new KeyPath(postIndexes ?? Array.Empty<uint>()))
{
}
public KeyPathTemplate(KeyPath preIndexes, KeyPath postIndexes)
{
this.preIndexes = preIndexes ?? new KeyPath();
this.postIndexes = postIndexes ?? new KeyPath();
}
public KeyPath GetKeyPath(uint index)
{
return PreIndexes.Derive(index).Derive(PostIndexes);
}
public KeyPath GetKeyPath(int index, bool hardened)
{
return PreIndexes.Derive(index, hardened).Derive(PostIndexes);
}
public override string ToString()
{
StringBuilder builder = new StringBuilder(PreIndexes.Length + PostIndexes.Length + 20);
if (PreIndexes.Length != 0)
builder.Append($"{PreIndexes}/*");
else
builder.Append("*");
if (PostIndexes.Length != 0)
builder.Append($"/{PostIndexes}");
return builder.ToString();
}
}
}

View File

@ -0,0 +1,48 @@
using NBitcoin;
using NBXplorer.DerivationStrategy;
using System;
using System.Collections.Generic;
namespace NBXplorer
{
public class KeyPathTemplates
{
private static readonly KeyPathTemplate depositKeyPathTemplate = KeyPathTemplate.Parse("0/*");
private static readonly KeyPathTemplate changeKeyPathTemplate = KeyPathTemplate.Parse("1/*");
private static readonly KeyPathTemplate directKeyPathTemplate = KeyPathTemplate.Parse("*");
private readonly KeyPathTemplate customKeyPathTemplate;
private static readonly KeyPathTemplates _Default = new KeyPathTemplates();
private readonly DerivationFeature[] derivationFeatures;
public static KeyPathTemplates Default => _Default;
private KeyPathTemplates() : this(null)
{
}
public KeyPathTemplates(KeyPathTemplate customKeyPathTemplate)
{
this.customKeyPathTemplate = customKeyPathTemplate;
List<DerivationFeature> derivationFeatures = new List<DerivationFeature>
{
DerivationFeature.Deposit,
DerivationFeature.Change,
DerivationFeature.Direct
};
if (customKeyPathTemplate != null)
derivationFeatures.Add(DerivationFeature.Custom);
this.derivationFeatures = derivationFeatures.ToArray();
}
public KeyPathTemplate GetKeyPathTemplate(DerivationFeature derivationFeature)
=> derivationFeature switch
{
DerivationFeature.Deposit => depositKeyPathTemplate,
DerivationFeature.Change => changeKeyPathTemplate,
DerivationFeature.Direct => directKeyPathTemplate,
DerivationFeature.Custom when customKeyPathTemplate != null => customKeyPathTemplate,
_ => throw new NotSupportedException($"The derivation feature {derivationFeature} is not supported by the key path templates.")
};
public IEnumerable<DerivationFeature> GetSupportedDerivationFeatures() => derivationFeatures;
}
}

View File

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
{
@ -28,6 +26,13 @@ namespace NBXplorer.Models
Code = code;
Message = message;
}
public NBXplorerError(int httpCode, string code, string message, string reason)
{
HttpCode = httpCode;
Code = code;
Message = message;
Reason = reason;
}
public int HttpCode
{
get; set;
@ -40,6 +45,10 @@ namespace NBXplorer.Models
{
get; set;
}
public string Reason
{
get; set;
}
public NBXplorerException AsException()
{

View File

@ -1,12 +1,13 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
namespace NBXplorer.Models
{
public class NewBlockEvent
{
public class NewBlockEvent : NewEventBase
{
public NewBlockEvent()
{
}
public int Height
{
get; set;
@ -20,10 +21,15 @@ namespace NBXplorer.Models
{
get; set;
}
public string CryptoCode
public long Confirmations { get; set; }
[JsonIgnore]
public override string EventType => "newblock";
public override string ToString()
{
get;
set;
return $"{CryptoCode}: New block {Hash} ({Height})";
}
}
}

View File

@ -1,20 +1,7 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
namespace NBXplorer.Models
{
public class NewBlockEventRequest
{
public NewBlockEventRequest()
{
}
public String CryptoCode
{
get; set;
}
public class NewBlockEventRequest : NewEventBase
{
public override string EventType => "subscribeblock";
}
}

View File

@ -0,0 +1,108 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
namespace NBXplorer.Models
{
public abstract class NewEventBase
{
static NewEventBase()
{
_TypeByName = new Dictionary<string, Type>();
_NameByType = new Dictionary<Type, string>();
Add("newblock", typeof(Models.NewBlockEvent));
Add("subscribeblock", typeof(Models.NewBlockEventRequest));
Add("subscribetransaction", typeof(Models.NewTransactionEventRequest));
Add("newtransaction", typeof(Models.NewTransactionEvent));
}
static Dictionary<string, Type> _TypeByName;
static Dictionary<Type, string> _NameByType;
private static void Add(string typeName, Type type)
{
_TypeByName.Add(typeName, type);
_NameByType.Add(type, typeName);
}
public static string GetEventTypeName(Type type)
{
_NameByType.TryGetValue(type, out string name);
return name;
}
[JsonIgnore]
public abstract string EventType { get; }
public string CryptoCode
{
get;
set;
}
[JsonIgnore]
public long EventId { get; set; }
public JObject ToJObject(JsonSerializerSettings settings)
{
var typeName = GetEventTypeName(this.GetType());
if (typeName == null)
throw new InvalidOperationException($"{this.GetType().Name} does not have an associated typeName");
JObject jobj = new JObject();
var serialized = JsonConvert.SerializeObject(this, settings);
var data = JObject.Parse(serialized);
if(this.EventId != 0)
jobj.Add(new JProperty("eventId", new JValue(EventId)));
jobj.Add(new JProperty("type", new JValue(typeName)));
jobj.Add(new JProperty("data", data));
return jobj;
}
public static NewEventBase ParseEvent(string str, JsonSerializerSettings settings)
{
if (str == null)
throw new ArgumentNullException(nameof(str));
JObject jobj = JObject.Parse(str);
return ParseEvent(jobj, settings);
}
public static NewEventBase ParseEvent(JObject jobj, JsonSerializerSettings settings)
{
if (jobj == null)
throw new ArgumentNullException(nameof(jobj));
var type = (jobj["type"] as JValue)?.Value<string>();
if (type == null)
throw new FormatException("'type' property not found");
bool unknown = false;
if (!_TypeByName.TryGetValue(type, out Type typeObject))
{
unknown = true;
typeObject = typeof(UnknownEvent);
}
var data = (jobj["data"] as JObject);
if (data == null)
throw new FormatException("'data' property not found");
NewEventBase evt = null;
if (unknown)
{
var unk = new UnknownEvent(type);
unk.Data = data;
unk.CryptoCode = data["cryptoCode"]?.Value<string>();
evt = unk;
}
else
{
evt = (NewEventBase)JsonConvert.DeserializeObject(data.ToString(), typeObject, settings);
}
if(jobj["eventId"] != null)
{
evt.EventId = jobj["eventId"].Value<long>();
}
return evt;
}
public string ToJson(JsonSerializerSettings settings)
{
return JsonConvert.SerializeObject(this, settings);
}
}
}

View File

@ -1,18 +1,23 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using NBitcoin;
using NBXplorer.DerivationStrategy;
using Newtonsoft.Json;
namespace NBXplorer.Models
{
public class NewTransactionEvent
public class NewTransactionEvent : NewEventBase
{
public uint256 BlockId
{
get; set;
}
public TrackedSource TrackedSource { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public DerivationStrategyBase DerivationStrategy
{
get; set;
@ -23,24 +28,57 @@ namespace NBXplorer.Models
get; set;
}
public List<KeyPathInformation> Outputs
public List<MatchedInput> Inputs
{
get; set;
} = new List<KeyPathInformation>();
public List<KeyPathInformation> Inputs
} = new List<MatchedInput>();
public List<MatchedOutput> Outputs
{
get; set;
} = new List<KeyPathInformation>();
public string CryptoCode
{
get;
set;
}
} = new List<MatchedOutput>();
public TransactionMatch AsMatch()
[JsonIgnore]
public override string EventType => "newtransaction";
public List<uint256> Replacing { get; set; }
public override string ToString()
{
return new TransactionMatch() { DerivationStrategy = DerivationStrategy, Inputs = Inputs, Outputs = Outputs, Transaction = TransactionData.Transaction };
var conf = (BlockId == null ? "unconfirmed" : "confirmed");
string strategy = TrackedSource.ToPrettyString();
var txId = TransactionData.TransactionHash.ToString();
txId = txId.Substring(0, 6) + "..." + txId.Substring(txId.Length - 6);
string keyPathSuffix = string.Empty;
var keyPaths = Outputs.Select(v => v.KeyPath?.ToString()).Where(k => k != null).ToArray();
if (keyPaths.Length != 0)
{
keyPathSuffix = $" ({String.Join(", ", keyPaths)})";
}
return $"{CryptoCode}: {strategy} matching {conf} transaction {txId}{keyPathSuffix}";
}
}
}
public class MatchedOutput
{
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public KeyPath KeyPath { get; set; }
public Script ScriptPubKey { get; set; }
public int Index { get; set; }
public int KeyIndex { get; set; }
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public DerivationFeature? Feature { get; set; }
public IMoney Value { get; set; }
public BitcoinAddress Address { get; set; }
}
public class MatchedInput : MatchedOutput
{
public int InputIndex { get; set; }
public uint256 TransactionId
{
get; set;
}
}
}

View File

@ -1,23 +1,12 @@
using NBXplorer.DerivationStrategy;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
namespace NBXplorer.Models
{
public class NewTransactionEventRequest
{
public NewTransactionEventRequest()
{
public class NewTransactionEventRequest : NewEventBase
{
public override string EventType => "subscribetransaction";
public string[] DerivationSchemes { get; set; }
}
public string CryptoCode
{
get; set;
}
public string[] DerivationSchemes
{
get; set;
}
public string[] TrackedSources { get; set; }
public bool? ListenAllTrackedSource { get; set; }
public bool? ListenAllDerivationSchemes { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace NBXplorer.Models
{
public class PruneRequest
{
public double? DaysToKeep { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace NBXplorer.Models
{
public class PruneResponse
{
public int TotalPruned { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using NBitcoin;
using System.Collections.Generic;
namespace NBXplorer.Models
{
public class RescanRequest
{
public class TransactionToRescan
{
public uint256 BlockId
{
get; set;
}
public uint256 TransactionId
{
get; set;
}
public Transaction Transaction
{
get; set;
}
}
public List<TransactionToRescan> Transactions
{
get; set;
} = new List<TransactionToRescan>();
}
}

View File

@ -0,0 +1,96 @@
using NBXplorer.DerivationStrategy;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace NBXplorer.Models
{
public enum ScanUTXOStatus
{
Queued,
Pending,
Error,
Complete
}
public class ScanUTXOProgress
{
[JsonConverter(typeof(Newtonsoft.Json.Converters.UnixDateTimeConverter))]
public DateTimeOffset StartedAt { get; set; }
[JsonConverter(typeof(Newtonsoft.Json.Converters.UnixDateTimeConverter))]
public DateTimeOffset? CompletedAt { get; set; }
public int Found { get; set; }
public int BatchNumber { get; set; }
public int RemainingBatches { get; set; }
public int CurrentBatchProgress { get; set; }
public int OverallProgress { get; set; }
public int RemainingSeconds { get; set; }
public int From { get; set; }
public int Count { get; set; }
public int TotalSearched { get; set; }
public int? TotalSizeOfUTXOSet { get; set; }
public void UpdateOverallProgress(DateTimeOffset? now = null)
{
now = now.HasValue ? now : DateTimeOffset.UtcNow;
double percentPerBatch = 100.0 / (RemainingBatches + BatchNumber + 1);
double batchPercent = BatchNumber * percentPerBatch;
double insideBatchPercent = CurrentBatchProgress * (percentPerBatch / 100.0);
var overallProgress = batchPercent + insideBatchPercent;
var timeSpent = now.Value - StartedAt;
var secondsRemaining = ((100 - overallProgress) / 100.0) * timeSpent.TotalSeconds;
OverallProgress = (int)Math.Round(overallProgress);
RemainingSeconds = (int)Math.Round(secondsRemaining);
}
public void UpdateRemainingBatches(int gapLimit)
{
int highestIndex = -1;
foreach(var index in HighestKeyIndexFound)
{
if(index.Value != null)
{
if (index.Value > highestIndex)
highestIndex = index.Value.Value;
}
}
int gapLimitIndex = highestIndex + gapLimit;
var totalBatchesRequired = (gapLimitIndex / Count) + 1;
RemainingBatches = totalBatchesRequired - (BatchNumber + 1);
}
public Dictionary<DerivationFeature, int?> HighestKeyIndexFound { get; set; } = new Dictionary<DerivationFeature, int?>();
public ScanUTXOProgress Clone()
{
return new ScanUTXOProgress()
{
Found = Found,
BatchNumber = BatchNumber,
CurrentBatchProgress = CurrentBatchProgress,
From = From,
Count = Count,
HighestKeyIndexFound = new Dictionary<DerivationFeature, int?>(HighestKeyIndexFound),
TotalSearched = TotalSearched,
OverallProgress = OverallProgress,
RemainingBatches = RemainingBatches,
CompletedAt = CompletedAt,
StartedAt = StartedAt,
RemainingSeconds = RemainingSeconds
};
}
}
public class ScanUTXOInformation
{
public string Error { get; set; }
[JsonConverter(typeof(Newtonsoft.Json.Converters.UnixDateTimeConverter))]
public DateTimeOffset QueuedAt { get; set; }
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public ScanUTXOStatus Status { get; set; }
public ScanUTXOProgress Progress { get; set; }
}
}

View File

@ -1,8 +1,5 @@
using NBitcoin;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
{
@ -35,18 +32,22 @@ namespace NBXplorer.Models
get;
set;
}
public string[] ExternalAddresses { get; set; }
public NodeCapabilities Capabilities { get; set; }
}
public class StatusResult
public class NodeCapabilities
{
public bool CanScanTxoutSet { get; set; }
public bool CanSupportSegwit { get; set; }
public bool CanSupportTaproot { get; set; }
public bool CanSupportTransactionCheck { get; set; }
}
public class StatusResult
{
public BitcoinStatus BitcoinStatus
{
get; set;
}
public double RepositoryPingTime
{
get;
set;
}
public bool IsFullySynched
{
get; set;
@ -61,8 +62,13 @@ namespace NBXplorer.Models
get;
set;
}
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public NetworkType NetworkType
public string InstanceName
{
get;
set;
}
[JsonConverter(typeof(NBitcoin.JsonConverters.ChainNameJsonConverter))]
public ChainName NetworkType
{
get;
set;

View File

@ -0,0 +1,19 @@
using NBXplorer.DerivationStrategy;
using Newtonsoft.Json;
namespace NBXplorer.Models
{
public class TrackWalletRequest
{
public TrackDerivationOption[] DerivationOptions { get; set; }
public bool Wait { get; set; } = false;
}
public class TrackDerivationOption
{
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public DerivationFeature? Feature { get; set; }
public int? MinAddresses { get; set; }
public int? MaxAddresses { get; set; }
}
}

View File

@ -0,0 +1,251 @@
using NBitcoin;
using NBitcoin.DataEncoders;
using NBXplorer.DerivationStrategy;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
namespace NBXplorer.Models
{
public abstract class TrackedSource
{
public static bool TryParse(string str, out TrackedSource trackedSource, NBXplorerNetwork network)
{
if (str == null)
throw new ArgumentNullException(nameof(str));
trackedSource = null;
var strSpan = str.AsSpan();
if (strSpan.StartsWith("DERIVATIONSCHEME:".AsSpan(), StringComparison.Ordinal))
{
if (network is null)
return false;
if (!DerivationSchemeTrackedSource.TryParse(strSpan, out var derivationSchemeTrackedSource, network))
return false;
trackedSource = derivationSchemeTrackedSource;
}
else if (strSpan.StartsWith("ADDRESS:".AsSpan(), StringComparison.Ordinal))
{
if (network is null)
return false;
if (!AddressTrackedSource.TryParse(strSpan, out var addressTrackedSource, network.NBitcoinNetwork))
return false;
trackedSource = addressTrackedSource;
}
else if (strSpan.StartsWith("GROUP:".AsSpan(), StringComparison.Ordinal))
{
if (!GroupTrackedSource.TryParse(strSpan, out var walletTrackedSource))
return false;
trackedSource = walletTrackedSource;
}
else
{
return false;
}
return true;
}
public override bool Equals(object obj)
{
TrackedSource item = obj as TrackedSource;
if (item == null)
return false;
return ToString().Equals(item.ToString());
}
public static bool operator ==(TrackedSource a, TrackedSource b)
{
if (System.Object.ReferenceEquals(a, b))
return true;
if (((object)a == null) || ((object)b == null))
return false;
return a.ToString() == b.ToString();
}
public static bool operator !=(TrackedSource a, TrackedSource b)
{
return !(a == b);
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
public static DerivationSchemeTrackedSource Create(DerivationStrategyBase strategy)
{
if (strategy == null)
throw new ArgumentNullException(nameof(strategy));
return new DerivationSchemeTrackedSource(strategy);
}
public static AddressTrackedSource Create(BitcoinAddress address)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
return new AddressTrackedSource(address);
}
public static AddressTrackedSource Create(Script scriptPubKey, Network network)
{
if (scriptPubKey == null)
throw new ArgumentNullException(nameof(scriptPubKey));
if (network == null)
throw new ArgumentNullException(nameof(network));
var address = scriptPubKey.GetDestinationAddress(network);
if (address == null)
throw new ArgumentException(paramName: nameof(scriptPubKey), message: $"{nameof(scriptPubKey)} can't be translated on an address on {network.Name}");
return new AddressTrackedSource(address);
}
public virtual string ToPrettyString()
{
return ToString();
}
public static TrackedSource Parse(string str, NBXplorerNetwork network)
{
if (!TryParse(str, out var trackedSource, network))
throw new FormatException("Invalid TrackedSource");
return trackedSource;
}
}
public class GroupTrackedSource : TrackedSource
{
public string GroupId { get; }
public static GroupTrackedSource Generate()
{
Span<byte> r = stackalloc byte[13];
// 13 is most consistent on number of chars and more than we need to avoid generating twice same id
RandomNumberGenerator.Fill(r);
return new GroupTrackedSource(Encoders.Base58.EncodeData(r));
}
public GroupTrackedSource(string groupId)
{
GroupId = groupId;
}
public static bool TryParse(ReadOnlySpan<char> trackedSource, out GroupTrackedSource walletTrackedSource)
{
walletTrackedSource = null;
if (!trackedSource.StartsWith("GROUP:".AsSpan(), StringComparison.Ordinal))
return false;
try
{
walletTrackedSource = new GroupTrackedSource(trackedSource.Slice("GROUP:".Length).ToString());
return true;
}
catch { return false; }
}
public override string ToString()
{
return "GROUP:" + GroupId;
}
public override string ToPrettyString()
{
return "G:" + GroupId;
}
public static GroupTrackedSource Parse(string trackedSource)
{
return TryParse(trackedSource, out var g) ? g : throw new FormatException("Invalid group tracked source format");
}
}
public class AddressTrackedSource : TrackedSource, IDestination
{
// Note that we should in theory access BitcoinAddress. But parsing BitcoinAddress is very expensive, so we keep storing plain strings
public AddressTrackedSource(BitcoinAddress address)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
_FullAddressString = "ADDRESS:" + address;
Address = address;
}
string _FullAddressString;
public BitcoinAddress Address
{
get;
}
public Script ScriptPubKey => Address.ScriptPubKey;
public static bool TryParse(ReadOnlySpan<char> strSpan, out TrackedSource addressTrackedSource, Network network)
{
if (network == null)
throw new ArgumentNullException(nameof(network));
addressTrackedSource = null;
if (!strSpan.StartsWith("ADDRESS:".AsSpan(), StringComparison.Ordinal))
return false;
try
{
addressTrackedSource = new AddressTrackedSource(BitcoinAddress.Create(strSpan.Slice("ADDRESS:".Length).ToString(), network));
return true;
}
catch { return false; }
}
public override string ToString()
{
return _FullAddressString;
}
public override string ToPrettyString()
{
return Address.ToString();
}
}
public class DerivationSchemeTrackedSource : TrackedSource
{
public DerivationSchemeTrackedSource(DerivationStrategy.DerivationStrategyBase derivationStrategy)
{
if (derivationStrategy == null)
throw new ArgumentNullException(nameof(derivationStrategy));
DerivationStrategy = derivationStrategy;
}
public DerivationStrategy.DerivationStrategyBase DerivationStrategy { get; }
public static bool TryParse(ReadOnlySpan<char> strSpan, out DerivationSchemeTrackedSource derivationSchemeTrackedSource, NBXplorerNetwork network)
{
if (network == null)
throw new ArgumentNullException(nameof(network));
derivationSchemeTrackedSource = null;
if (!strSpan.StartsWith("DERIVATIONSCHEME:".AsSpan(), StringComparison.Ordinal))
return false;
try
{
var factory = network.DerivationStrategyFactory;
var derivationScheme = factory.Parse(strSpan.Slice("DERIVATIONSCHEME:".Length).ToString());
derivationSchemeTrackedSource = new DerivationSchemeTrackedSource(derivationScheme);
return true;
}
catch { return false; }
}
public override string ToString()
{
return "DERIVATIONSCHEME:" + DerivationStrategy.ToString();
}
public override string ToPrettyString()
{
var strategy = DerivationStrategy.ToString();
if (strategy.Length > 35)
{
strategy = strategy.Substring(0, 10) + "..." + strategy.Substring(strategy.Length - 20);
}
return strategy;
}
#if !NO_RECORD
public IEnumerable<DerivationFeature> GetDerivationFeatures(KeyPathTemplates keyPathTemplates)
=> DerivationStrategy is PolicyDerivationStrategy ? new[] { DerivationFeature.Deposit, DerivationFeature.Change } : keyPathTemplates.GetSupportedDerivationFeatures();
#else
public IEnumerable<DerivationFeature> GetDerivationFeatures(KeyPathTemplates keyPathTemplates)
=> keyPathTemplates.GetSupportedDerivationFeatures();
#endif
}
}

View File

@ -1,29 +0,0 @@
using NBitcoin;
using NBXplorer.DerivationStrategy;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
{
public class TransactionMatch
{
public DerivationStrategyBase DerivationStrategy
{
get; set;
}
public Transaction Transaction
{
get; set;
}
public List<KeyPathInformation> Outputs
{
get; set;
} = new List<KeyPathInformation>();
public List<KeyPathInformation> Inputs
{
get; set;
} = new List<KeyPathInformation>();
}
}

View File

@ -0,0 +1,40 @@
using NBitcoin;
using NBitcoin.JsonConverters;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
{
public class TransactionMetadata
{
public class ChunkMetadata
{
[JsonProperty("fees", DefaultValueHandling = DefaultValueHandling.Ignore)]
[JsonConverter(typeof(NBXplorer.JsonConverters.MoneyJsonConverter))]
public Money Fees { get; set; }
[JsonProperty("weight", DefaultValueHandling = DefaultValueHandling.Ignore)]
public int Weight { get; set; }
[JsonProperty("feeRate", DefaultValueHandling = DefaultValueHandling.Ignore)]
[JsonConverter(typeof(NBitcoin.JsonConverters.FeeRateJsonConverter))]
public FeeRate FeeRate { get; set; }
}
[JsonProperty("vsize", DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? VirtualSize { get; set; }
[JsonProperty("fees", DefaultValueHandling = DefaultValueHandling.Ignore)]
[JsonConverter(typeof(NBXplorer.JsonConverters.MoneyJsonConverter))]
public Money Fees { get; set; }
[JsonProperty("feeRate", DefaultValueHandling = DefaultValueHandling.Ignore)]
[JsonConverter(typeof(NBitcoin.JsonConverters.FeeRateJsonConverter))]
public FeeRate FeeRate { get; set; }
public ChunkMetadata Chunk { get; set; }
public static TransactionMetadata Parse(string json) => JsonConvert.DeserializeObject<TransactionMetadata>(json);
public string ToString(bool indented) => JsonConvert.SerializeObject(this, indented ? Formatting.Indented : Formatting.None);
public override string ToString() => ToString(true);
[JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; } = new Dictionary<string, JToken>();
}
}

View File

@ -1,22 +1,20 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer.Models
{
public class TransactionResult
{
uint _Confirmations;
public int Confirmations
long _Confirmations;
public long Confirmations
{
get
{
return checked((int)_Confirmations);
return _Confirmations;
}
set
{
_Confirmations = checked((uint)value);
_Confirmations = value;
}
}
@ -32,7 +30,7 @@ namespace NBXplorer.Models
_BlockId = value;
}
}
public uint256 TransactionHash { get; set; }
Transaction _Transaction;
public Transaction Transaction
{
@ -46,7 +44,7 @@ namespace NBXplorer.Models
}
}
public int? Height
public long? Height
{
get;
set;
@ -56,5 +54,8 @@ namespace NBXplorer.Models
get;
set;
}
public uint256 ReplacedBy { get; set; }
public TransactionMetadata Metadata { get; set; }
}
}

View File

@ -1,18 +1,19 @@
using NBitcoin;
using System.Linq;
using NBitcoin.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
using NBitcoin.Crypto;
using System.IO;
using Newtonsoft.Json;
using NBXplorer.DerivationStrategy;
#if !NO_RECORD
using NBitcoin.WalletPolicies;
#endif
namespace NBXplorer.Models
{
public class UTXOChanges
{
public TrackedSource TrackedSource { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public DerivationStrategyBase DerivationStrategy
{
get; set;
@ -44,6 +45,11 @@ namespace NBXplorer.Models
}
}
public List<UTXO> SpentUnconfirmed
{
get;
set;
} = new List<UTXO>();
UTXOChange _Confirmed = new UTXOChange();
public UTXOChange Confirmed
@ -58,69 +64,41 @@ namespace NBXplorer.Models
}
}
public bool HasChanges
{
get
{
return Confirmed.HasChanges || Unconfirmed.HasChanges;
}
}
public Coin[] GetUnspentCoins(bool excludeUnconfirmedUTXOs = false)
{
if(Confirmed.KnownBookmark != null || Unconfirmed.KnownBookmark != null)
throw new InvalidOperationException("This UTXOChanges is partial, it is calculate the unspent coins");
return GetUnspentUTXOs(excludeUnconfirmedUTXOs).Select(c => c.AsCoin(DerivationStrategy)).ToArray();
return GetUnspentCoins(excludeUnconfirmedUTXOs ? 1 : 0);
}
public UTXO[] GetUnspentUTXOs(bool excludeUnconfirmedUTXOs = false)
public Coin[] GetUnspentCoins(int minConfirmations)
{
return GetUnspentUTXOs(minConfirmations).Select(c => c.AsCoin(DerivationStrategy)).ToArray();
}
public UTXO[] GetUnspentUTXOs(int minConf)
{
var excludeUnconfirmedUTXOs = minConf > 0;
Dictionary<OutPoint, UTXO> received = new Dictionary<OutPoint, UTXO>();
foreach(var utxo in Confirmed.UTXOs.Concat(excludeUnconfirmedUTXOs ? (IEnumerable<UTXO>)Array.Empty<UTXO>() : Unconfirmed.UTXOs))
foreach (var utxo in Confirmed.UTXOs.Where(u => u.Confirmations >= minConf).Concat(excludeUnconfirmedUTXOs ? (IEnumerable<UTXO>)Array.Empty<UTXO>() : Unconfirmed.UTXOs))
{
received.TryAdd(utxo.Outpoint, utxo);
}
foreach(var utxo in Confirmed.SpentOutpoints.Concat(Unconfirmed.SpentOutpoints))
foreach (var utxo in Confirmed.SpentOutpoints.Concat(Unconfirmed.SpentOutpoints))
{
received.Remove(utxo);
}
return received.Values.ToArray();
}
public UTXO[] GetUnspentUTXOs(bool excludeUnconfirmedUTXOs = false)
{
return GetUnspentUTXOs(excludeUnconfirmedUTXOs ? 1 : 0);
}
public Key[] GetKeys(ExtKey extKey, bool excludeUnconfirmedUTXOs = false)
{
return GetUnspentUTXOs(excludeUnconfirmedUTXOs).Select(u => extKey.Derive(u.KeyPath).PrivateKey).ToArray();
return GetUnspentUTXOs(excludeUnconfirmedUTXOs).Where(u => u.KeyPath is not null).Select(u => extKey.Derive(u.KeyPath).PrivateKey).ToArray();
}
}
public class UTXOChange
{
Bookmark _KnownBookmark;
public Bookmark KnownBookmark
{
get
{
return _KnownBookmark;
}
set
{
_KnownBookmark = value;
}
}
Bookmark _Bookmark = null;
public Bookmark Bookmark
{
get
{
return _Bookmark;
}
set
{
_Bookmark = value;
}
}
List<UTXO> _UTXOs = new List<UTXO>();
public List<UTXO> UTXOs
{
@ -146,14 +124,6 @@ namespace NBXplorer.Models
_SpentOutpoints = value;
}
}
public bool HasChanges
{
get
{
return KnownBookmark != Bookmark || UTXOs.Count != 0 || SpentOutpoints.Count != 0;
}
}
}
public class UTXO
@ -163,15 +133,18 @@ namespace NBXplorer.Models
}
public UTXO(Coin coin)
public UTXO(ICoin coin)
{
Outpoint = coin.Outpoint;
Value = coin.TxOut.Value;
Index = (int)coin.Outpoint.N;
TransactionHash = coin.Outpoint.Hash;
Value = coin.Amount;
ScriptPubKey = coin.TxOut.ScriptPubKey;
}
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public DerivationFeature Feature
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public DerivationFeature? Feature
{
get; set;
}
@ -183,16 +156,37 @@ namespace NBXplorer.Models
public Coin AsCoin(DerivationStrategy.DerivationStrategyBase derivationStrategy)
{
var coin = new Coin(Outpoint, new TxOut(Value, ScriptPubKey));
if(derivationStrategy != null)
if (Value is Money v)
{
var derivation = derivationStrategy.Derive(KeyPath);
if(derivation.ScriptPubKey != coin.ScriptPubKey)
throw new InvalidOperationException($"This Derivation Strategy does not own this coin");
if(derivation.Redeem != null)
coin = coin.ToScriptCoin(derivation.Redeem);
var coin = new Coin(Outpoint, new TxOut(v, ScriptPubKey));
if (Redeem is not null)
{
coin = coin.ToScriptCoin(Redeem);
}
else
{
DerivationStrategy.Derivation derivation = null;
if (derivationStrategy is StandardDerivationStrategyBase kd && KeyPath is not null)
{
derivation = kd.GetDerivation(KeyPath);
}
#if !NO_RECORD
else if (derivationStrategy is PolicyDerivationStrategy md && Feature is { } f)
{
derivation = md.GetDerivation(f, (uint)KeyIndex);
}
#endif
if (derivation is not null)
{
if (derivation.ScriptPubKey != coin.ScriptPubKey)
throw new InvalidOperationException($"This Derivation Strategy does not own this coin");
if (derivation.Redeem != null)
coin = coin.ToScriptCoin(derivation.Redeem);
}
}
return coin;
}
return coin;
return null;
}
OutPoint _Outpoint = new OutPoint();
@ -208,7 +202,8 @@ namespace NBXplorer.Models
}
}
public int Index { get; set; }
public uint256 TransactionHash { get; set; }
Script _ScriptPubKey;
public Script ScriptPubKey
@ -223,9 +218,12 @@ namespace NBXplorer.Models
}
}
public BitcoinAddress Address { get; set; }
Money _Value;
public Money Value
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public Script Redeem { get; set; }
IMoney _Value;
public IMoney Value
{
get
{
@ -239,6 +237,7 @@ namespace NBXplorer.Models
KeyPath _KeyPath;
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public KeyPath KeyPath
{
get
@ -266,17 +265,19 @@ namespace NBXplorer.Models
}
uint _Confirmations;
public int Confirmations
long _Confirmations;
public long Confirmations
{
get
{
return checked((int)_Confirmations);
return checked((long)_Confirmations);
}
set
{
_Confirmations = checked((uint)value);
_Confirmations = checked((long)value);
}
}
public int KeyIndex { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using Newtonsoft.Json.Linq;
namespace NBXplorer.Models
{
public class UnknownEvent : NewEventBase
{
public UnknownEvent()
{
}
public UnknownEvent(string eventType)
{
_EventType = eventType;
}
string _EventType;
public override string EventType => _EventType;
public JObject Data { get; set; }
}
}

View File

@ -0,0 +1,37 @@
using NBitcoin;
using NBXplorer.DerivationStrategy;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace NBXplorer.Models
{
public class UpdatePSBTRequest
{
[JsonProperty("psbt")]
public PSBT PSBT { get; set; }
public DerivationStrategyBase DerivationScheme { get; set; }
/// <summary>
/// Rebase the hdkey paths (if no rebase, the key paths are relative to the xpub that NBXplorer knows about)
/// This transform (PubKey0, 0/0, accountFingerprint) by (PubKey0, m/49'/0'/0/0, masterFingerprint)
/// </summary>
public List<PSBTRebaseKeyRules> RebaseKeyPaths { get; set; }
/// <summary>
/// Attempt setting non_witness_utxo for all inputs even if they are segwit.
/// </summary>
public bool AlwaysIncludeNonWitnessUTXO { get; set; }
/// <summary>
/// Whether the include the global xpub in the PSBT (default: false)
/// </summary>
public bool? IncludeGlobalXPub { get; set; }
}
public class UpdatePSBTResponse
{
[JsonProperty("psbt")]
public PSBT PSBT { get; set; }
}
}

View File

@ -1,24 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFrameworks>net10.0;netstandard2.1</TargetFrameworks>
<Company>Digital Garage</Company>
<Version>1.0.2.7</Version>
<Version>5.0.6</Version>
<Copyright>Copyright © Digital Garage 2017</Copyright>
<Description>Client API for the minimalist HD Wallet Tracker NBXplorer</Description>
<PackageIconUrl>https://aois.blob.core.windows.net/public/Bitcoin.png</PackageIconUrl>
<PackageIcon>Bitcoin.png</PackageIcon>
<PackageTags>bitcoin</PackageTags>
<PackageProjectUrl>https://github.com/dgarage/NBXplorer/</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/dgarage/NBXplorer/blob/master/LICENSE</PackageLicenseUrl>
<RepositoryUrl>https://github.com/dgarage/NBXplorer/</RepositoryUrl>
<PackageProjectUrl>https://github.com/btcpayserver/NBXplorer/</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/btcpayserver/NBXplorer</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageReadmeFile>README.md</PackageReadmeFile>
<LangVersion>12</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
<DefineConstants>$(DefineConstants);NO_RECORD</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591;1573;1572;1584;1570;3021</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NBitcoin" Version="4.1.1.6" />
<PackageReference Include="NBitcoin.Altcoins" Version="1.0.1.4" />
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.13" />
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.2" />
<PackageReference Include="NBitcoin" Version="10.0.6" />
<PackageReference Include="NBitcoin.Altcoins" Version="6.0.3" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="NBXplorer.Tests" />
</ItemGroup>
<ItemGroup>
<None Include="..\README.md" Pack="true" PackagePath="\" />
<None Include="Bitcoin.png" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.9" />
</ItemGroup>
</Project>

View File

@ -2,44 +2,26 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace NBXplorer
{
public class NBXplorerDefaultSettings
{
static NBXplorerDefaultSettings()
public static string GetFolderName(ChainName chainName)
{
_Settings = new Dictionary<NetworkType, NBXplorerDefaultSettings>();
foreach(var networkType in new[] { NetworkType.Mainnet, NetworkType.Testnet, NetworkType.Regtest })
{
var settings = new NBXplorerDefaultSettings();
_Settings.Add(networkType, settings);
settings.DefaultDataDirectory = StandardConfiguration.DefaultDataDirectory.GetDirectory("NBXplorer", GetFolderName(networkType));
settings.DefaultConfigurationFile = Path.Combine(settings.DefaultDataDirectory, "settings.config");
settings.DefaultCookieFile = Path.Combine(settings.DefaultDataDirectory, ".cookie");
settings.DefaultPort = (networkType == NetworkType.Mainnet ? 24444 :
networkType == NetworkType.Regtest ? 24446 :
networkType == NetworkType.Testnet ? 24445 : throw new NotSupportedException(networkType.ToString()));
settings.DefaultUrl = new Uri($"http://127.0.0.1:{settings.DefaultPort}/", UriKind.Absolute);
}
if (chainName == null)
throw new ArgumentNullException(nameof(chainName));
if (chainName == ChainName.Mainnet)
return "Main";
if (chainName == ChainName.Testnet)
return "TestNet";
if (chainName == ChainName.Regtest)
return "RegTest";
return chainName.ToString();
}
public static string GetFolderName(NetworkType networkType)
{
switch(networkType)
{
case NetworkType.Mainnet:
return "Main";
case NetworkType.Regtest:
return "RegTest";
case NetworkType.Testnet:
return "TestNet";
}
throw new NotSupportedException();
}
static Dictionary<NetworkType, NBXplorerDefaultSettings> _Settings;
static Dictionary<ChainName, NBXplorerDefaultSettings> _Settings = new Dictionary<ChainName, NBXplorerDefaultSettings>();
public string DefaultDataDirectory
{
get;
@ -66,9 +48,68 @@ namespace NBXplorer
set;
}
public static NBXplorerDefaultSettings GetDefaultSettings(NetworkType networkType)
public static NBXplorerDefaultSettings GetDefaultSettings(ChainName networkType)
{
return _Settings[networkType];
if (_Settings.TryGetValue(networkType, out var v))
return v;
lock (_Settings)
{
if (_Settings.TryGetValue(networkType, out v))
return v;
var settings = new NBXplorerDefaultSettings();
settings.DefaultDataDirectory = GetDirectory("NBXplorer", GetFolderName(networkType), false);
settings.DefaultConfigurationFile = Path.Combine(settings.DefaultDataDirectory, "settings.config");
settings.DefaultCookieFile = Path.Combine(settings.DefaultDataDirectory, ".cookie");
settings.DefaultPort = (networkType == ChainName.Mainnet ? 24444 :
networkType == ChainName.Regtest ? 24446 :
networkType == ChainName.Testnet ? 24445 : 24447);
settings.DefaultUrl = new Uri($"http://127.0.0.1:{settings.DefaultPort}/", UriKind.Absolute);
_Settings.Add(networkType, settings);
return settings;
}
}
static string GetDirectory(string appDirectory, string subDirectory, bool createIfNotExists = true)
{
string directory = null;
var home = Environment.GetEnvironmentVariable("HOME");
var localAppData = Environment.GetEnvironmentVariable("APPDATA");
if (!string.IsNullOrEmpty(home) && string.IsNullOrEmpty(localAppData))
{
directory = home;
directory = Path.Combine(directory, "." + appDirectory.ToLowerInvariant());
}
else
{
if (!string.IsNullOrEmpty(localAppData))
{
directory = localAppData;
directory = Path.Combine(directory, appDirectory);
}
else if (createIfNotExists)
{
throw new DirectoryNotFoundException("Could not find suitable datadir environment variables HOME or APPDATA are not set");
}
else
return string.Empty;
}
if (createIfNotExists)
{
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
directory = Path.Combine(directory, subDirectory);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
}
else
{
directory = Path.Combine(directory, subDirectory);
}
return directory;
}
}
}

View File

@ -1,20 +1,22 @@
using NBitcoin;
using NBXplorer.DerivationStrategy;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace NBXplorer
{
public class NBXplorerNetwork
{
public NBXplorerNetwork(INetworkSet networkSet, NBitcoin.NetworkType networkType)
internal NBXplorerNetwork(INetworkSet networkSet, ChainName networkType)
{
NBitcoinNetwork = networkSet.GetNetwork(networkType);
CryptoCode = networkSet.CryptoCode;
DefaultSettings = NBXplorerDefaultSettings.GetDefaultSettings(networkType);
}
public static uint256 UnknownTxId = uint256.Parse("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
public static string UnknownAssetId = uint256.Parse("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").ToString();
public static AssetMoney UnknownAssetMoney = new AssetMoney(uint256.Parse("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), 1);
public bool IsElement => NBitcoinNetwork.NetworkSet == NBitcoin.Altcoins.Liquid.Instance;
public Network NBitcoinNetwork
{
get;
@ -37,31 +39,80 @@ namespace NBXplorer
private set;
}
internal virtual DerivationStrategyFactory CreateStrategyFactory()
{
return new DerivationStrategy.DerivationStrategyFactory(NBitcoinNetwork);
}
public DerivationStrategy.DerivationStrategyFactory DerivationStrategyFactory
{
get;
internal set;
}
[Obsolete]
public virtual BitcoinAddress CreateAddress(DerivationStrategyBase derivationStrategy, KeyPath keyPath, Script scriptPubKey)
{
return scriptPubKey.GetDestinationAddress(NBitcoinNetwork);
}
public bool SupportCookieAuthentication
{
get;
internal set;
} = true;
private Serializer _Serializer;
public Serializer Serializer
{
get
{
_Serializer ??= new Serializer(this);
return _Serializer;
}
}
public JsonSerializerSettings JsonSerializerSettings
{
get
{
return Serializer.Settings;
}
}
public TimeSpan ChainLoadingTimeout
{
get;
set;
} = TimeSpan.FromMinutes(15);
public bool SupportEstimatesSmartFee
public TimeSpan ChainCacheLoadingTimeout
{
get;
set;
} = true;
} = TimeSpan.FromSeconds(30);
/// <summary>
/// Minimum blocks to keep if pruning is activated
/// </summary>
public int MinBlocksToKeep
{
get; set;
} = 288;
public KeyPath CoinType { get; internal set; }
public override string ToString()
{
return CryptoCode.ToString();
}
public virtual ExplorerClient CreateExplorerClient(Uri uri)
{
return new ExplorerClient(this, uri);
}
}
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitAlthash(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Althash.Instance, networkType)
{
MinRPCVersion = 169900,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("172'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetALTHASH()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Althash.Instance.CryptoCode);
}
}
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitArgoneum(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Argoneum.Instance, networkType)
{
MinRPCVersion = 1040000,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("421'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetAGM()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Argoneum.Instance.CryptoCode);
}
}
}

View File

@ -1,17 +1,15 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitBCash(NetworkType networkType)
public partial class NBXplorerNetworkProvider
{
private void InitBCash(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.BCash.Instance, networkType)
{
MinRPCVersion = 140200
MinRPCVersion = 140200,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("145'") : new KeyPath("1'")
});
}

View File

@ -4,11 +4,12 @@ namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitBGold(NetworkType networkType)
private void InitBGold(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.BGold.Instance, networkType)
{
MinRPCVersion = 140200
MinRPCVersion = 140200,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("156'") : new KeyPath("1'")
});
}

View File

@ -1,17 +1,15 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitBitcoin(NetworkType networkType)
private void InitBitcoin(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Bitcoin.Instance, networkType)
{
MinRPCVersion = 150000
MinRPCVersion = 150000,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("0'") : new KeyPath("1'")
});
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitBitcore(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Bitcore.Instance, networkType)
{
MinRPCVersion = 80007,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("160'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetBTX()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Bitcore.Instance.CryptoCode);
}
}
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitChaincoin(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Chaincoin.Instance, networkType)
{
MinRPCVersion = 160400,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("711'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetCHC()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Chaincoin.Instance.CryptoCode);
}
}
}

View File

@ -0,0 +1,20 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitColossus(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Colossus.Instance, networkType)
{
MinRPCVersion = 1010000
});
}
public NBXplorerNetwork GetCOLX()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Colossus.Instance.CryptoCode);
}
}
}

View File

@ -1,17 +1,15 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitDash(NetworkType networkType)
private void InitDash(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Dash.Instance, networkType)
{
MinRPCVersion = 120000
MinRPCVersion = 120000,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("5'") : new KeyPath("1'")
});
}

View File

@ -1,20 +1,18 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitDogecoin(NetworkType networkType)
private void InitDogecoin(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Dogecoin.Instance, networkType)
{
MinRPCVersion = 140200,
ChainLoadingTimeout = TimeSpan.FromHours(1),
SupportCookieAuthentication = false,
SupportEstimatesSmartFee = false
ChainCacheLoadingTimeout = TimeSpan.FromMinutes(2),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("3'") : new KeyPath("1'")
});
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitFeathercoin(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Feathercoin.Instance, networkType)
{
MinRPCVersion = 160000,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("8'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetFTC()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Feathercoin.Instance.CryptoCode);
}
}
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitGobyte(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.GoByte.Instance, networkType)
{
MinRPCVersion = 120204,
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("176'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetGBX()
{
return GetFromCryptoCode(NBitcoin.Altcoins.GoByte.Instance.CryptoCode);
}
}
}

View File

@ -0,0 +1,16 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitGroestlcoin(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Groestlcoin.Instance, networkType)
{
MinRPCVersion = 150000,
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("17'") : new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,124 @@
using NBitcoin;
using System;
using NBitcoin.Altcoins.Elements;
using NBitcoin.Crypto;
using NBitcoin.DataEncoders;
using NBXplorer.DerivationStrategy;
#if !NO_RECORD
using NBitcoin.WalletPolicies;
#endif
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
public class LiquidNBXplorerNetwork : NBXplorerNetwork
{
internal LiquidNBXplorerNetwork(INetworkSet networkSet, ChainName networkType) : base(networkSet, networkType)
{
}
internal override DerivationStrategyFactory CreateStrategyFactory()
{
var factory = base.CreateStrategyFactory();
factory.AuthorizedOptions.Add("unblinded");
factory.AuthorizedOptions.Add("slip77");
return factory;
}
public BitcoinAddress BlindIfNeeded(DerivationStrategyBase derivationStrategy, BitcoinAddress address, KeyPath keyPath)
{
if (derivationStrategy.Unblinded() || address is BitcoinBlindedAddress)
return address;
var blindingPubKey = GenerateBlindingKey(derivationStrategy, keyPath, address.ScriptPubKey, NBitcoinNetwork).PubKey;
return new BitcoinBlindedAddress(blindingPubKey, address);
}
[Obsolete]
public override BitcoinAddress CreateAddress(DerivationStrategyBase derivationStrategy, KeyPath keyPath, Script scriptPubKey)
{
var addr = scriptPubKey.GetDestinationAddress(NBitcoinNetwork);
return BlindIfNeeded(derivationStrategy, addr, keyPath);
}
public static Key GenerateSlip77BlindingKeyFromMnemonic(Mnemonic mnemonic, Script script)
{
var seed = mnemonic.DeriveSeed();
var slip21 = Slip21Node.FromSeed(seed);
var slip77 = slip21.GetSlip77Node();
return slip77.DeriveSlip77BlindingKey(script);
}
public static Key GenerateSlip77BlindingKeyFromMasterBlindingKey(Key masterBlindingKey, Script script)
{
return new Key(Hashes.HMACSHA256(masterBlindingKey.ToBytes(), script.ToBytes()));
}
public static Key GenerateBlindingKey(DerivationStrategyBase derivationStrategy, KeyPath keyPath, Script scriptPubKey, Network network)
{
if (derivationStrategy.Unblinded())
{
throw new InvalidOperationException("This derivation scheme is set to only track unblinded addresses");
}
if (derivationStrategy.Slip77(out var key))
{
if (HexEncoder.IsWellFormed(key))
{
return GenerateSlip77BlindingKeyFromMasterBlindingKey(new Key(Encoders.Hex.DecodeData(key)), scriptPubKey);
}
try
{
return GenerateSlip77BlindingKeyFromMasterBlindingKey(Key.Parse(key, network), scriptPubKey);
}
catch (Exception)
{
// ignored
}
try
{
var data = new Mnemonic(key);
return GenerateSlip77BlindingKeyFromMnemonic(data, scriptPubKey);
}
catch (Exception)
{
// ignored
}
throw new InvalidOperationException("The key provided for slip77 derivation was invalid.");
}
else if (derivationStrategy is StandardDerivationStrategyBase kpd && keyPath is not null)
{
var blindingKey = new Key(kpd.GetDerivation(keyPath.Derive(new KeyPath(0))).ScriptPubKey.WitHash.ToBytes());
return blindingKey;
}
throw new InvalidOperationException("-[blinded] doesn't work on miniscript derivation strategies, use [slip77=key] instead");
}
}
private void InitLiquid(ChainName networkType)
{
Add(new LiquidNBXplorerNetwork(NBitcoin.Altcoins.Liquid.Instance, networkType)
{
MinRPCVersion = 150000,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
});
}
public NBXplorerNetwork GetLBTC()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Liquid.Instance.CryptoCode);
}
}
public static class LiquidDerivationStrategyOptionsExtensions
{
public static bool Unblinded(this DerivationStrategyBase derivationStrategyBase)
{
return derivationStrategyBase.AdditionalOptions.TryGetValue("unblinded", out _);
}
public static bool Slip77(this DerivationStrategyBase derivationStrategyBase ,out string key)
{
return derivationStrategyBase.AdditionalOptions.TryGetValue("slip77", out key);
}
}
}

View File

@ -1,17 +1,15 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitLitecoin(NetworkType networkType)
private void InitLitecoin(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Litecoin.Instance, networkType)
{
MinRPCVersion = 140200
MinRPCVersion = 140200,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("2'") : new KeyPath("1'"),
});
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitMonacoin(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Monacoin.Instance, networkType)
{
MinRPCVersion = 140200,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("22'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetMONA()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Monacoin.Instance.CryptoCode);
}
}
}

View File

@ -0,0 +1,20 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitMonetaryUnit(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.MonetaryUnit.Instance, networkType)
{
MinRPCVersion = 70702
});
}
public NBXplorerNetwork GetMUE()
{
return GetFromCryptoCode(NBitcoin.Altcoins.MonetaryUnit.Instance.CryptoCode);
}
}
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitMonoeci(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Monoeci.Instance, networkType)
{
MinRPCVersion = 120203,
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1998'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetXMCC()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Monoeci.Instance.CryptoCode);
}
}
}

View File

@ -0,0 +1,24 @@
using NBitcoin;
using System;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitPepecoin(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Pepecoin.Instance, networkType)
{
MinRPCVersion = 10000,
ChainLoadingTimeout = TimeSpan.FromHours(1),
ChainCacheLoadingTimeout = TimeSpan.FromMinutes(2),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("3434'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetPEPE()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Pepecoin.Instance.CryptoCode);
}
}
}

View File

@ -1,17 +1,15 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitPolis(NetworkType networkType)
private void InitPolis(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Polis.Instance, networkType)
{
MinRPCVersion = 1030000
MinRPCVersion = 1030000,
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1997'") : new KeyPath("1'")
});
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitQtum(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Qtum.Instance, networkType)
{
MinRPCVersion = 140200,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("2301'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetQTUM()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Qtum.Instance.CryptoCode);
}
}
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitTerracoin(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Terracoin.Instance, networkType)
{
MinRPCVersion = 120204,
CoinType = networkType == ChainName.Mainnet ? new KeyPath("83'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetTRC()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Terracoin.Instance.CryptoCode);
}
}
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitUfo(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Ufo.Instance, networkType)
{
MinRPCVersion = 150000,
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("202'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetUFO()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Ufo.Instance.CryptoCode);
}
}
}

View File

@ -0,0 +1,21 @@
using NBitcoin;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
private void InitViacoin(ChainName networkType)
{
Add(new NBXplorerNetwork(NBitcoin.Altcoins.Viacoin.Instance, networkType)
{
MinRPCVersion = 140200,
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("14'") : new KeyPath("1'")
});
}
public NBXplorerNetwork GetVIA()
{
return GetFromCryptoCode(NBitcoin.Altcoins.Viacoin.Instance.CryptoCode);
}
}
}

View File

@ -1,29 +1,44 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
namespace NBXplorer
{
public partial class NBXplorerNetworkProvider
{
public NBXplorerNetworkProvider(NetworkType networkType)
public NBXplorerNetworkProvider(ChainName networkType)
{
NetworkType = networkType;
InitArgoneum(networkType);
InitBitcoin(networkType);
InitBitcore(networkType);
InitLitecoin(networkType);
InitDogecoin(networkType);
InitPepecoin(networkType);
InitBCash(networkType);
InitGroestlcoin(networkType);
InitBGold(networkType);
InitDash(networkType);
InitTerracoin(networkType);
InitPolis(networkType);
NetworkType = networkType;
foreach(var chain in _Networks.Values)
InitMonacoin(networkType);
InitFeathercoin(networkType);
InitUfo(networkType);
InitViacoin(networkType);
InitMonoeci(networkType);
InitGobyte(networkType);
InitColossus(networkType);
InitChaincoin(networkType);
InitLiquid(networkType);
InitQtum(networkType);
InitAlthash(networkType);
InitMonetaryUnit(networkType);
foreach (var chain in _Networks.Values)
{
chain.DerivationStrategyFactory = new DerivationStrategy.DerivationStrategyFactory(chain.NBitcoinNetwork);
chain.DerivationStrategyFactory ??= chain.CreateStrategyFactory();
}
}
public NetworkType NetworkType
public ChainName NetworkType
{
get;
private set;
@ -31,7 +46,7 @@ namespace NBXplorer
public NBXplorerNetwork GetFromCryptoCode(string cryptoCode)
{
_Networks.TryGetValue(cryptoCode, out NBXplorerNetwork network);
_Networks.TryGetValue(cryptoCode.ToUpperInvariant(), out NBXplorerNetwork network);
return network;
}
@ -43,6 +58,8 @@ namespace NBXplorer
Dictionary<string, NBXplorerNetwork> _Networks = new Dictionary<string, NBXplorerNetwork>();
private void Add(NBXplorerNetwork network)
{
if (network.NBitcoinNetwork == null)
return;
_Networks.Add(network.CryptoCode, network);
}
}

View File

@ -0,0 +1,128 @@
#nullable enable
using System;
using System.Collections.Generic;
using NBXplorer.DerivationStrategy;
namespace NBitcoin;
public static class NBitcoinNBXplorerExtensions
{
/// <summary>
/// Filter the keys which contains the <paramref name="accountKey"/> and <paramref name="accountKeyPath"/> in the HDKeys and whose input/output
/// the same scriptPubKeys as <paramref name="derivationStrategy"/>.
/// </summary>
/// <param name="psbt">The PSBT from which to get the keys</param>
/// <param name="derivationStrategy">The derivation scheme</param>
/// <param name="accountKey">The account key that will be used to sign (i.e., 49'/0'/0')</param>
/// <param name="accountKeyPath">The account key path</param>
/// <returns>HD Keys matching master root key</returns>
public static IEnumerable<PSBTHDKeyMatch> HDKeysFor(this PSBT psbt, DerivationStrategyBase derivationStrategy, IHDKey accountKey,
RootedKeyPath? accountKeyPath)
{
if (ToHDScriptPubKey(derivationStrategy, accountKey) is {} hd)
return psbt.HDKeysFor(hd, accountKey, accountKeyPath);
return Array.Empty<PSBTHDKeyMatch>();
}
/// <summary>
/// Filter the keys that contain the <paramref name="accountKey"/> in the HDKeys and whose input/output
/// the same scriptPubKeys as <paramref name="derivationStrategy"/>.
/// </summary>
/// <param name="psbt">The PSBT from which to get the keys</param>
/// <param name="derivationStrategy">The derivation scheme</param>
/// <param name="accountKey">The account key that will be used to sign (i.e., 49'/0'/0')</param>
/// <returns>HD Keys matching master root key</returns>
public static IEnumerable<PSBTHDKeyMatch> HDKeysFor(this PSBT psbt, DerivationStrategyBase derivationStrategy, IHDKey accountKey)
=> HDKeysFor(psbt, derivationStrategy, accountKey, null);
/// <summary>
/// Filter the keys which contains the <paramref name="accountKey"/> and <paramref name="accountKeyPath"/> in the HDKeys and whose input/output
/// the same scriptPubKeys as <paramref name="derivationStrategy"/>.
/// </summary>
/// <param name="coin">The coins to get the keys from</param>
/// <param name="derivationStrategy">The derivation scheme</param>
/// <param name="accountKey">The account key that will be used to sign (i.e., 49'/0'/0')</param>
/// <param name="accountKeyPath">The account key path</param>
/// <returns>HD Keys matching master root key</returns>
public static IEnumerable<PSBTHDKeyMatch> HDKeysFor(this PSBTCoin coin, DerivationStrategyBase derivationStrategy, IHDKey accountKey,
RootedKeyPath? accountKeyPath)
{
if (ToHDScriptPubKey(derivationStrategy, accountKey) is {} hd)
return coin.HDKeysFor(hd, accountKey, accountKeyPath);
return Array.Empty<PSBTHDKeyMatch>();
}
static IHDScriptPubKey? ToHDScriptPubKey(DerivationStrategyBase derivationStrategy, IHDKey accountKey)
{
if (derivationStrategy is null)
throw new ArgumentNullException(nameof(derivationStrategy));
if (derivationStrategy is StandardDerivationStrategyBase standard)
return standard;
#if !NO_RECORD
else if (derivationStrategy is PolicyDerivationStrategy policy && policy.GetHDScriptPubKey(accountKey) is IHDScriptPubKey hd)
return hd;
#endif
return null;
}
/// <summary>
/// Filter the keys that contain the <paramref name="accountKey"/> in the HDKeys and whose input/output
/// the same scriptPubKeys as <paramref name="derivationStrategy"/>.
/// </summary>
/// <param name="coin">The coins to get the keys from</param>
/// <param name="derivationStrategy">The derivation scheme</param>
/// <param name="accountKey">The account key that will be used to sign (i.e., 49'/0'/0')</param>
/// <returns>HD Keys matching master root key</returns>
public static IEnumerable<PSBTHDKeyMatch> HDKeysFor(this PSBTCoin coin, DerivationStrategyBase derivationStrategy, IHDKey accountKey)
=> HDKeysFor(coin, derivationStrategy, accountKey, null);
/// <summary>
/// Get the balance change if you were signing this transaction.
/// </summary>
/// <param name="psbt">The PSBT from which to get the balance</param>
/// <param name="derivationStrategy">The derivation scheme</param>
/// <param name="accountKey">The account key that will be used to sign (i.e., 49'/0'/0')</param>
/// <param name="accountKeyPath">The account key path</param>
/// <returns>The balance change</returns>
public static Money GetBalance(this PSBT psbt, DerivationStrategyBase derivationStrategy, IHDKey accountKey, RootedKeyPath? accountKeyPath)
{
if (ToHDScriptPubKey(derivationStrategy, accountKey) is {} hd)
return psbt.GetBalance(hd, accountKey, accountKeyPath);
return Money.Zero;
}
/// <summary>
/// Get the balance change if you were signing this transaction.
/// </summary>
/// <param name="psbt">The PSBT from which to get the balance</param>
/// <param name="derivationStrategy">The derivation scheme</param>
/// <param name="accountKey">The account key that will be used to sign (i.e., 49'/0'/0')</param>
/// <returns>The balance change</returns>
public static Money GetBalance(this PSBT psbt, DerivationStrategyBase derivationStrategy, IHDKey accountKey)
=> GetBalance(psbt, derivationStrategy, accountKey, null);
/// <summary>
/// Sign all inputs that derive addresses from <paramref name="derivationStrategy"/> and that need to be signed by <paramref name="accountKey"/>.
/// </summary>
/// <param name="psbt">The PSBT to sign</param>
/// <param name="derivationStrategy">The derivation scheme</param>
/// <param name="accountKey">The account key with which to sign</param>
/// <param name="accountKeyPath">The account key path (eg. [masterFP]/49'/0'/0')</param>
/// <returns>The signed PSBT</returns>
public static PSBT SignAll(this PSBT psbt, DerivationStrategyBase derivationStrategy, IHDKey accountKey, RootedKeyPath? accountKeyPath)
{
if (ToHDScriptPubKey(derivationStrategy, accountKey) is {} hd)
return psbt.SignAll(hd, accountKey, accountKeyPath);
return psbt;
}
/// <summary>
/// Sign all inputs that derive addresses from <paramref name="derivationStrategy"/> and that need to be signed by <paramref name="accountKey"/>.
/// </summary>
/// <param name="psbt">The PSBT to sign</param>
/// <param name="derivationStrategy">The derivation scheme</param>
/// <param name="accountKey">The account key with which to sign</param>
/// <returns>The signed PSBT</returns>
public static PSBT SignAll(this PSBT psbt, DerivationStrategyBase derivationStrategy, IHDKey accountKey)
=> SignAll(psbt, derivationStrategy, accountKey, null);
}

View File

@ -0,0 +1,16 @@
using NBXplorer.Models;
using System.Threading;
using System.Threading.Tasks;
namespace NBXplorer
{
public abstract class NotificationSessionBase
{
public NewEventBase NextEvent(CancellationToken cancellation = default)
{
return NextEventAsync(cancellation).GetAwaiter().GetResult();
}
public abstract Task<NewEventBase> NextEventAsync(CancellationToken cancellation = default);
}
}

View File

@ -1,6 +1,7 @@
rm "bin\release\" -Recurse -Force
dotnet pack --configuration Release
dotnet nuget push "bin\Release\" --source "https://api.nuget.org/v3/index.json"
dotnet pack --configuration Release --include-symbols -p:SymbolPackageFormat=snupkg
$package=(ls .\bin\Release\*.nupkg).FullName
dotnet nuget push $package --source "https://api.nuget.org/v3/index.json"
$ver = ((ls .\bin\release\*.nupkg)[0].Name -replace 'NBXplorer\.Client\.(\d+(\.\d+){1,3}).*', '$1')
git tag -a "Client/v$ver" -m "Client/$ver"
git push --tags
git push origin "Client/v$ver"

Some files were not shown because too many files have changed in this diff Show More