Compare commits

...

662 Commits

Author SHA1 Message Date
dfe655393d Fix enable payjoin when p2sh 2020-04-16 17:23:29 +02:00
8c81dae167 bump 2020-04-16 22:44:53 +09:00
de75d30f06 bump 2020-04-16 22:41:36 +09:00
8ba99d4e7c Merge pull request #1469 from NicolasDorier/changelog
Add changelog 1.0.4.0
2020-04-16 22:40:04 +09:00
c3bc25a7d4 Add changelog 2020-04-16 22:38:38 +09:00
42be03b560 bump nbx and nbitcoin 2020-04-16 19:37:10 +09:00
e73aece9c3 Merge pull request #1467 from dennisreimann/404-center-supporters
Center supporter logos on 404 page
2020-04-16 14:47:35 +09:00
69c57867b3 Merge pull request #1468 from NicolasDorier/persisit/offchaintx
Persist offchain transactions
2020-04-16 14:46:48 +09:00
7434163848 Persist offchain transactions 2020-04-16 14:36:50 +09:00
00d1c4ebcc Navbar fixes (#1466)
* Fix onion position in mobile navbar

* Show nav hover highlight only on desktop

* Fix navbar icon color
2020-04-15 17:13:58 +02:00
26067fbfe2 Center supporter logos on 404 page 2020-04-15 16:49:13 +02:00
03458efea4 Change payjoin endpoint key to pj 2020-04-13 11:52:22 +02:00
bd21bf9c0f add payjoin tooltip 2020-04-13 11:51:25 +02:00
5b7a20c33e Merge pull request #1464 from NicolasDorier/payjoin/persistance
Persist planned transactions and locks for payjoin
2020-04-13 16:57:13 +09:00
c73c34dfaa Persisting locked input and outpoints 2020-04-13 16:06:07 +09:00
a3a9361ba5 Persist the Delayed Broadcaster 2020-04-13 16:00:33 +09:00
2f0e9569a1 Fix build error 2020-04-13 15:57:57 +09:00
511a0efa89 Merge pull request #1465 from Kukks/hot-wallet-policy
add additional server policy for hot wallet RPC import
2020-04-13 15:51:01 +09:00
4ae91ba307 Merge pull request #1461 from bolatovumar/fix-1459
Make sure sponsor logos show up nicely on all screen sizes
2020-04-13 15:49:23 +09:00
3a70f467eb Merge pull request #1457 from bolatovumar/fix-1456
Add mnemonic phrase color CSS variable
2020-04-13 15:48:55 +09:00
4e09bb0b01 add additional server policy for hot wallet
So that if you enable hot wallets for all, you can still not allow them to import to your RPC
2020-04-13 08:48:35 +02:00
5ae18cf21f Make sure mnemonic phrase doesn't blend into background 2020-04-13 08:05:44 +02:00
6bfb6a795e Merge pull request #1462 from NicolasDorier/improve-ux-hotwallet
Do not ask for address confirmation if a new wallet is generated
2020-04-13 13:55:34 +09:00
1d2540543b Payjoin: Randomly round the payment output if it is one input tx 2020-04-13 13:34:23 +09:00
9efe6267d3 Do not ask for address confirmation if a new wallet is generated 2020-04-13 13:03:55 +09:00
cb10551d2c Make sure sponsor logos show up nicely on all screen sizes
fix #1459
2020-04-12 15:08:28 -07:00
b0073af5aa Merge pull request #1408 from Kukks/api/api-key-uu-refactor
Refactor UI for Add APIKey/Authorize
2020-04-10 22:00:32 +09:00
7ca7f53446 Merge pull request #1447 from dennisreimann/modern-theme
New default theme
2020-04-10 16:55:55 +09:00
ad284a4b61 Refactor UI for Add APIKey/Authorize 2020-04-10 09:49:01 +02:00
95e7d5ded9 fix tests 2020-04-10 09:18:08 +02:00
55722b3191 Improve border color themeing 2020-04-10 09:18:08 +02:00
5e34efc9f4 Improve invoice table details display 2020-04-10 09:18:08 +02:00
6f85ffd9df Update footer color 2020-04-10 09:18:08 +02:00
9783a76c38 Fix generated API key color 2020-04-10 09:18:08 +02:00
05952f95f1 Split light and dark theme
To make them explicitely selectable.
2020-04-10 09:18:08 +02:00
b9c97cc5d7 Homepage updates 2020-04-10 09:18:08 +02:00
d5b088b924 Improve color handling 2020-04-10 09:18:08 +02:00
fbd5673cfd POS logo coloring fix 2020-04-10 09:18:08 +02:00
dbb7ad083a Add alert-link classes where necessary 2020-04-10 09:18:08 +02:00
ce7e4234cc Decrease font-size to match classic theme 2020-04-10 09:18:07 +02:00
d2b38fdfce Set default theme for kitchensink 2020-04-10 09:18:07 +02:00
fcdcc5e69b Update main nav hover
fix
2020-04-10 09:18:07 +02:00
8d73606809 Footer updates 2020-04-10 09:18:07 +02:00
15a7c4d092 Use consistent variable names 2020-04-10 09:18:07 +02:00
c205e41072 Rename modern to default theme 2020-04-10 09:18:07 +02:00
48c220b751 Header updates for light/dark theme
fix
2020-04-10 09:18:07 +02:00
06ff268644 Update bootstrap and kitchensink
Discourage use of explicit light/dark classes, because they interfere with the approach of having parallel light/dark themes.
2020-04-10 09:18:07 +02:00
9ee920a816 Draft modern theme
Based on the design input from Figma, see https://www.figma.com/file/C7Xyq0FlxgFW8vaBr8ht1z/BTCPAY?node-id=17%3A126

Color updates
2020-04-10 09:18:07 +02:00
a403363015 Move font-size setting to site.css 2020-04-10 09:18:07 +02:00
6274958409 Trust the payment method details for the payjoin enabled and make invoice logs consistent (#1450) 2020-04-10 16:00:41 +09:00
d47e225dce Fix email sending on registration crash (#1454) 2020-04-10 15:59:39 +09:00
841cf61c92 Add L-CAD support (new Liquid Asset) (#1448)
* Add Liquid CAD support

Adds the Liquid CAD asset ID from Bull Bitcoin

* Add the Liquid CAD logo

* Fix image asset

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
2020-04-10 15:59:02 +09:00
2d2c5b46af Support RBF and PayJoin for GRS (#1455) 2020-04-10 15:38:00 +09:00
cc80e4636f Remove runtime compile on tests (#1453) 2020-04-09 23:19:45 +09:00
bb24c95e71 delete files on user delete (#1452)
fixes #1451
(I did not do a cascade delete for 2 reasons: Sqlite does not support the migration easily to alter a foreign key AND files would be orphaned in the storage with no record in the db)
2020-04-09 22:17:06 +09:00
c7a4158a39 use payjoin key from constants 2020-04-09 14:07:26 +02:00
ed0e423aa7 Add payment link in checkout with payjoin indicator (#1449)
* Add payment link in checkout with payjoin indicator

* move to bottom
2020-04-09 20:44:08 +09:00
1c0d713b00 Catch all error thrown by EndAccept 2020-04-09 20:36:19 +09:00
70c80f4d44 Remove useless line 2020-04-09 20:27:24 +09:00
e3f6de8472 Remove warnings, replace BIP79 by Payjoin, Fix strange error in Accept loop 2020-04-09 20:25:17 +09:00
95644f8884 Fix tests 2020-04-09 19:44:16 +09:00
f57db12c09 Document downside of socks proxy 2020-04-09 18:56:30 +09:00
0d821ff4db Connection: close when using the internal proxy, fix gateway errors with connections in limbo 2020-04-09 18:49:30 +09:00
6927d81175 Implement Http Tor proxy as a internal http proxy 2020-04-09 17:38:55 +09:00
e183714475 Add Tor Http Client Factory (#1445)
* WIP: Add Tor Http Client Factory

This PR adds a Tor Http Client Factory so that we can do HTTP requests over socks5( which we usually use tor's socks5).
Using it for payjoin when possible.

Currently have an issue where a 503 is always returned.  Must be something wrong with my tor config or the socks proxy lib Im using

* fix

* remove external dependency

* make payjoin client use tor client if available

* fix docker test

* use tor client only if available and necessary

* remove bip 79 mention
2020-04-08 22:40:41 +09:00
6602823067 Refactor the input type detection 2020-04-08 22:14:16 +09:00
111feeb673 Merge pull request #1441 from Kukks/payjoin/p2sh
Payjoin: P2SH support
2020-04-08 22:00:07 +09:00
3bf1b78b33 fix 2020-04-08 14:32:29 +02:00
751ccc333f make test less slimfy 2020-04-08 14:32:29 +02:00
624e6e4744 adapt 2020-04-08 14:32:29 +02:00
73b13c750d add P2sh support 2020-04-08 14:32:29 +02:00
148b04e9ba allow changing the payjoin key easily later 2020-04-08 14:32:29 +02:00
32938479ac WIP: Payjoin: P2SH support 2020-04-08 14:32:29 +02:00
aae0086e68 Merge pull request #1443 from dennisreimann/wallet-view-improvements
Wallet view improvements
2020-04-08 21:25:31 +09:00
1f4556bd9d Merge pull request #1440 from dennisreimann/transactions-dropdowns
Fix transaction dropdowns
2020-04-08 21:24:23 +09:00
d7bb15cac3 Merge pull request #1442 from dennisreimann/docker-bitcoin-generate-shell-script
Add bitcoin generate shell script
2020-04-08 21:22:47 +09:00
9a54445785 Merge pull request #1439 from Kukks/monero-fix
monero fixes
2020-04-08 21:11:27 +09:00
523edfef58 Fix wording for no vault connection 2020-04-08 13:29:54 +02:00
5a841216b8 Add checkbox coin selection (js version) 2020-04-08 13:29:16 +02:00
d6d58a98db Improve non-js coin selection 2020-04-08 13:15:03 +02:00
6a0cda69c9 Wallet view improvements
As brought up by @kukks with [this comment](https://github.com/btcpayserver/btcpayserver/pull/1434#issuecomment-609724703). These improvements can be applied across themes, so these fixes are separated from the originating PR.
Pure visual finetunings for the Coin Selection form and amount/fee rate links.
2020-04-08 12:38:26 +02:00
24691e5290 Bump NBitcoin and NBX 2020-04-08 19:34:12 +09:00
b203d369fb Fix payjoin tests 2020-04-08 18:24:04 +09:00
b1cc30d25d Fix sequence check on client 2020-04-08 17:51:22 +09:00
72e64885be Fix sequence check on client 2020-04-08 17:42:50 +09:00
f4a47f5197 Deterministically sort the UTXOs to select 2020-04-08 13:46:11 +09:00
fe45152529 Add bitcoin generate shell script
The shell equivalent to the existing powershell script.
2020-04-07 18:13:47 +02:00
9a9773853e Fix bug in payjoin receiver where too much fee could be substracted from receiver 2020-04-07 20:04:08 +09:00
9d2ab8b154 The payjoin receiver can inject a fake change 2020-04-07 18:14:31 +09:00
ba2184e21a Do not remove outputs in payjoin tx 2020-04-07 15:10:19 +09:00
829b0dd5e2 Fix transaction dropdowns
As brought up by @kukks with [this comment](https://github.com/btcpayserver/btcpayserver/pull/1434#issuecomment-609720019). The problem exists across themes, so the fix is separated from the originating PR.
2020-04-06 18:16:41 +02:00
9fc451c9ca Make stronger check on fee, let the merchant make bigger transaction if he pays the fee 2020-04-06 23:24:51 +09:00
452568e740 monero fixes 2020-04-06 16:12:48 +02:00
5503132ffc Fix vue-i18n var substitution error in checkout
fixes #1438
2020-04-06 13:38:58 +02:00
17a6b7d34f Rename an error of pj to unsupported-input 2020-04-06 20:27:48 +09:00
69ad9edc9a Check pj with 1 sat/b 2020-04-06 20:26:50 +09:00
80bb959ac3 Merge pull request #1415 from Kukks/wallet/camera
Scan address/bip21 with camera
2020-04-06 20:07:32 +09:00
1e0587af26 Make sure tests use fixed fees 2020-04-06 19:18:49 +09:00
16e35e8b55 update html styles 2020-04-06 11:51:01 +02:00
8278926e42 change wallet send buttons 2020-04-06 11:51:01 +02:00
1debbc3cdb Change error messages 2020-04-06 11:51:01 +02:00
65c99ead1d cleanup 2020-04-06 11:51:00 +02:00
2693dacae6 Scan address/bip21 with camera 2020-04-06 11:51:00 +02:00
7ce614f1c4 Merge pull request #1435 from btcpayserver/pr/dark-checkout
Dark checkout theme
2020-04-06 17:46:17 +09:00
79a0f97abb Merge pull request #1432 from Kukks/api/permission-blob
GreenField: Switch to Blob for API Keys
2020-04-06 17:42:32 +09:00
670e0ee7df Merge pull request #1436 from NicolasDorier/remove-rider-errors
Remove bugs reported by Rider
2020-04-06 17:36:07 +09:00
3f231a8894 Merge pull request #1437 from dennisreimann/manage-store-spacings
Improve section spacings on manage store page
2020-04-06 17:35:05 +09:00
f5dfee7642 Improve section spacings on manage store page 2020-04-06 10:12:05 +02:00
ab120c5dcb Fix tests 2020-04-06 17:05:03 +09:00
f085a5618b Defaulting Powered by BtcPayServer link to green in light and dark theme 2020-04-05 23:21:14 -05:00
d939baac84 Give more information to the user if payjoin fails 2020-04-06 13:19:51 +09:00
41d70e8462 Tweaking black background on Pay with and Totals line, colors for dropdowns 2020-04-05 23:09:31 -05:00
9af7edf8b8 Add comments to explain the Payment's ReceivedTime 2020-04-06 12:23:56 +09:00
01a8c20ee8 Use better time precision for time in PaymentEntity 2020-04-05 23:57:43 +09:00
4c966e2a09 Refactor the detection of the payjoin payment 2020-04-05 22:44:34 +09:00
42aead3c89 A replacement payment should have the same fee as the replaced one 2020-04-05 20:48:00 +09:00
a348960041 Make CanChangeNetworkFeeMode more resilient 2020-04-05 20:00:28 +09:00
c737a25234 Add logs to test CanChangeNetworkFeeMode 2020-04-05 18:54:12 +09:00
a01b2e4a83 Small optimization for faster AddDerivationScheme 2020-04-05 18:35:29 +09:00
5f838db281 Merge pull request #1431 from Kukks/api/merge-multiple-swagger
Swagger Generator: Merge multiple documents
2020-04-05 17:36:04 +09:00
08beffb005 Make sure FindPaymentViaPolling does not requests over and over the same coins 2020-04-05 16:50:19 +09:00
76b919d887 make swagger test use live endpoint 2020-04-05 09:43:49 +02:00
c106ac2c42 Adding margin around QR code 2020-04-04 23:44:47 -05:00
4a1fb71e09 Do not print dangerous info in the logs 2020-04-05 12:33:10 +09:00
98e2baae19 Remove bugs reported by Rider 2020-04-05 12:30:23 +09:00
963c69a0e0 Merge pull request #1321 from Kukks/bpu
BIP79
2020-04-05 12:10:55 +09:00
fd026a9733 Refactor server-side 2020-04-05 12:02:36 +09:00
e76785a64e Removing background gradient
It's not smooth on most displays
2020-04-04 18:03:37 -05:00
1a8f222e46 Migrating fixes on default theme back to legacy style 2020-04-04 17:54:13 -05:00
24d26d7a44 Streamlining btnGroupLnd styling 2020-04-04 17:49:43 -05:00
c86370c25a Extracting css class for Powered by BtcPayServer 2020-04-04 17:28:00 -05:00
20cba1d3a1 Tweaking colors on language dropdown hover 2020-04-04 17:16:09 -05:00
3e2efc7f27 Adding first iteration of dark design 2020-04-04 16:47:37 -05:00
d2c29aaec6 Tweaking text opacity, recommended fee line consistency 2020-04-04 16:47:26 -05:00
bb1c5dead5 Moving selection slider to bottom to comply with new design 2020-04-04 16:32:44 -05:00
41cc79600a Swagger Generator: Merge multiple documents
It's becoming very hard to edit the swagger file as it grows (especially with multiple PRs altering it). This PR allows the swagger file to be generated from multiple jsons instead which are merged in the controller.
2020-04-04 14:22:07 +02:00
238d4fceea Merge pull request #1426 from Kukks/fix-dead-link-tester
Fix dead link tester
2020-04-04 14:21:22 +02:00
c6d75de3d7 GreenField: Switch to Blob for API Keys 2020-04-02 09:32:22 +02:00
9e1ae29600 change broadcast button style 2020-03-31 14:57:24 +02:00
d60b00e8cd Merge pull request #1425 from Kukks/coin-selection-fix
Fix coin selection filter bug
2020-03-31 18:43:41 +09:00
49786f4195 add bitpay to blacklist 2020-03-31 11:13:22 +02:00
7b6eae6053 Fixing CheckNoDeadLink test now that btse blocks our call from circleci 2020-03-31 11:12:01 +02:00
a408541eb3 Fix coin selection filter bug 2020-03-31 11:07:57 +02:00
1ba25448cc Merge pull request #1423 from btcpayserver/feat/new-invoice
New invoice design
2020-03-31 17:33:10 +09:00
4d2e59e1a1 Change flow of payjoin in wallet + fix tests 2020-03-30 13:27:04 +02:00
7b4f686add Moving default theme back to root now that we don't have separate setting 2020-03-30 01:38:45 -05:00
ee0ef2881a Removing extra setting, depending on Custom CSS for bundled themes 2020-03-28 14:10:18 -06:00
22f79e9fe4 Merge pull request #1401 from Kukks/wallet-receive-improve
Allow wallet receive to generate new addresses
2020-03-29 00:38:32 +09:00
fdad5a47d5 Reformating file according to project standards 2020-03-27 18:04:30 -05:00
e32f3cbf80 Preserving legacy margin for previous CustomLogoLinks 2020-03-27 18:04:05 -05:00
b56d026fdb small changes 2020-03-27 14:59:00 +01:00
64717328f6 check output sum of proposed payjoin 2020-03-27 10:45:13 +01:00
065be9be64 Revert back to original transaction if payjoined tx does not work 2020-03-27 10:45:13 +01:00
10fcfab233 remove bpu TempData 2020-03-27 10:45:13 +01:00
ff9865c516 fix tor docker 2020-03-27 10:45:13 +01:00
59bae2c337 ToHashSet 2020-03-27 10:45:13 +01:00
89d9793692 remove support from old ledger websocket 2020-03-27 10:45:13 +01:00
23b2f55b47 use nameof 2020-03-27 10:45:13 +01:00
886510c2e1 remove tor for now 2020-03-27 10:45:13 +01:00
2b11b43d6d NRE fix 2020-03-27 10:45:13 +01:00
d90ffb2254 move payjoin settings to store settings from checkout experience 2020-03-27 10:45:13 +01:00
fc88a867fa try fix test 2020-03-27 10:45:13 +01:00
e4cb1a875b Remove tor client factory 2020-03-27 10:45:13 +01:00
1a62ee9260 try fix tor socks5 connection 2020-03-27 10:45:12 +01:00
56d5e6f99f fixes 2020-03-27 10:45:12 +01:00
f1821636db fix tor client creator 2020-03-27 10:45:12 +01:00
2e3a0706ee RBF Protection & Handling 2020-03-27 10:45:12 +01:00
89da4184ff BIP79 Support 2020-03-27 10:45:12 +01:00
1895e154d9 Add missing file 2020-03-27 18:42:59 +09:00
6d7b57ea3b A api key can always revoke itself, add a route to delete any api key 2020-03-27 14:46:51 +09:00
39a8c3fe47 GreenField: Create API Key 2020-03-27 14:17:31 +09:00
927c09ff7b Merge pull request #1418 from NicolasDorier/refactor/greenfield-classes
Rename classes in Greenfield Authentication, use different AuthenticationScheme for Basic versus APIKey
2020-03-27 13:41:32 +09:00
08abda1522 Restrict authentication to the APIController to GreenFieldAPIKeys 2020-03-27 13:34:03 +09:00
d219ba5d32 Split the greenfield authhandler in two classes 2020-03-27 13:06:41 +09:00
afdee9d8a2 Move directories, rename controllers 2020-03-27 12:58:45 +09:00
ac14f199e4 Renaming GreenField classes 2020-03-27 12:55:21 +09:00
76818fa385 Rename API Keys folder to GreenField 2020-03-27 12:44:21 +09:00
49be370e51 Rename GreenField auth to APIKey auth in swagger doc 2020-03-27 12:43:06 +09:00
fbe89f1784 Merge pull request #1416 from pavlenex/invoice-summary
Rename Paid summary to Invoice Summary
2020-03-27 12:32:15 +09:00
b7afcb90a2 Tweaking checkout logo to be set from css 2020-03-26 19:49:04 -05:00
a6ac67963e Adding setting to CheckoutExperience to pick checkout theme 2020-03-26 19:49:00 -05:00
bde8ed7aa2 Extracting checkout theme css as separate link 2020-03-26 19:48:54 -05:00
ca234838a3 Tweaks for close button, recommended fee 2020-03-26 19:48:48 -05:00
d54d340bef Adjusting new color scheme on invoice 2020-03-26 19:48:42 -05:00
a926a5eedf Fix warning 2020-03-26 22:56:30 +09:00
0df5e7d7a3 Fix clone button in edit payment request (Fix #1414) 2020-03-26 22:54:47 +09:00
034fb4ec80 Rename Paid summary to Invoice Summary 2020-03-26 12:50:08 +01:00
69482eb4fb Allow wallet receive to generate new addresses 2020-03-25 17:24:41 +01:00
10e52f08be Prevent NRE exception 2020-03-26 01:21:47 +09:00
5565d8dae5 Catch error with bitflyer 2020-03-26 01:21:46 +09:00
c633402fe2 Merge pull request #1410 from Kukks/coin-selection-fix
fix coin selection
2020-03-26 01:08:53 +09:00
0688feea3c Fix docker files 2020-03-26 00:57:54 +09:00
c906fd42df Add bitflyer direct integration 2020-03-26 00:48:01 +09:00
6468b39121 try fix test again 2020-03-25 14:11:38 +01:00
d0a95f5a69 fix coin selection 2020-03-25 14:11:38 +01:00
e36338d903 Merge pull request #1403 from Kukks/basic-auth
Greenfield API: Basic Auth
2020-03-25 21:00:14 +09:00
e596513fc1 Merge pull request #1409 from Kukks/fix-link-test
Fix Link checker test
2020-03-25 19:33:50 +09:00
77588182b9 Make validation uniform 2020-03-24 23:44:26 -05:00
ca00caa4a4 Do not allow 0 amount invocies for crowdfund and payment requests 2020-03-24 23:43:56 -05:00
36bd76248b Allow Pay Button to work on Apps
This PR allows you to use the pay button generator to create buttons that target apps. This means that you can generate an invoice that is linked to an item on the POS/Crowdfund (targeting the item is optional). The POS/Crowdfund item amount -> invoice creation amount validation works too so that the user cannot modify the amount of a perk using just html ( fixes #1392 )
2020-03-24 23:43:47 -05:00
f0f05acdfd Let 0 amount invoices pass through and allow email to be set when required but paid
@ketominer @Askuwheteau @IAskuwheteau
2020-03-24 23:43:19 -05:00
6df7ffd7e2 lol windows phone 2020-03-24 08:55:47 +01:00
91924512e6 Fix Link checker test 2020-03-24 08:00:47 +01:00
7899c2d5c5 fix test 2020-03-23 21:18:40 +01:00
56ba834ca2 Consolidate auth into one 2020-03-23 16:46:49 +01:00
d57fdd4785 Remove useless delay in tests 2020-03-23 17:19:19 +09:00
805e1f53b3 Test Wallet Receive, Send with NBX, Coin selection 2020-03-23 15:46:54 +09:00
40953ef2c6 Merge pull request #1399 from Kukks/coin-selection
New feature: Coin Selection
2020-03-23 14:05:11 +09:00
ff055c08fb Merge pull request #1404 from btcpayserver/pr/can-manage-wallet
Additional logging and tweaking for CanManageWallet test
2020-03-22 13:53:29 +09:00
f3d5cf3622 Additional logging and tweaking for CanManageWallet test 2020-03-21 12:23:02 -05:00
e48e8c34d9 fix tst 2020-03-20 17:59:14 +01:00
98a48cd0a5 fix swagger validation test 2020-03-20 17:37:39 +01:00
f8f358ebdb add to client, fix tests and doc 2020-03-20 17:14:47 +01:00
9d99c32305 add basic auth for greenfield 2020-03-20 14:07:31 +01:00
478b1463ff Improve documentation 2020-03-20 20:26:36 +09:00
7e7f0053e2 Improve documentation 2020-03-20 20:25:10 +09:00
9a940a044e Fix tests 2020-03-20 20:00:30 +09:00
d2864ccd7c Make sure ApiKeyData set all the fields, remove UserId 2020-03-20 20:00:05 +09:00
ad4dbdad6d Fix the PermissionJsonConverter 2020-03-20 19:57:00 +09:00
094307d688 Remove warning and improve UI of permission selection 2020-03-20 19:39:02 +09:00
53e7c84e73 Fix tests 2020-03-20 18:56:30 +09:00
2a865284da Merge pull request #1402 from Kukks/liquid-assets-divisibility
Fix liquid asset BIP21 decimal precision
2020-03-20 18:39:05 +09:00
4666238e38 Fix build 2020-03-20 18:38:21 +09:00
b54a7b80e3 add tests and fix 2020-03-20 09:31:22 +01:00
432d6bb261 Update documentation 2020-03-20 14:33:11 +09:00
fb36ed2cae Fix tests 2020-03-20 14:07:42 +09:00
55516a3253 Remove excessive folders 2020-03-20 14:03:28 +09:00
a0e638d500 Switch from System.Text.Json to Newtonsoft, typify the BTCPayServer.Client 2020-03-20 14:01:51 +09:00
2def9e7bd3 fix build 2020-03-20 13:58:07 +09:00
0bfc12ae3d Fix build 2020-03-20 13:44:02 +09:00
318d826694 Rename Permissions.Can.. to Policies.Can.. 2020-03-20 13:41:47 +09:00
44b3bb34a4 Merge pull request #1400 from NicolasDorier/refactor/permissions
Refactor permissions of GreenField
2020-03-20 13:30:58 +09:00
46edc281b6 Fix tests 2020-03-20 13:22:10 +09:00
d72139c2c1 Fix liquid asset BIP21 decimal precision
The way liquid assets decimal precision works is just an ui layer. Each unit of an asset is actually 1sat.
Fixes https://github.com/Blockstream/green_android/issues/86
2020-03-19 16:00:33 +01:00
29a807696b Refactor permissions of GreenField 2020-03-19 23:43:51 +09:00
517c65f1fc make no js version a littler better 2020-03-19 10:08:33 +01:00
8f18be727b add clear option 2020-03-19 10:02:31 +01:00
d6c66d0c03 New feature: Coin Selection
This opt-in feature allows you to select which utxos you want to use for a specific transaction.
2020-03-19 09:44:47 +01:00
eac33d494a Add logs 2020-03-19 13:34:11 +09:00
2105b44610 Make sure the create user is respecting the disable-registration settings 2020-03-19 13:30:53 +09:00
ab74013a05 Merge pull request #1398 from btcpayserver/pr/fix-tests
Adding loggin in CanManageWallet test, fixing UserControllerTests
2020-03-19 13:15:17 +09:00
967b02e373 Commenting out conflicting check 2020-03-18 19:17:15 -05:00
8432cd5477 Adding new check for expected behavior in UsersControllerTests 2020-03-18 19:01:27 -05:00
ccfca65c41 Reverting changes to UsersController because of CanCreateUsersViaAPI test 2020-03-18 18:55:45 -05:00
0a8abaf7d5 Refactoring if condition to ensure CanCreateUser permission
Fixing UsersControllerTests
2020-03-18 18:40:21 -05:00
47c1164003 Adding logging to detect failure of CanManageWallet test
If all logs are present it's possibly issue with TempData
2020-03-18 18:17:21 -05:00
65d26ad8a1 improve redoc documentation 2020-03-18 23:35:17 +09:00
0a0d8d53a4 Improve redoc 2020-03-18 23:30:27 +09:00
e50e3f662d Can create user without authentication if there is no admin 2020-03-18 23:10:15 +09:00
540a31207e Properly validate create user input 2020-03-18 20:51:50 +09:00
132c36df7b Remove useless routes and info in swagger 2020-03-18 20:25:54 +09:00
e351e0c9ea Remove dependency on NSwag 2020-03-18 20:08:09 +09:00
8d7b9fcef2 Merge pull request #1390 from Kukks/api/user-create
Greenfield API: Create User
2020-03-18 16:26:10 +09:00
6e1f3989e8 remove special case 2020-03-18 08:10:35 +01:00
e99767c7e2 Greenfield API: Create User
Slightly big PR because I started refactoring to reduce code duplication between the UI based business logic and the api one.
2020-03-17 08:21:27 +01:00
c85fb3e89f Remove ndax from test suite (crashing exchange) 2020-03-17 13:19:55 +09:00
348934488d Refactor tests for greenfield 2020-03-16 16:36:55 +09:00
6c8918a308 Merge pull request #1389 from Kukks/api/god-mode
Greenfield API: God Mode
2020-03-16 16:29:19 +09:00
ff2ea5815c add else tests 2020-03-16 08:13:44 +01:00
cc0202ecb3 fix test 2020-03-13 08:00:04 +01:00
0c065df4bd Greenfield API: God Mode
When the `ServerManagement` permission is granted, you should be able to do everything in the system.
Maybe I should rename it to GodMode as a permission to not have any confusion with managing server settings (currently `ServerManagement`)?
2020-03-12 18:43:57 +01:00
b5664dac81 Merge pull request #1387 from Kukks/api/users-get-current
Greenfield API: Get current User
2020-03-12 23:20:18 +09:00
8173296c96 Greenfield API: Get current User
Builds on #1368
This PR adds a new endpoint: Get current user.. It only returns the current user's id and email for now( let's extend later)
It also adds a new permission: `ProfileManagement` which is needed for this endpoint (and for update endpoints later)
2020-03-12 14:59:24 +01:00
71a00c0e67 Merge pull request #1384 from Kukks/api/authorize-ui
Make api docs only available after login
2020-03-12 12:00:00 +09:00
70b172addc Make api docs only available after login 2020-03-11 18:05:40 +01:00
2002c6750b target netstandard2.1 for Client 2020-03-11 16:54:53 +01:00
786be9d1f5 fix tests 2020-03-11 16:54:53 +01:00
233fa8a4a1 BTCPayServer.Client library + Revoke API Key 2020-03-11 16:54:53 +01:00
c74f52a61c Merge pull request #1383 from Kukks/fix-tests
fix e2e tests
2020-03-12 00:53:44 +09:00
245507f821 fix e2e tests 2020-03-11 16:52:29 +01:00
5495c4b5d3 Merge pull request #1382 from NicolasDorier/betterrbf
Simplify RBF handling, and handle case of double spend happening outs…
2020-03-11 22:41:46 +09:00
afd2c8e3d7 Bump nbx 2020-03-11 22:32:53 +09:00
c8e1db2102 Better event messages 2020-03-11 21:11:07 +09:00
95f859b6db Simplify RBF handling, and handle case of double spend happening outside of wallet (Fix #1375) 2020-03-11 21:05:12 +09:00
6bf7ef0798 Revert "Simplify RBF handling, and handle case of double spend happening outside of wallet (Fix #1375)"
This reverts commit 42152050a3786bd3db103220e1a45b60c9fcddaf.
2020-03-11 20:57:19 +09:00
42152050a3 Simplify RBF handling, and handle case of double spend happening outside of wallet (Fix #1375) 2020-03-11 20:46:37 +09:00
67befcc629 bump 2020-03-10 22:48:17 +09:00
3cdf881438 bump lightning libraries 2020-03-10 22:31:05 +09:00
153992a458 Use good rng for generating API keys 2020-03-10 21:30:46 +09:00
691a8d6fd8 Fix warnings 2020-03-10 21:28:00 +09:00
a9bf843be0 Bump various libraries 2020-03-10 21:24:22 +09:00
60e5afe690 Merge pull request #1379 from Kukks/fix-cf
fix duplicate key error in crowdfunding
2020-03-10 20:58:09 +09:00
980bedf301 Merge pull request #1378 from pavlenex/supporter-btse
Add new supporter to readme and front page
2020-03-10 20:18:40 +09:00
6f6e8ba1a1 fix duplicate key error in crowdfunding 2020-03-10 11:20:05 +01:00
d3af82e38b Add btse to readme and front page 2020-03-10 10:28:15 +01:00
65afc9f7b2 Make sure dashboard is initialized from the beginning 2020-03-10 17:42:53 +09:00
2e630ac5d8 WaitSiteIsOperational should only wait full sync 2020-03-10 17:24:38 +09:00
e6acc19bcc Add a test catching expirationTime bug (#1336) 2020-03-10 17:11:15 +09:00
c598a1827f Remove misleading error message (Fix #1377) 2020-03-10 16:33:50 +09:00
1edd19f403 bump 2020-03-10 15:54:02 +09:00
1052e9a035 Merge pull request #1376 from Kukks/fix-sqllite-again
fix sqlite again
2020-03-10 15:52:38 +09:00
5e15dd97b3 fix sqlite again
closes #1287
Seems like a merge conflict removed the fix for sqlite datetimeoffsets
2020-03-09 09:44:44 +01:00
7763ad5b2c Merge pull request #1372 from btcpayserver/pr/flaky-tests
Fixing ocassional CanManageWallet Selenium test fails
2020-03-06 19:22:43 +09:00
4e826553f8 Fixing ocassional CanManageWallet Selenium test fails
Looking through logs it seems like Generate button is never clicked, which can happen if modal is not displayed
https://circleci.com/gh/btcpayserver/btcpayserver/6167
2020-03-06 02:06:32 -06:00
21c7bcca5a bump 2020-03-06 15:16:53 +09:00
1df0fe9deb Merge pull request #1369 from pavlenex/readme-macos-dev-video
Readme Update .Net Core to 3.1, Add MacOS video
2020-03-04 16:46:07 +09:00
7038c28429 Merge pull request #1370 from bolatovumar/prettify-receive-tab
Prettify wallet receive tab screen
2020-03-04 16:45:34 +09:00
d9bdb46033 Prettify wallet receive tab screen 2020-03-03 21:15:27 -08:00
e0aad34105 Update .Net Core to 3.1, Add MacOS video 2020-03-03 22:44:58 +01:00
a88f46e1ab Merge pull request #1365 from bolatovumar/fix-1332
Specify QR code error correction level explicitly
2020-03-03 18:03:40 +09:00
ba480e40e6 Merge pull request #1362 from pavlenex/readme-deployment
add enviroment configuration to issue template
2020-03-02 18:17:36 +09:00
ef52d6b4c7 Merge pull request #1352 from Kukks/changelly-fiat
remove changelly fiat option
2020-03-02 18:10:50 +09:00
99f47e2848 Merge pull request #1360 from Kukks/pay-button-modal
Modal option for Pay Button
2020-03-02 18:10:14 +09:00
8046872315 Add private info note, change command 2020-03-02 10:07:50 +01:00
b282a70534 Merge pull request #1351 from Kukks/api/api-keys-get
GreenField API #1: Get current API Key info
2020-03-02 18:06:34 +09:00
991daefd85 Merge pull request #1359 from Kukks/cf-fixes
Use proper divisibility for payments in crowdfund and do not show too…
2020-03-02 18:04:14 +09:00
2a0353b6ff Merge pull request #1367 from btcpayserver/fix/flaky-tests
Fixing Selenium tests failing because of dynamic hidden elements
2020-03-02 17:58:06 +09:00
304caaaf1d Fixing Selenium tests failing because of dynamic hidden elements 2020-03-01 22:38:40 -06:00
4f5f52b937 Merge pull request #1366 from btcpayserver/pr/fix-931
Fixing modal Open Wallet click on iOS Safari by targeting iframe parent
2020-03-01 22:05:20 -06:00
0b4760bc29 Merge pull request #1361 from btcpayserver/pr/fix-1316
Showing the next available address in the invoices list
2020-03-01 21:49:14 -06:00
7f6d27cc5b Fixing modal Open Wallet click on iOS Safari by targeting iframe parent 2020-03-01 20:51:34 -06:00
f8520201ce Using tuple for payments partial model 2020-03-01 19:46:05 -06:00
efda8ff5bd Specify QR code error correction level explicitly
fix #1332
2020-03-01 14:16:24 -08:00
27f964e2a1 add enviroment configuration 2020-02-29 10:46:07 +01:00
56380a5fb3 Formatting code 2020-02-28 23:15:14 -06:00
a303e793b4 Fixing CanCreateApiKeys test admin user check 2020-02-28 23:15:06 -06:00
2934c27ee5 Commenting decision to leave partial 2020-02-28 17:04:56 -06:00
44d4673981 Removing reference to InvoicePaymentsPartial.cshtml from Invoice.cshtml 2020-02-28 16:58:09 -06:00
fca6b39681 Revert "Remove the next address to pay to from Invoice details page (Fix #1056) (#1283)"
This reverts commit 6848482999910d4480773a24c6bab407e0565023.
2020-02-28 16:30:57 -06:00
c3bfce7656 Modal option for Pay Button
closes #796
2020-02-28 16:01:44 +01:00
c607696230 Use proper divisibility for payments in crowdfund and do not show tooltip if identical data
fixes #1037 and fixes #1003
2020-02-28 12:51:15 +01:00
9eac33793a GreenField API #1: Get current API Key info 2020-02-26 16:20:32 +01:00
18aaa1a0c4 Merge pull request #1341 from btcpayserver/swagger
Add Swagger and Redoc
2020-02-26 19:02:35 +09:00
e7eea1036b make api key delete use confirm page 2020-02-26 10:26:38 +01:00
48c21baee5 add migration attributes and remove designer 2020-02-26 09:53:58 +01:00
95b9884af7 Revert "consolidate migrations"
This reverts commit 501c3241b543c5941ec9e5d7ecc14cf1edf9661f.
2020-02-26 09:41:32 +01:00
d9ea9fbffd Fix colspan 2020-02-26 17:34:32 +09:00
0c7f35b000 fix swagger gen 2020-02-26 09:17:50 +01:00
78f73132ed Delete docs folder (#1354) 2020-02-26 14:00:46 +09:00
5a93857b4a Simplifying delegate invoke
Ref: 0074790684 (r37477529)
2020-02-25 16:08:57 -06:00
b71fd1653e remove changelly fiat option
closes #728
2020-02-25 16:44:19 +01:00
ec80787120 fix 2020-02-25 15:33:04 +01:00
501c3241b5 consolidate migrations 2020-02-25 15:00:47 +01:00
0a8b303c11 add label for api keys, make api keys without -, fix null exception on authorize 2020-02-25 14:43:53 +01:00
fec5637040 Replace Datetime.UTCNow by entity.InvoiceTime 2020-02-25 17:22:39 +09:00
5cbe61e2e0 Allow user to set the expirationTime of invoice via the API (Fix #1336) 2020-02-25 17:21:08 +09:00
023e64704d Add Swagger and Redoc
Blocked by #1262
2020-02-24 19:04:04 +01:00
276a9a95f9 Remove OpenIddict (#1244) 2020-02-25 00:40:04 +09:00
d16a4334cb Fix error 500 on services page 2020-02-25 00:10:07 +09:00
fa51180dfa Api keys with openiddict (#1262)
* Remove OpenIddict

* Add API Key system

* Revert removing OpenIddict

* fix rebase

* fix tests

* pr changes

* fix tests

* fix apikey test

* pr change

* fix db

* add migration attrs

* fix migration error

* PR Changes

* Fix sqlite migration

* change api key to use Authorization Header

* add supportAddForeignKey

* use tempdata status message

* fix add api key css

* remove redirect url + app identifier feature :(
2020-02-24 22:36:15 +09:00
a3e7729c52 Remove warnings 2020-02-24 22:12:50 +09:00
2a7f6e4aa3 bump 2020-02-24 21:32:54 +09:00
1d61db4758 Add text customization for pay button (#1346)
* Add text customization for pay button

https://i.imgur.com/nFxscOZ.gifv

* pr changes
2020-02-24 21:29:29 +09:00
ee524e36c5 bump 2020-02-24 21:25:52 +09:00
f097ecdc80 fix: remove ipn via email #1241 (#1337)
* fix: remove ipn via email #1241

* fix: remove ipn via email #1241
2020-02-24 21:21:03 +09:00
a354f7d9dd add GET endpoint for pay button (#1349)
closes #889
2020-02-24 21:18:04 +09:00
29d51ad2a2 Adding 1 second leeway for expiration 2020-02-21 17:09:03 -06:00
1be6408246 Adding logging to try and catch situations where invoice is not expired 2020-02-21 17:09:03 -06:00
34702d2633 Revoke Legacy Api Keys (#1344)
closes #1333
2020-02-21 13:40:00 +09:00
b79b310bd5 Revert "Sort invoice list (Fix #1329)"
This reverts commit dc4f8a1fbe841c0824d545582cc46ae533e955c4.
2020-02-21 11:29:09 +09:00
dc4f8a1fbe Sort invoice list (Fix #1329) 2020-02-19 22:04:46 +09:00
6d0896084f Add JS Modal test (#1342) 2020-02-19 17:39:14 +09:00
d31bff7070 BPU Prep Work Part2 (#1340)
* BPU Prep Work Part2

* Adjust tests to use the hot wallet when registering deriv scheme
* Add amount to payment data view for onchain payments
* Make zone limits higher when in dev mode (for tests in next PR)
* Make IPaymentMethodDetails serialize/deserialize through payment type using the network
* Allow named settings through settings repo
* Refactor some extensions for next PR

* pr changes

* use json convert for now
2020-02-19 17:35:23 +09:00
f2aab4cf03 Add warning if fail to load rates from cache 2020-02-16 23:04:48 +09:00
c03dc48fe9 Do not crash if can't load rate cache 2020-02-16 22:07:56 +09:00
143c909812 bump 2020-02-16 19:58:53 +09:00
821b904163 Added SendGrid, Mailgun to Quick-fill email settings (#1335) 2020-02-15 14:37:29 +09:00
6015eb337a Fix broken link 2020-02-15 14:36:36 +09:00
5d817a0483 Revert fix mysql 2020-02-14 00:23:00 +09:00
ee9905e85a Fix mysql 2020-02-14 00:07:19 +09:00
ff4c7c364e Fix mysql 2020-02-14 00:02:47 +09:00
a2d657f5cb Fix mysql migration 2020-02-13 23:58:48 +09:00
db6a4687d2 Wallet prep work for BPU (#1331)
* Wallet prep work for BPU

This PR prepares the wallet for #1321. It makes transfers from the vault and ledger to go to their own post actions for processing (not particularly useful in this PR but is needed in BPU to propose a new tx)  It also makes the Sign with seed consistent with redirect to /psbt/ready after signing which it did not do (it stayed on the seed route)

* fix test

* add assert
2020-02-13 22:06:00 +09:00
07f0d95f56 BIP21 Support for Wallet spending (#1322)
* BIP21 Support for Wallet spending

* extract bip21 loading to method

* add bip21 parsing test
2020-02-13 17:18:43 +09:00
1a409a441d Update POLIS related entries (#1313)
* Update Polis related info and services

* Fix Polis Rate Fetcher

* Fix Polis ratefetcher - Cryptopia is obsolete

* POLIS rate provider changes to comply with internal testing

* URL / pair alignment

* Add small doc to re-trigger testing
2020-02-13 14:44:31 +09:00
445e184154 Merge pull request #1328 from pavlenex/readme
Minor readme cleanup + license clarification
2020-02-13 14:42:18 +09:00
9a10f55a85 Merge remote-tracking branch 'upstream/master' into readme 2020-02-12 19:11:53 +01:00
ae33b1d0a8 Fix PSBT Redirect No-access issues 2020-02-12 16:35:24 +09:00
4ed2db83a5 fix a broken link 2020-02-09 19:01:52 +01:00
500aa85142 Fix broken links 2020-02-09 17:35:00 +01:00
3b6cc84a93 Minor readme cleanup + license clarification 2020-02-09 17:33:44 +01:00
5ce29d2bb8 Merge pull request #1325 from Kukks/fix-revoke-access
Fixes #1324 store token revoke redirect error
2020-02-08 21:54:43 +00:00
3184d2b2df Merge pull request #1327 from Kukks/coldcard-fix
Fix #1326 Coldcard import dialog
2020-02-08 21:54:19 +00:00
f5e65ec2a6 Fix #1326 Coldcard import dialog 2020-02-08 10:54:34 +01:00
66488d813b Fixes #1324 store token revoke redirect error 2020-02-07 08:23:00 +01:00
4853cfe41a bump 2020-02-03 19:13:51 +09:00
dc7733abcd Merge pull request #1041 from Kukks/satscurrency
Add sats as a native currency
2020-02-03 08:42:35 +00:00
771c8e2758 Merge pull request #1314 from btcpayserver/feature/errorpages
Adding error pages to handle HTTP errors
2020-02-03 08:39:58 +00:00
24664b60af Adding test ensuring that api errors are properly returned 2020-02-03 02:21:03 -06:00
82393eb8bb Fixing api exception handling in the pipeline 2020-02-03 02:18:36 -06:00
b432d8903f Grammar fix by Kukks 2020-02-01 11:16:40 -06:00
ea9169f607 Updating 404 page not found assert 2020-02-01 02:29:08 -06:00
496a6f0f55 Special page to handle 429 errors 2020-02-01 01:59:56 -06:00
fb2a0fb7fb Special page to handle 500 errors 2020-02-01 01:58:17 -06:00
ef503fa907 Special page to handle 404 errors 2020-02-01 01:41:27 -06:00
fe2eca4fda Adding prettier error handling page in the pipeline 2020-02-01 01:40:50 -06:00
88835b5b55 Moving _LayoutWelcome to shared folder 2020-02-01 00:32:31 -06:00
876c940032 Reverting delegate reference to previous state until Nicolas confirms change 2020-02-01 00:26:01 -06:00
a08d5be35c Expanding tests to check implicit conversion of Sats to BTC 2020-01-29 22:31:43 -06:00
0074790684 Remove "#nullable enable" directive and unnecessary operators 2020-01-29 01:53:47 -06:00
23aaf794ef Add nullable enable directive to HttpClientRequestMaker.MakeRequestAsync 2020-01-29 01:53:47 -06:00
bb12d37416 Displaying sats in a more user-friendly way (space as group separator) (#1306)
Fix: #1146
2020-01-27 19:57:46 +09:00
e058903450 Do not show assets in sync modal (#1309) 2020-01-26 19:45:52 +09:00
06f1c17a5f Make unused assets in store settings collapsed (#1310) 2020-01-26 19:45:24 +09:00
e00136de93 Fix spurious DefaultAntiforgery errors 2020-01-26 15:02:40 +09:00
56d8c033d7 Update display text on the view model. 2020-01-24 15:45:35 -06:00
666682677c Merge pull request #1303 from btcpayserver/feat/viewnewwindow
Providing open in new window split buttons
2020-01-24 15:34:25 -06:00
652b958d4f Removing viewapp command now that we directly redirect in cshtml 2020-01-24 15:11:34 -06:00
c7c0db612a Restoring IDs Selenium depends on for tests 2020-01-23 20:40:20 -06:00
a83edce4dc Updating idents, code formatting 2020-01-23 20:19:24 -06:00
f99058a9fa Adding code comment for review 2020-01-23 20:18:33 -06:00
a907143d81 Providing open in new window split button when updating crowdfund
Unifying styles on POS and Crowdfund settings

co-authored-by: radWorx <dramirez@soulrivers.com>
2020-01-23 20:17:29 -06:00
4ae173bb69 Providing open in new window split button when updating POS app
co-authored-by: radWorx <dramirez@soulrivers.com>
2020-01-23 20:04:34 -06:00
1436420a93 Providing link to view app in new window
co-authored-by: radWorx <dramirez@soulrivers.com>
2020-01-23 19:51:57 -06:00
086cbaa231 Add clightning rest services page (#1297)
* Add clightning rest services page

* fix rebase
2020-01-23 22:20:37 +09:00
5dd3112e0d Ensure "import from....a new/existing seed" modal text is readable in Casa theme (#1300)
fix #1299
2020-01-23 22:20:00 +09:00
b42e4f240a Fix (#1301)
* Fix seed signing validation

* fix ident
2020-01-23 22:02:37 +09:00
7076692069 fix configurator password loader (#1298) 2020-01-22 15:16:32 +09:00
dcb3601791 Fix ETB asset id 2020-01-21 18:22:42 +01:00
54c7c0d696 Add currency precision based on network (#1294) 2020-01-21 22:28:13 +09:00
f324185d82 bump nbx 2020-01-21 21:47:51 +09:00
a63502873c Add implicit hidden rate rule for sats in parser 2020-01-21 13:34:00 +01:00
f5cbf6672a remove default rate rule for sats 2020-01-21 13:34:00 +01:00
a78dff5931 remove padding 2020-01-21 13:34:00 +01:00
f8139a9156 cleanup (remove sats rate provider and just use rate scripting) 2020-01-21 13:34:00 +01:00
27a61b7afd fix test 2020-01-21 13:34:00 +01:00
71671b9e16 Add sats as a native currency
This will allow you to create an invoice where its primary currency is denominated in sats
2020-01-21 13:33:59 +01:00
c68bf5220e bump 2020-01-21 21:09:49 +09:00
80ee03d897 Remove dead link 2020-01-21 21:06:35 +09:00
d0bfa67495 Fix build 2020-01-21 21:04:35 +09:00
bdb2edba12 Fix U2F signing 2020-01-21 21:00:34 +09:00
78d8f4e011 Fix rescan wallet link 2020-01-21 20:54:45 +09:00
1bfe9dda97 Integrate Configurator External Service (#1190) 2020-01-21 18:27:10 +09:00
8e6f43cd3a Sign with NBX Seed (#1218) 2020-01-21 17:33:12 +09:00
6848482999 Remove the next address to pay to from Invoice details page (Fix #1056) (#1283) 2020-01-21 16:53:24 +09:00
43967ee86e bump 2020-01-21 13:20:52 +09:00
61b99f6630 Add support for ETB liquid asset (#1295) 2020-01-21 13:19:55 +09:00
7e073fb7e1 Add test CanCreateSqlitedb 2020-01-19 22:10:05 +09:00
1ceb5cb576 Fix sqlite madness (#1293)
* Need to reference Microsoft.EntityFrameworkCore.Design else you can't generate migrations
* fix design time migrations issues
* update snapshot
```
Your startup project 'BTCPayServer.Data' doesn't reference Microsoft.EntityFrameworkCore.Design. This package is required for the Entity Framework Core Tools to work. Ensure your startup project is c
orrect, install the package, and try again.
```
*
2020-01-19 21:57:50 +09:00
53a60d1660 Add Direct Provider with bitfinex, okex and coinbasepro 2020-01-18 21:48:04 +09:00
25b733ca7f Remove knowledge of ExchangeName from BackgroundRateFetcher 2020-01-18 19:42:46 +09:00
1bf680fdb9 Improve tests to not create new HttpClient every times 2020-01-18 19:23:40 +09:00
76008c9f5c bump to sdk 3.1.101 and version 3.1.1 (security patch) 2020-01-18 16:59:25 +09:00
1bf04ac4ac Generate less keys with nbxplorer 2020-01-18 16:20:03 +09:00
4b088defd3 Increase timeout for AssertHappyMessage test 2020-01-18 15:36:20 +09:00
a2be7ee471 Fix statusMessage handling for the Receive wallet page 2020-01-18 15:32:01 +09:00
025da0261d new feature: Wallet Receive Page (#1065)
* new feature: Wallet Receive Page

closes #965

* Conserve addresses by waiting till address is spent before generating each run

* fix tests

* Filter by cryptocode before matching outpoint

* fix build

* fix edge case issue

* use address in keypathinfo directly

* rebase fixes

* rebase fixes

* remove duplicate code

* fix messy condition

* fixes

* fix e2e

* fix
2020-01-18 14:12:27 +09:00
4ac79a7ea3 Fixing SQLite in Invoices page (Fix #1287) 2020-01-17 21:45:20 +09:00
ef20a03b95 Fix send from ledger and send from vault 2020-01-17 21:38:27 +09:00
d9681398e5 Bump 2020-01-17 18:21:21 +09:00
25f19e5d9f Merge pull request #1286 from NicolasDorier/rate/refactor
Refactor rate handling to prevent error of exchange name and re-reroute coinaverage to coingecko
2020-01-17 18:20:43 +09:00
aab6fcd508 use coingecko if coinaverage is set 2020-01-17 18:15:08 +09:00
a8ac01cd8b Refactor rate handling to prevent error of exchange name 2020-01-17 18:11:05 +09:00
605a0fd3c9 bump 2020-01-17 15:16:57 +09:00
90ec416125 Add exponential backoff for CoinGecko, pass the cancellationtoken around 2020-01-17 15:08:28 +09:00
827b6085af Do not hammer CoinGecko with tests 2020-01-17 14:56:05 +09:00
ff11e6e032 Remove CoinAverage RateSource enum 2020-01-17 14:51:07 +09:00
9b55648e41 Fix build 2020-01-17 14:45:26 +09:00
6dffbbd93d Remove CachedRateProvider 2020-01-17 14:42:02 +09:00
7a0991d6b1 Remove CoinAverage integration (2) 2020-01-17 14:30:51 +09:00
9b165de5e6 Remove CoinAverage integration 2020-01-17 14:29:22 +09:00
1b9a4e7775 Coingecko should use BackgroundFetcherRateProvider instead of CachedRateProvider 2020-01-17 14:23:04 +09:00
48799562f8 Fix comment 2020-01-17 14:18:18 +09:00
7d545ca682 Remove ability to set custom cache, fix coinaverage not really using coinaverage 2020-01-17 14:16:12 +09:00
9739f3fb25 LN store config: fix a typo (#1285) 2020-01-17 12:12:12 +09:00
bb12de8945 Fix Sqlite migration (#1284) 2020-01-16 22:05:33 +09:00
feabeafed9 Fix Selenium tests ran in Debug mode 2020-01-16 18:03:41 +09:00
6b427e99ca use directly clightning integration instead of charge during debug 2020-01-16 17:15:11 +09:00
31db34ec8d Revert "Revert RazorCompileOnBuild=false temporarily"
This reverts commit 92e5f2852a866bd11bbb4a53b5ebcb6967152c89.
2020-01-16 16:52:46 +09:00
bf614cd322 Make sure the payment button does not error 500 if node not ready (Fix #1180) 2020-01-16 16:25:37 +09:00
9410933e1c Fix: Adding comment on wallet transactions causes 500 error (Close #1280) 2020-01-16 15:19:45 +09:00
c269dee980 Liquid changes (#1281)
Add assetid to bip21 for liquid
change liquid icons
change liquid asset name
change currency code displayed in checkout to one set in network
2020-01-16 15:01:01 +09:00
5aefb585e9 Fix Serilog logging too much 2020-01-16 14:00:31 +09:00
780cf67a1b bump bitcoin core 2020-01-15 13:25:29 +09:00
12e7c5e5e1 Updating referenced lnd to 0.8.2-beta (#1279) 2020-01-15 13:24:10 +09:00
92e5f2852a Revert RazorCompileOnBuild=false temporarily 2020-01-15 00:37:42 +09:00
0fbda9441a Fix AddressInUseException in tests 2020-01-15 00:22:31 +09:00
32a82bbb7c bump 2020-01-15 00:01:36 +09:00
628d0bb690 fix tests 2020-01-15 00:00:36 +09:00
05223c1a5f Bump NBX (#1277) 2020-01-14 23:10:58 +09:00
9aa0603d87 bump 2020-01-14 22:07:13 +09:00
36f980135f fix broken url (#1275)
* fix broken url

* fix test
2020-01-14 22:06:46 +09:00
63953e42a8 Merge pull request #1276 from NicolasDorier/norazor/compile
Remove build of razor view for better debug experience
2020-01-14 21:38:38 +09:00
4ba836f1ff Remove build of razor view for better debug experience 2020-01-14 21:33:13 +09:00
b7132ab66a Merge pull request #1274 from Kukks/route-fixes
fix broken links
2020-01-14 21:00:29 +09:00
67810d50cb Merge pull request #1272 from Kukks/u2ftests
U2F Tests + throw non non U2f exceptions
2020-01-14 20:46:25 +09:00
b7503c994c fix broken links 2020-01-14 12:46:07 +01:00
389695751f add u2f tests 2020-01-14 11:47:24 +01:00
dad3039c06 throw on non-u2f specific errors 2020-01-14 09:49:51 +01:00
9ccb472c7a Fix U2F 2020-01-14 12:31:10 +09:00
c35afd5e9a Fix arm64 image 2020-01-14 00:35:45 +09:00
74adaf1d1f Rename 2020-01-14 00:28:43 +09:00
0fce8d0739 bump 2020-01-13 23:58:01 +09:00
dbb6408acb Merge pull request #1264 from Kukks/coingecko
CoinGecko Rate Provider
2020-01-13 23:57:20 +09:00
5dbdb4b399 Keep coinaverage compatibility, improve UX, hardcode feed provider supported exchanges 2020-01-13 23:37:01 +09:00
58d9a48787 CoinGecko Rate Provider
The CoinGecko rate provider is similar to the bitcoin average one, in that you can ask it for a rate from its aggregated sourcing or you can get rates from specific exchanges. I've added support for both.
I haven't integrated it or replaced coinaverage just to see if we should use it as the default and switch everyone to it or what other action to take.
2020-01-13 20:20:00 +09:00
4baeb7bc71 Merge pull request #1268 from escapedcat/fix/1216_users-remove-name
fix(users): remove name from list #1216
2020-01-13 13:41:03 +09:00
1a3da096a7 Go back to bitcoind 0.18.0 2020-01-13 13:26:42 +09:00
ba0e501e38 bump bitcoind 2020-01-13 13:14:41 +09:00
bff95e4655 fix(users): remove name from list #1216
name is not used and can not be edited so it should be removed to avoid confusion
2020-01-12 15:23:58 +01:00
4f03f3c9cb Bump nbx in tests 2020-01-12 21:45:23 +09:00
d48334b97f Make test more reliable 2020-01-12 20:54:04 +09:00
90da81f68e Merge pull request #1238 from bolatovumar/fix-1236
Adjust invoice pagination behavior
2020-01-12 16:55:57 +09:00
9ba1403f5c bump libraries 2020-01-12 16:30:25 +09:00
846fd815ff Merge pull request #1267 from NicolasDorier/remove/uselesss
Remove useless dependencies
2020-01-12 16:16:03 +09:00
60e0f775ed Remove useless dependencies 2020-01-12 16:05:01 +09:00
529c2df1cc Make tests more resilient 2020-01-12 15:50:23 +09:00
430a9eb261 Merge pull request #1266 from NicolasDorier/bump/libs
Bump libraries
2020-01-12 15:44:05 +09:00
d3408b91be bump libraries 2020-01-12 15:32:26 +09:00
d5febb30e7 Fix build 2020-01-12 13:59:41 +09:00
63c4ec1809 Reactivate GoogleCloudStorage 2020-01-12 13:55:41 +09:00
6ac8cd19d3 Better logs for HappyMessage 2020-01-12 13:54:06 +09:00
c95bceef4d Fix circleci 2020-01-12 13:42:04 +09:00
3449bba4b3 Reactivate google storage 2020-01-12 13:39:42 +09:00
d94b016e63 Add google storage at build time 2020-01-12 13:34:29 +09:00
629dfcf152 Cleanup netcore21 specific code 2020-01-12 13:30:54 +09:00
9876208b7d Add arm64v8 support 2020-01-11 22:54:08 +09:00
df617d5186 Merge pull request #1258 from NicolasDorier/migration/netcore31
Switch to .netcoreapp3.1
2020-01-11 15:03:51 +09:00
21f715bfc6 Add runtime razor compilation during debug 2020-01-11 13:12:41 +09:00
18a2c1a00f Remove warning 2020-01-11 13:12:41 +09:00
6c2fdecebe Rewrite EF query for 3.1 compatibility 2020-01-11 13:12:40 +09:00
c3b7779ea3 Migrate dockerfile to .netcoreapp3.1 2020-01-11 13:12:40 +09:00
83ea95ed6d Switch to .netcoreapp3.1 2020-01-11 13:12:39 +09:00
a816e37621 Update libs 2020-01-11 13:12:24 +09:00
33c65a6548 Merge pull request #1246 from Kukks/generate-wallet-message
add error message on generate wallet failure
2020-01-10 20:03:59 +09:00
bf57701cf0 Passing ambient route parameters explicitely 2020-01-10 15:37:44 +09:00
bfcd90d8d1 Refactor test 2020-01-10 14:46:42 +09:00
0387306918 Adjust invoice listing pagination display 2020-01-09 20:04:55 -08:00
c99d26a55d Fix test for clightning overpaying 2020-01-10 11:13:54 +09:00
7efeeb7c28 Merge pull request #1231 from NicolasDorier/fix/mysqlmigrations
Fix MySQL support
2020-01-09 17:13:53 +09:00
3164783b31 Merge pull request #1253 from radWorx/lead-login-verbiage
"Lead-login" verbiage
2020-01-06 21:50:37 -06:00
28c441924a Merge pull request #1242 from jlopp/varFix
Fix variable spelling
2020-01-06 21:44:17 -06:00
3b3ec7fc21 Merge pull request #1237 from jlopp/spellCheck
Add spelling / grammar fixes and some clarifications for settings
2020-01-06 21:43:46 -06:00
dfa0201726 Merge pull request #1254 from pavlenex/year-update
Update Year 2020 in License
2020-01-06 21:43:20 -06:00
2b889d9e29 Update Year
Happy New Year.
2020-01-03 14:11:15 +01:00
08688f69c0 "lead-login" verbiage
missing final period. suggest changing verbiage to reflect btcpayserver.org verbiage for consistency.
2020-01-02 22:50:26 -05:00
4a0d29c700 Merge pull request #1247 from Kukks/liquid-wallet-changes
Liquid: Show limited wallet pages
2019-12-30 13:21:39 +09:00
fa916d4862 Liquid: Show limited wallet pages 2019-12-29 17:08:30 +01:00
1973647b51 add error message on generate wallet failure 2019-12-29 16:43:55 +01:00
12133cd7d3 fix variable spelling 2019-12-27 14:32:43 -05:00
9b66ba226b bump lightning lib 2019-12-27 21:59:06 +09:00
96731fabc7 bump lightning lib 2019-12-27 21:44:28 +09:00
87f1ab7caa bump lightning 2019-12-27 21:21:18 +09:00
4cf6f8e753 Rename ListInvoices function to ListInvoicesPage to avoid ambiguity 2019-12-26 15:08:43 -08:00
dc59c4ca47 Adjust invoice pagination behavior
fix #1236
2019-12-26 14:53:09 -08:00
4f046ed1d3 add spelling / grammar fixes and some clarifications for settings. 2019-12-26 07:32:32 -05:00
d689222e04 Merge pull request #1234 from NicolasDorier/rates/limit
Do not preemptively fetch rates of all exchanges
2019-12-26 16:36:53 +09:00
57985e78e5 Save the last update time instead of the next update time 2019-12-26 16:24:57 +09:00
731341b749 Do not preemptively fetch rates of all exchanges 2019-12-26 14:54:26 +09:00
f12186e09f The tests in btcpayserver should use only the services they use (#1233) 2019-12-24 18:11:21 +09:00
4d7480db15 Liquid & Liquid Assets Support (#1118) 2019-12-24 16:20:44 +09:00
0485a9178d Merge pull request #1232 from NicolasDorier/seed/bettererror
Make it mandatory for the user to set the master fingerprint in the w…
2019-12-23 23:32:14 +09:00
17d2b20cd5 User can use passphrase when importing seed 2019-12-23 23:31:39 +09:00
aa459d0ff3 Make it mandatory for the user to set the master fingerprint in the wallet settings for seed signing. Improve error messages. 2019-12-23 23:08:41 +09:00
656ff7029e Do not show on screen seed passphrase in sign with seed 2019-12-23 22:32:33 +09:00
8e00e6d8e3 Make sure that varchar(255) is used for mysql migration scripts 2019-12-23 15:03:06 +09:00
8bcb2381a0 Comment code to generate MySql migration scripts 2019-12-23 15:03:05 +09:00
a73d2db02a Fix tests 2019-12-19 16:36:04 +09:00
47eb087d1b Rename CanGenerateWallet to CanUseHotWallet, small refactoring on generatewallet 2019-12-18 22:28:03 +09:00
ed6a01469a Merge pull request #1212 from Kukks/generatewallet
Generate wallet
2019-12-18 16:35:34 +09:00
7cfe5f0421 failsafe with selenium 2019-12-16 12:10:03 +01:00
1aef7f7ea6 rebase fix 2019-12-16 10:25:07 +01:00
9142c48a0b return correct view 2019-12-16 09:38:00 +01:00
45e139c5b7 define view 2019-12-16 09:35:41 +01:00
6706658377 add policy to restrict generate wallet usage 2019-12-16 09:32:43 +01:00
a75b6201b7 update message 2019-12-16 09:01:27 +01:00
f724d6c0cf tests for importing keys to rpc 2019-12-16 09:01:27 +01:00
0dccbeac3d fix tests 2019-12-16 09:01:27 +01:00
7d2dc45dfb fix ident 2019-12-16 09:01:27 +01:00
f284ef9052 Add generate wallet e2e test 2019-12-16 09:01:27 +01:00
80790bd9b0 fixes 2019-12-16 09:00:41 +01:00
2da9434571 init work 2019-12-16 09:00:00 +01:00
579e0d2e09 Do not crash if empty destination is entered on WalletSend 2019-12-11 13:05:59 +09:00
33703b83a3 Fix device not found with Trezor T 2019-12-10 23:52:48 +09:00
23b9dfed2c Cache balance 2019-12-10 22:17:59 +09:00
b8f6ef8844 fix sdk version of tests 2019-12-10 21:46:06 +09:00
6f6b4c8ead Fix dockerfile versions 2019-12-10 21:43:21 +09:00
5d87dd5861 Merge pull request #1219 from bolatovumar/fix-1217
[Wallet] Show invalid address message when address is invalid
2019-12-10 21:39:43 +09:00
02c8bf4469 bump 2019-12-10 21:23:38 +09:00
540cb912f3 Can display address on device at confirmation screen 2019-12-10 21:22:46 +09:00
93f490f570 Fix pin entry 2019-12-10 20:10:13 +09:00
de2e222ae5 Improve signing vault UI 2019-12-10 18:58:12 +09:00
12055a000b Proper handling of passphrase for Trezor T 2019-12-10 18:16:52 +09:00
6addb3e481 [Wallet] Show invalid address message when address is invalid
fix #1217
2019-12-07 21:20:42 -08:00
d9cd916440 Merge pull request #1187 from dennisreimann/document-themeing
Document themeing
2019-12-07 17:22:47 +09:00
452a705b75 Update russian,portugal 2019-12-07 17:21:11 +09:00
1c8206c749 Merge pull request #1214 from MrPaz/feature/satsfix
Fixing sats exchange rate display
2019-12-07 17:18:54 +09:00
062c42e524 Fix build 2019-12-07 16:42:03 +09:00
2d932ebb21 Update to runtime .NET 2.1.14 2019-12-07 16:35:13 +09:00
0e0fa53517 Prompt passphrase for trezor T 2019-12-05 22:47:07 +09:00
4e20730379 Better message when the device is signing (#1207) 2019-12-05 22:37:04 +09:00
1d70d935b8 Provide better details for unknown error (#1208) 2019-12-05 22:03:35 +09:00
9b8f42cdf6 Merge pull request #1213 from rockstardev/feature/accessdeniedfix
Fixing AccessDenied page displayed
2019-12-04 15:38:40 -06:00
eb85b1a7b4 Fixing sats exchange rate display
Fixed #1147
2019-12-04 14:21:33 -06:00
df7e2073df Fixing AccessDenied page displayed 2019-12-04 13:45:09 -06:00
84d943d6cc Trying to fix TrezorT 2019-12-04 22:12:38 +09:00
a1d82b0e8b fix bug if selecting segwit with vault 2019-12-04 21:52:48 +09:00
ab7c124302 Properly show text to enter pin or passphrase for trezor T 2019-12-04 17:23:10 +09:00
544597348b Fix inverted boolean logic 2019-12-04 17:19:25 +09:00
1559c510be Trying to handle Trezor T correctly 2019-12-04 17:16:37 +09:00
3e08581e9b Fix fetching xpub for trezor 2019-12-04 16:34:25 +09:00
49c70d9b04 Vault: Reinite the popup in add derivation scheme if it is closed 2019-12-04 16:16:41 +09:00
50e7d8389c Vault: Allow user to pick the account number 2019-12-04 15:54:08 +09:00
ea5bd6d435 bump nbx 2019-12-04 13:51:26 +09:00
cbb37674e3 vault-confirm is not a popper 2019-12-04 12:56:39 +09:00
e70d5ceb08 Fix JS error on WalletSendVault after PIN is asked (#1210) 2019-12-04 12:52:54 +09:00
5d7b253edd Fix bug: Vault option now showing up in PSBT 2019-12-03 18:57:07 +09:00
df38b84bbb Fix alignment in psbt review 2019-12-03 14:30:29 +09:00
a3fc75ea3b Show proper error message if the keypath in wallet settings is not good (Fix #1209) 2019-12-03 14:26:52 +09:00
71a8166027 Handle "pin already prompted" error. (Fix #1209) 2019-12-03 13:53:58 +09:00
564dd95d81 Remove unused modern theme
We can start over with this once the general design decisions are decided on and we start implementing a new UI.
2019-12-02 11:19:17 +01:00
eb16b435df Document themeing approach
Sums up and adds to the comments made in #1175.

I wasn't sure where to put these kinds of docs, as tehy are code related and not end user facing. Let's discuss whether or not this fits in here or should become part of the docs repo.

fix
2019-12-02 11:18:33 +01:00
f94daed06d Merge pull request #1184 from bolatovumar/fix-1182
[Wallet] Specify validation range for wallet send amount
2019-12-02 18:29:45 +09:00
e841bfa148 Merge pull request #1186 from dennisreimann/custom-theme
UI: Add custom theme file option
2019-12-02 18:29:07 +09:00
03b76380e7 Merge pull request #1192 from dennisreimann/ln-node-certthumbprint
LN Node: Show ready to use certthumbprint
2019-12-02 18:28:11 +09:00
c6671f7122 Merge pull request #1200 from pavlenex/ipn-notification
Re-name the Invoice e-mail notification field
2019-12-02 18:27:52 +09:00
db56c2b106 Merge pull request #1202 from pavlenex/create-wallet
Change create with connect in wallet page
2019-12-02 18:27:40 +09:00
f7b2c836b7 Merge pull request #1203 from Kukks/nbxupdate
update nbx + prep bitcoin payment data ctor change
2019-12-02 18:27:28 +09:00
6ec379b538 Merge pull request #1198 from Eskyee/patch-3
Update vaultbridge.js
2019-12-02 18:27:02 +09:00
d6e1d34ebf update nbx docker tests 2019-12-02 10:10:24 +01:00
bc2a039ea2 bump nbx and make balance return decimal 2019-12-02 09:57:38 +01:00
e31e600144 update nbx + prep bitcoin payment data ctor change 2019-12-01 15:30:56 +01:00
91f83d751f Connect wallet clarification 2019-11-30 18:15:28 +01:00
b8288f1efa Use 127.0.0.1 instead of localhost for vault's http requests. (Fix https://github.com/btcpayserver/BTCPayServer.Vault/issues/9#issuecomment-559858955) 2019-11-30 23:34:47 +09:00
fa61c2bcdd Add error for invalid network 2019-11-30 23:33:42 +09:00
2d168e1d9b Re-name the Invoice e-mail notification field 2019-11-30 11:45:06 +01:00
3e7ad4a4f6 Update vaultbridge.js
replace localhost with 127.0.0.1 fixes BTCPay Vault in Apple Safari browser Version 13.0.3 macOS Catalina 10.15.1 (19B88)
thanks to Nicolas
2019-11-29 20:09:46 +00:00
d2357ee8b9 Do not ask for Pin on Trezor T 2019-11-25 11:50:49 +09:00
9f04c7d0bc Merge pull request #1193 from dennisreimann/heading-alignment
UI: Left align admin section headings
2019-11-25 11:44:25 +09:00
9baa7eed80 UI: Left align admin section headings
Removes the custom property and consistently align the headings left, which I agree is the better choice.

Context: https://chat.btcpayserver.org/btcpayserver/pl/upb3nzch7j8nmpbt7kfmoe6beo
2019-11-24 20:39:29 +01:00
287fbc523f Asks pin and passphrase if device is not ready 2019-11-24 22:51:13 +09:00
ea6df35c87 LN Node: Show ready to use certthumbprint
Turns the output of the `openssl` command from

`SHA256 Fingerprint=48:37:A4:D6:[…]`

into this, which can be copied and inserted directly:

`4837A4D6[…]`
2019-11-24 09:48:34 +01:00
2e0db1a430 Fix XML parsing error (#1185) 2019-11-23 14:04:26 +09:00
7ccf0c3612 UI: Add custom theme file option
This adds a "Custom Theme CSS file" to the "Server settings: Theme" page. It works similar to the custom bootstrap and creative start fields, but is only added to the head in case it is set.

The main idea here is that users would choose one of our predefined themes and have the option to override specific values. The other apporach would be to let the custom theme replace the predefined one, but this could lead to issues in case we extend the set of variables, which would be unset then.
This way users can copy a predefined theme and override all variables or just the ones they want to customize.

The help icon besides the label links to a page in the docs I'm currently working on.
2019-11-22 21:09:18 +01:00
1b58058796 Refactoring 2019-11-22 19:19:05 +09:00
98276bcb3d Remove useless try/catch 2019-11-22 19:16:32 +09:00
1aa4dbb90d bump 2019-11-22 19:14:15 +09:00
eef623fb4b Ask passphrase when appropriate (Fix #1185) 2019-11-22 19:13:04 +09:00
84bbbcbe10 [Wallet] Specify validation range for wallet send amount
fix #1182
2019-11-21 18:47:10 -08:00
a324e2aeaf Merge pull request #1181 from Kukks/register-login-link
add login button to register page
2019-11-21 18:01:39 +09:00
d6b3530384 add login button to register page 2019-11-21 09:28:35 +01:00
dfe8a10e1a bump 2019-11-21 16:39:18 +09:00
5afa847e6e Fix sign transaction 2019-11-21 16:38:43 +09:00
1bcbc9bab0 Should not show the password 2019-11-21 16:03:06 +09:00
b915544798 Prevent NRE on NBxplorer listener 2019-11-21 14:27:57 +09:00
2b9f390c64 update translation 2019-11-21 14:17:27 +09:00
ee42d5c7b4 bump 2019-11-21 14:15:04 +09:00
f809dd51a6 Merge pull request #1152 from NicolasDorier/feature/vault
Add hardware support via BTCPayServer Vault
2019-11-21 14:13:43 +09:00
1a8d6e5c05 Implement BTCPayServer vault derivation scheme import 2019-11-21 14:06:16 +09:00
869ba745b2 Merge pull request #1175 from bolatovumar/fix-1169
Add CSS variable for preformatted text color
2019-11-21 14:03:05 +09:00
180dfb6edf Add CSS variable for preformatted text color
fix #1169
2019-11-20 11:12:31 -08:00
45b08ac8d2 Add sponsor 2019-11-19 20:37:15 +09:00
9a4b385432 Add sponsor 2019-11-19 20:33:26 +09:00
08289b89c5 Merge pull request #1176 from pavlenex/supporters-sw
Add new supporter to readme
2019-11-19 20:32:42 +09:00
a31d1d81c8 Update README.md 2019-11-19 09:20:31 +01:00
e4c7bb0378 add wallet of satoshi to readme
add wallet of Satoshi, fix Lunanode spacing
2019-11-19 09:16:04 +01:00
374aaf2e2b add walletofsatoshi svg logo 2019-11-19 09:12:38 +01:00
52fd686993 Merge pull request #1174 from pavlenex/new-supporters-lw
add new supporter to readme
2019-11-19 00:31:06 +09:00
03c36ef0d2 add lunanode to readme 2019-11-18 15:55:56 +01:00
71a6ffac2e Adjust status message for WalletTransactions 2019-11-18 21:47:09 +09:00
92777ba181 Make sure SSH does not throw in separate thread 2019-11-18 17:15:40 +09:00
81843fb609 Do not delete the password from the seed 2019-11-18 17:00:54 +09:00
3af3ffd038 Merge pull request #1167 from dennisreimann/ui-improvements
UI: Payment request improvement
2019-11-18 14:23:51 +09:00
2ce5cd0b6f Merge pull request #1171 from bitcoinbrisbane/master
Add office 365 to the quick fill settings
2019-11-18 14:23:12 +09:00
d791fd59e9 Merge branch 'master' of github.com:bitcoinbrisbane/btcpayserver 2019-11-18 08:12:30 +10:00
6064e3ce55 Add office365 quick settings 2019-11-18 08:12:06 +10:00
0fcfe0e977 Can prune wallet transaction history 2019-11-17 17:13:09 +09:00
997df5c64d Remove build warnings 2019-11-17 16:50:28 +09:00
27af96662f Fix bug for Network not having a NBitcoin Network 2019-11-17 13:04:42 +09:00
0be6f3ca70 UI: Remove superfluous spaces when description is empty 2019-11-16 23:28:53 +01:00
7af80611b6 UI: Better payment request amounts display
Uses a table instead of list group items, so that the columns properly align (rows use the same grid). Also aligns the values on the right.
2019-11-16 22:59:51 +01:00
929b5c7951 Add display attributes. Fix #98 2019-08-24 15:39:43 +10:00
444 changed files with 29122 additions and 6343 deletions

View File

@ -66,6 +66,20 @@ jobs:
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
arm64v8:
machine:
enabled: true
steps:
- checkout
- run:
command: |
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
#
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 -f arm64v8.Dockerfile .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm64v8
multiarch:
machine:
enabled: true
@ -80,9 +94,10 @@ jobs:
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
#
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
sudo docker manifest create --amend $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
sudo docker manifest create --amend $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 $DOCKERHUB_REPO:$LATEST_TAG-arm64v8
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 --os linux --arch amd64
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 --os linux --arch arm --variant v7
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 --os linux --arch arm64 --variant v8
sudo docker manifest push $DOCKERHUB_REPO:$LATEST_TAG -p
workflows:
@ -114,10 +129,17 @@ workflows:
ignore: /.*/
tags:
only: /(v[1-9]+(\.[0-9]+)*)|(v[a-z]+)/
- multiarch:
requires:
- amd64
- arm32v7
- arm64v8:
filters:
branches:
ignore: /.*/
tags:
only: /(v[1-9]+(\.[0-9]+)*)|(v[a-z]+)/
- multiarch:
requires:
- amd64
- arm32v7
- arm64v8
filters:
branches:
ignore: /.*/

View File

@ -14,6 +14,9 @@ A clear and concise description of what the bug is.
**Logs (if applicable)**
Basic logs can be found in Server Settings > Logs.
**Setup Parameters**
If you're reporting a deployment issue run `. btcpay-setup.sh -i` and paste your the paremeters by obscuring private information.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'

1
.gitignore vendored
View File

@ -293,3 +293,4 @@ BTCPayServer/wwwroot/bundles/*
!BTCPayServer/wwwroot/bundles/.gitignore
.vscode
BTCPayServer/testpwd

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NBitcoin" Version="5.0.29" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,39 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<ApiKeyData> GetCurrentAPIKeyInfo(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current"), token);
return await HandleResponse<ApiKeyData>(response);
}
public virtual async Task<ApiKeyData> CreateAPIKey(CreateApiKeyRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys", bodyPayload: request, method: HttpMethod.Post), token);
return await HandleResponse<ApiKeyData>(response);
}
public virtual async Task RevokeCurrentAPIKeyInfo(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current", null, HttpMethod.Delete), token);
HandleResponse(response);
}
public virtual async Task RevokeAPIKey(string apikey, CancellationToken token = default)
{
if (apikey == null)
throw new ArgumentNullException(nameof(apikey));
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/api-keys/{apikey}", null, HttpMethod.Delete), token);
HandleResponse(response);
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public static Uri GenerateAuthorizeUri(Uri btcpayHost, string[] permissions, bool strict = true,
bool selectiveStores = false)
{
var result = new UriBuilder(btcpayHost);
result.Path = "api-keys/authorize";
AppendPayloadToQuery(result,
new Dictionary<string, object>()
{
{"strict", strict}, {"selectiveStores", selectiveStores}, {"permissions", permissions}
});
return result.Uri;
}
}
}

View File

@ -0,0 +1,23 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<ApplicationUserData> GetCurrentUser(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/users/me"), token);
return await HandleResponse<ApplicationUserData>(response);
}
public virtual async Task<ApplicationUserData> CreateUser(CreateApplicationUserRequest request,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/users", null, request, HttpMethod.Post), token);
return await HandleResponse<ApplicationUserData>(response);
}
}
}

View File

@ -0,0 +1,117 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
private readonly string _apiKey;
private readonly Uri _btcpayHost;
private readonly string _username;
private readonly string _password;
private readonly HttpClient _httpClient;
public string APIKey => _apiKey;
public BTCPayServerClient(Uri btcpayHost, HttpClient httpClient = null)
{
if (btcpayHost == null)
throw new ArgumentNullException(nameof(btcpayHost));
_btcpayHost = btcpayHost;
_httpClient = httpClient ?? new HttpClient();
}
public BTCPayServerClient(Uri btcpayHost, string APIKey, HttpClient httpClient = null)
{
_apiKey = APIKey;
_btcpayHost = btcpayHost;
_httpClient = httpClient ?? new HttpClient();
}
public BTCPayServerClient(Uri btcpayHost, string username, string password, HttpClient httpClient = null)
{
_apiKey = APIKey;
_btcpayHost = btcpayHost;
_username = username;
_password = password;
_httpClient = httpClient ?? new HttpClient();
}
protected void HandleResponse(HttpResponseMessage message)
{
message.EnsureSuccessStatusCode();
}
protected async Task<T> HandleResponse<T>(HttpResponseMessage message)
{
HandleResponse(message);
return JsonConvert.DeserializeObject<T>(await message.Content.ReadAsStringAsync());
}
protected virtual HttpRequestMessage CreateHttpRequest(string path,
Dictionary<string, object> queryPayload = null,
HttpMethod method = null)
{
UriBuilder uriBuilder = new UriBuilder(_btcpayHost) {Path = path};
if (queryPayload != null && queryPayload.Any())
{
AppendPayloadToQuery(uriBuilder, queryPayload);
}
var httpRequest = new HttpRequestMessage(method ?? HttpMethod.Get, uriBuilder.Uri);
if (_apiKey != null)
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("token", _apiKey);
else if (!string.IsNullOrEmpty(_username))
{
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Basic", System.Convert.ToBase64String(Encoding.ASCII.GetBytes(_username + ":" + _password)));
}
return httpRequest;
}
protected virtual HttpRequestMessage CreateHttpRequest<T>(string path,
Dictionary<string, object> queryPayload = null,
T bodyPayload = default, HttpMethod method = null)
{
var request = CreateHttpRequest(path, queryPayload, method);
if (typeof(T).IsPrimitive || !EqualityComparer<T>.Default.Equals(bodyPayload, default(T)))
{
request.Content = new StringContent(JsonConvert.SerializeObject(bodyPayload), Encoding.UTF8, "application/json");
}
return request;
}
private static void AppendPayloadToQuery(UriBuilder uri, Dictionary<string, object> payload)
{
if (uri.Query.Length > 1)
uri.Query += "&";
foreach (KeyValuePair<string, object> keyValuePair in payload)
{
UriBuilder uriBuilder = uri;
if (keyValuePair.Value.GetType().GetInterfaces().Contains((typeof(IEnumerable))))
{
foreach (var item in (IEnumerable)keyValuePair.Value)
{
uriBuilder.Query = uriBuilder.Query + Uri.EscapeDataString(keyValuePair.Key) + "=" +
Uri.EscapeDataString(item.ToString()) + "&";
}
}
else
{
uriBuilder.Query = uriBuilder.Query + Uri.EscapeDataString(keyValuePair.Key) + "=" +
Uri.EscapeDataString(keyValuePair.Value.ToString()) + "&";
}
}
uri.Query = uri.Query.Trim('&');
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using NBitcoin.JsonConverters;
namespace BTCPayServer.Client.JsonConverters
{
public class PermissionJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Permission).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("Type 'Permission' is expected to be a 'String'", reader);
if (reader.Value is String s && Permission.TryParse(s, out var permission))
return permission;
throw new JsonObjectException("Invalid 'Permission' String", reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is Permission v)
writer.WriteValue(v.ToString());
}
}
}

View File

@ -0,0 +1,13 @@
using BTCPayServer.Client.JsonConverters;
using Newtonsoft.Json;
namespace BTCPayServer.Client.Models
{
public class ApiKeyData
{
public string ApiKey { get; set; }
public string Label { get; set; }
[JsonProperty(ItemConverterType = typeof(PermissionJsonConverter))]
public Permission[] Permissions { get; set; }
}
}

View File

@ -0,0 +1,38 @@
namespace BTCPayServer.Client.Models
{
public class ApplicationUserData
{
/// <summary>
/// the id of the user
/// </summary>
public string Id { get; set; }
/// <summary>
/// the email AND username of the user
/// </summary>
public string Email { get; set; }
/// <summary>
/// Whether the user has verified their email
/// </summary>
public bool EmailConfirmed { get; set; }
/// <summary>
/// whether the user needed to verify their email on account creation
/// </summary>
public bool RequiresEmailConfirmation { get; set; }
}
public class CreateApplicationUserRequest
{
/// <summary>
/// the email AND username of the new user
/// </summary>
public string Email { get; set; }
/// <summary>
/// password of the new user
/// </summary>
public string Password { get; set; }
/// <summary>
/// Whether this user is an administrator. If left null and there are no admins in the system, the user will be created as an admin.
/// </summary>
public bool? IsAdministrator { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
using BTCPayServer.Client.JsonConverters;
using Newtonsoft.Json;
namespace BTCPayServer.Client.Models
{
public class CreateApiKeyRequest
{
public string Label { get; set; }
[JsonProperty(ItemConverterType = typeof(PermissionJsonConverter))]
public Permission[] Permissions { get; set; }
}
}

View File

@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace BTCPayServer.Client
{
public class Policies
{
public const string CanModifyServerSettings = "btcpay.server.canmodifyserversettings";
public const string CanModifyStoreSettings = "btcpay.store.canmodifystoresettings";
public const string CanViewStoreSettings = "btcpay.store.canviewstoresettings";
public const string CanCreateInvoice = "btcpay.store.cancreateinvoice";
public const string CanModifyProfile = "btcpay.user.canmodifyprofile";
public const string CanViewProfile = "btcpay.user.canviewprofile";
public const string CanCreateUser = "btcpay.server.cancreateuser";
public const string Unrestricted = "unrestricted";
public static IEnumerable<string> AllPolicies
{
get
{
yield return CanCreateInvoice;
yield return CanModifyServerSettings;
yield return CanModifyStoreSettings;
yield return CanViewStoreSettings;
yield return CanModifyProfile;
yield return CanViewProfile;
yield return CanCreateUser;
yield return Unrestricted;
}
}
public static bool IsValidPolicy(string policy)
{
return AllPolicies.Any(p => p.Equals(policy, StringComparison.OrdinalIgnoreCase));
}
public static bool IsStorePolicy(string policy)
{
return policy.StartsWith("btcpay.store", StringComparison.OrdinalIgnoreCase);
}
public static bool IsServerPolicy(string policy)
{
return policy.StartsWith("btcpay.server", StringComparison.OrdinalIgnoreCase);
}
}
public class Permission
{
public static Permission Create(string policy, string storeId = null)
{
if (TryCreatePermission(policy, storeId, out var r))
return r;
throw new ArgumentException("Invalid Permission");
}
public static bool TryCreatePermission(string policy, string storeId, out Permission permission)
{
permission = null;
if (policy == null)
throw new ArgumentNullException(nameof(policy));
policy = policy.Trim().ToLowerInvariant();
if (!Policies.IsValidPolicy(policy))
return false;
if (storeId != null && !Policies.IsStorePolicy(policy))
return false;
permission = new Permission(policy, storeId);
return true;
}
public static bool TryParse(string str, out Permission permission)
{
permission = null;
if (str == null)
throw new ArgumentNullException(nameof(str));
str = str.Trim();
var separator = str.IndexOf(':');
if (separator == -1)
{
str = str.ToLowerInvariant();
if (!Policies.IsValidPolicy(str))
return false;
permission = new Permission(str, null);
return true;
}
else
{
var policy = str.Substring(0, separator).ToLowerInvariant();
if (!Policies.IsValidPolicy(policy))
return false;
if (!Policies.IsStorePolicy(policy))
return false;
var storeId = str.Substring(separator + 1);
if (storeId.Length == 0)
return false;
permission = new Permission(policy, storeId);
return true;
}
}
internal Permission(string policy, string storeId)
{
Policy = policy;
StoreId = storeId;
}
public bool Contains(Permission subpermission)
{
if (subpermission is null)
throw new ArgumentNullException(nameof(subpermission));
if (!ContainsPolicy(subpermission.Policy))
{
return false;
}
if (!Policies.IsStorePolicy(subpermission.Policy))
return true;
return StoreId == null || subpermission.StoreId == this.StoreId;
}
public static IEnumerable<Permission> ToPermissions(string[] permissions)
{
if (permissions == null)
throw new ArgumentNullException(nameof(permissions));
foreach (var p in permissions)
{
if (TryParse(p, out var pp))
yield return pp;
}
}
private bool ContainsPolicy(string subpolicy)
{
if (this.Policy == Policies.Unrestricted)
return true;
if (this.Policy == subpolicy)
return true;
if (subpolicy == Policies.CanViewStoreSettings && this.Policy == Policies.CanModifyStoreSettings)
return true;
if (subpolicy == Policies.CanCreateInvoice && this.Policy == Policies.CanModifyStoreSettings)
return true;
if (subpolicy == Policies.CanViewProfile && this.Policy == Policies.CanModifyProfile)
return true;
return false;
}
public string StoreId { get; }
public string Policy { get; }
public override string ToString()
{
if (StoreId != null)
{
return $"{Policy}:{StoreId}";
}
return Policy;
}
public override bool Equals(object obj)
{
Permission item = obj as Permission;
if (item == null)
return false;
return ToString().Equals(item.ToString());
}
public static bool operator ==(Permission a, Permission 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 !=(Permission a, Permission b)
{
return !(a == b);
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
}
}

View File

@ -17,7 +17,6 @@ namespace BTCPayServer
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Bitcoin",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://blockstream.info/tx/{0}" : "https://blockstream.info/testnet/tx/{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "bitcoin",
CryptoImagePath = "imlegacy/bitcoin.svg",
@ -25,6 +24,7 @@ namespace BTCPayServer
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == NetworkType.Mainnet ? new KeyPath("0'") : new KeyPath("1'"),
SupportRBF = true,
SupportPayJoin = true,
//https://github.com/spesmilo/electrum/blob/11733d6bc271646a00b69ff07657119598874da4/electrum/constants.py
ElectrumMapping = NetworkType == NetworkType.Mainnet
? new Dictionary<uint, DerivationType>()

View File

@ -12,7 +12,6 @@ namespace BTCPayServer
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "BGold",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://explorer.bitcoingold.org/insight/tx/{0}/" : "https://test-explorer.bitcoingold.org/insight/tx/{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "bitcoingold",
DefaultRateRules = new[]

View File

@ -17,7 +17,6 @@ namespace BTCPayServer
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Bitcoinplus",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://chainz.cryptoid.info/xbc/tx.dws?{0}" : "https://chainz.cryptoid.info/xbc/tx.dws?{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "bitcoinplus",
DefaultRateRules = new[]

View File

@ -17,7 +17,6 @@ namespace BTCPayServer
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Bitcore",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://insight.bitcore.cc/tx/{0}" : "https://insight.bitcore.cc/tx/{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "bitcore",
DefaultRateRules = new[]

View File

@ -15,7 +15,6 @@ namespace BTCPayServer
BlockExplorerLink = NetworkType == NetworkType.Mainnet
? "https://insight.dash.org/insight/tx/{0}"
: "https://testnet-insight.dashevo.org/insight/tx/{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "dash",
DefaultRateRules = new[]

View File

@ -17,7 +17,6 @@ namespace BTCPayServer
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Dogecoin",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://dogechain.info/tx/{0}" : "https://dogechain.info/tx/{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "dogecoin",
DefaultRateRules = new[]

View File

@ -17,7 +17,6 @@ namespace BTCPayServer
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Feathercoin",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://explorer.feathercoin.com/tx/{0}" : "https://explorer.feathercoin.com/tx/{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "feathercoin",
DefaultRateRules = new[]

View File

@ -18,7 +18,6 @@ namespace BTCPayServer
BlockExplorerLink = NetworkType == NetworkType.Mainnet
? "https://chainz.cryptoid.info/grs/tx.dws?{0}.htm"
: "https://chainz.cryptoid.info/grs-test/tx.dws?{0}.htm",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "groestlcoin",
DefaultRateRules = new[]
@ -29,7 +28,9 @@ namespace BTCPayServer
CryptoImagePath = "imlegacy/groestlcoin.png",
LightningImagePath = "imlegacy/groestlcoin-lightning.svg",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == NetworkType.Mainnet ? new KeyPath("17'") : new KeyPath("1'")
CoinType = NetworkType == NetworkType.Mainnet ? new KeyPath("17'") : new KeyPath("1'"),
SupportRBF = true,
SupportPayJoin = true
});
}
}

View File

@ -19,9 +19,13 @@ namespace BTCPayServer
BlockExplorerLink = NetworkType == NetworkType.Mainnet
? "https://live.blockcypher.com/ltc/tx/{0}/"
: "http://explorer.litecointools.com/tx/{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "litecoin",
DefaultRateRules = new[]
{
"LTC_X = LTC_BTC * BTC_X",
"LTC_BTC = coingecko(LTC_BTC)"
},
CryptoImagePath = "imlegacy/litecoin.svg",
LightningImagePath = "imlegacy/litecoin-lightning.svg",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),

View File

@ -17,7 +17,6 @@ namespace BTCPayServer
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Monacoin",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://mona.insight.monaco-ex.org/insight/tx/{0}" : "https://testnet-mona.insight.monaco-ex.org/insight/tx/{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "monacoin",
DefaultRateRules = new[]

View File

@ -16,14 +16,13 @@ namespace BTCPayServer
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Polis",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://insight.polispay.org/tx/{0}" : "https://insight.polispay.org/tx/{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://blockbook.polispay.org/tx/{0}" : "https://blockbook.polispay.org/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "polis",
DefaultRateRules = new[]
{
"POLIS_X = POLIS_BTC * BTC_X",
"POLIS_BTC = cryptopia(POLIS_BTC)"
"POLIS_BTC = polispay(POLIS_BTC)"
},
CryptoImagePath = "imlegacy/polis.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),

View File

@ -17,7 +17,6 @@ namespace BTCPayServer
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Ufo",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://chainz.cryptoid.info/ufo/tx.dws?{0}" : "https://chainz.cryptoid.info/ufo/tx.dws?{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "ufo",
DefaultRateRules = new[]

View File

@ -17,7 +17,6 @@ namespace BTCPayServer
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Viacoin",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://explorer.viacoin.org/tx/{0}" : "https://explorer.viacoin.org/tx/{0}",
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "viacoin",
DefaultRateRules = new[]

View File

@ -47,6 +47,8 @@ namespace BTCPayServer
_NBXplorerNetworkProvider = new NBXplorerNetworkProvider(networkType);
NetworkType = networkType;
InitBitcoin();
InitLiquid();
InitLiquidAssets();
InitLitecoin();
InitBitcore();
InitDogecoin();
@ -57,7 +59,8 @@ namespace BTCPayServer
InitGroestlcoin();
InitViacoin();
InitMonero();
InitPolis();
// Assume that electrum mappings are same as BTC if not specified
foreach (var network in _Networks.Values.OfType<BTCPayNetwork>())
{
@ -75,7 +78,6 @@ namespace BTCPayServer
}
// Disabled because of https://twitter.com/Cryptopia_NZ/status/1085084168852291586
//InitPolis();
//InitBitcoinplus();
//InitUfo();
}
@ -93,6 +95,12 @@ namespace BTCPayServer
[Obsolete("To use only for legacy stuff")]
public BTCPayNetwork BTC => GetNetwork<BTCPayNetwork>("BTC");
public void Add(BTCPayNetwork network)
{
if (network.NBitcoinNetwork == null)
return;
Add(network as BTCPayNetworkBase);
}
public void Add(BTCPayNetworkBase network)
{
_Networks.Add(network.CryptoCode.ToUpperInvariant(), network);
@ -109,7 +117,7 @@ namespace BTCPayServer
}
public BTCPayNetworkBase GetNetwork(string cryptoCode)
{
return GetNetwork<BTCPayNetworkBase>(cryptoCode);
return GetNetwork<BTCPayNetworkBase>(cryptoCode.ToUpperInvariant());
}
public T GetNetwork<T>(string cryptoCode) where T: BTCPayNetworkBase
{

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NBitcoin;
using NBitcoin.Altcoins;
using NBitcoin.Altcoins.Elements;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitLiquid()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("LBTC");
Add(new ElementsBTCPayNetwork()
{
AssetId = NetworkType == NetworkType.Mainnet ? ElementsParams<Liquid>.PeggedAssetId: ElementsParams<Liquid.LiquidRegtest>.PeggedAssetId,
CryptoCode = "LBTC",
NetworkCryptoCode = "LBTC",
DisplayName = "Liquid Bitcoin",
DefaultRateRules = new[]
{
"LBTC_X = LBTC_BTC * BTC_X",
"LBTC_BTC = 1",
},
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://blockstream.info/liquid/tx/{0}" : "https://blockstream.info/testnet/liquid/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "liquidnetwork",
CryptoImagePath = "imlegacy/liquid.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == NetworkType.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
SupportRBF = true
});
}
}
}

View File

@ -0,0 +1,80 @@
using NBitcoin;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitLiquidAssets()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("LBTC");
Add(new ElementsBTCPayNetwork()
{
CryptoCode = "USDt",
NetworkCryptoCode = "LBTC",
ShowSyncSummary = false,
DefaultRateRules = new[]
{
"USDT_UST = 1",
"USDT_X = USDT_BTC * BTC_X",
"USDT_BTC = bitfinex(UST_BTC)",
},
AssetId = new uint256("ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2"),
DisplayName = "Liquid Tether",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://blockstream.info/liquid/tx/{0}" : "https://blockstream.info/testnet/liquid/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "liquidnetwork",
CryptoImagePath = "imlegacy/liquid-tether.svg",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == NetworkType.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
SupportRBF = true
});
Add(new ElementsBTCPayNetwork()
{
CryptoCode = "ETB",
NetworkCryptoCode = "LBTC",
ShowSyncSummary = false,
DefaultRateRules = new[]
{
"ETB_X = ETB_BTC * BTC_X",
"ETB_BTC = bitpay(ETB_BTC)"
},
Divisibility = 2,
AssetId = new uint256("aa775044c32a7df391902b3659f46dfe004ccb2644ce2ddc7dba31e889391caf"),
DisplayName = "Ethiopian Birr",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://blockstream.info/liquid/tx/{0}" : "https://blockstream.info/testnet/liquid/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "liquidnetwork",
CryptoImagePath = "imlegacy/etb.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == NetworkType.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
SupportRBF = true
});
Add(new ElementsBTCPayNetwork()
{
CryptoCode = "LCAD",
NetworkCryptoCode = "LBTC",
ShowSyncSummary = false,
DefaultRateRules = new[]
{
"LCAD_CAD = 1",
"LCAD_X = CAD_BTC * BTC_X",
"LCAD_BTC = bylls(CAD_BTC)",
},
AssetId = new uint256("0e99c1a6da379d1f4151fb9df90449d40d0608f6cb33a5bcbfc8c265f42bab0a"),
DisplayName = "Liquid CAD",
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://blockstream.info/liquid/tx/{0}" : "https://blockstream.info/testnet/liquid/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
UriScheme = "liquidnetwork",
CryptoImagePath = "imlegacy/lcad.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == NetworkType.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
SupportRBF = true
});
}
}
}

View File

@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.Linq;
using NBitcoin;
using NBXplorer;
using NBXplorer.Models;
namespace BTCPayServer
{
public class ElementsBTCPayNetwork : BTCPayNetwork
{
public string NetworkCryptoCode { get; set; }
public uint256 AssetId { get; set; }
public override bool ReadonlyWallet { get; set; } = true;
public override IEnumerable<(MatchedOutput matchedOutput, OutPoint outPoint)> GetValidOutputs(
NewTransactionEvent evtOutputs)
{
return evtOutputs.Outputs.Where(output =>
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId).Select(output =>
{
var outpoint = new OutPoint(evtOutputs.TransactionData.TransactionHash, output.Index);
return (output, outpoint);
});
}
public override string GenerateBIP21(string cryptoInfoAddress, Money cryptoInfoDue)
{
//precision 0: 10 = 0.00000010
//precision 2: 10 = 0.00001000
//precision 8: 10 = 10
var money = new Money(cryptoInfoDue.ToDecimal(MoneyUnit.BTC) / decimal.Parse("1".PadRight(1 + 8 - Divisibility, '0')), MoneyUnit.BTC);
return $"{base.GenerateBIP21(cryptoInfoAddress, money)}&assetid={AssetId}";
}
}
}

View File

@ -10,10 +10,16 @@ namespace BTCPayServer
{
CryptoCode = "XMR",
DisplayName = "Monero",
Divisibility = 12,
BlockExplorerLink =
NetworkType == NetworkType.Mainnet
? "https://www.exploremonero.com/transaction/{0}"
: "https://testnet.xmrchain.net/tx/{0}",
DefaultRateRules = new[]
{
"XMR_X = XMR_BTC * BTC_X",
"XMR_BTC = kraken(XMR_BTC)"
},
CryptoImagePath = "/imlegacy/monero.svg"
});
}

View File

@ -14,7 +14,6 @@ namespace BTCPayServer.Services.Altcoins.Monero.RPC.Models
[JsonProperty("amount")] public long Amount { get; set; }
[JsonProperty("confirmations")] public long Confirmations { get; set; }
[JsonProperty("double_spend_seen")] public bool DoubleSpendSeen { get; set; }
[JsonProperty("fee")] public long Fee { get; set; }
[JsonProperty("height")] public long Height { get; set; }
[JsonProperty("note")] public string Note { get; set; }
[JsonProperty("payment_id")] public string PaymentId { get; set; }

View File

@ -18,7 +18,6 @@ namespace BTCPayServer.Services.Altcoins.Monero.RPC.Models
[JsonProperty("amount")] public long Amount { get; set; }
[JsonProperty("confirmations")] public long Confirmations { get; set; }
[JsonProperty("double_spend_seen")] public bool DoubleSpendSeen { get; set; }
[JsonProperty("fee")] public long Fee { get; set; }
[JsonProperty("height")] public long Height { get; set; }
[JsonProperty("note")] public string Note { get; set; }
[JsonProperty("payment_id")] public string PaymentId { get; set; }

View File

@ -1,10 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using NBitcoin;
using NBXplorer;
using NBXplorer.Models;
using Newtonsoft.Json;
namespace BTCPayServer
@ -46,7 +47,7 @@ namespace BTCPayServer
public class BTCPayNetwork:BTCPayNetworkBase
{
public Network NBitcoinNetwork { get; set; }
public Network NBitcoinNetwork { get { return NBXplorerNetwork?.NBitcoinNetwork; } }
public NBXplorer.NBXplorerNetwork NBXplorerNetwork { get; set; }
public bool SupportRBF { get; internal set; }
public string LightningImagePath { get; set; }
@ -55,8 +56,13 @@ namespace BTCPayServer
public Dictionary<uint, DerivationType> ElectrumMapping = new Dictionary<uint, DerivationType>();
public virtual bool WalletSupported { get; set; } = true;
public virtual bool ReadonlyWallet{ get; set; } = false;
public int MaxTrackedConfirmation { get; internal set; } = 6;
public string UriScheme { get; internal set; }
public bool SupportPayJoin { get; set; } = false;
public KeyPath GetRootKeyPath(DerivationType type)
{
KeyPath baseKey;
@ -100,14 +106,28 @@ namespace BTCPayServer
{
return NBXplorerNetwork.Serializer.ToString(obj);
}
public virtual IEnumerable<(MatchedOutput matchedOutput, OutPoint outPoint)> GetValidOutputs(NewTransactionEvent evtOutputs)
{
return evtOutputs.Outputs.Select(output =>
{
var outpoint = new OutPoint(evtOutputs.TransactionData.TransactionHash, output.Index);
return (output, outpoint);
});
}
public virtual string GenerateBIP21(string cryptoInfoAddress, Money cryptoInfoDue)
{
return $"{UriScheme}:{cryptoInfoAddress}?amount={cryptoInfoDue.ToString(false, true)}";
}
}
public abstract class BTCPayNetworkBase
{
public bool ShowSyncSummary { get; set; } = true;
public string CryptoCode { get; internal set; }
public string BlockExplorerLink { get; internal set; }
public string DisplayName { get; set; }
public int Divisibility { get; set; } = 8;
[Obsolete("Should not be needed")]
public bool IsBTC
{
@ -126,7 +146,7 @@ namespace BTCPayServer
public virtual T ToObject<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json);
return NBitcoin.JsonConverters.Serializer.ToObject<T>(json, null);
}
public virtual string ToString<T>(T obj)

View File

@ -3,8 +3,7 @@
<Import Project="../Build/Common.csproj" />
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.9" Condition="'$(TargetFramework)' == 'netcoreapp2.1'" />
<FrameworkReference Include="Microsoft.AspNetCore.App" Condition="'$(TargetFramework)' != 'netcoreapp2.1'" />
<PackageReference Include="NBXplorer.Client" Version="2.0.0.26" />
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="NBXplorer.Client" Version="3.0.9" />
</ItemGroup>
</Project>

View File

@ -1,4 +1,3 @@
#if !NETCOREAPP21
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
@ -235,25 +234,3 @@ namespace Microsoft.Extensions.Logging.Abstractions.Internal
}
}
}
#else
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Mvc
{
/// <summary>
/// Provides programmatic configuration for JSON formatters using Newtonsoft.JSON.
/// </summary>
public class MvcNewtonsoftJsonOptions
{
IOptions<MvcJsonOptions> jsonOptions;
public MvcNewtonsoftJsonOptions(IOptions<MvcJsonOptions> jsonOptions)
{
this.jsonOptions = jsonOptions;
}
public JsonSerializerSettings SerializerSettings => this.jsonOptions.Value.SerializerSettings;
}
}
#endif

View File

@ -1,19 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../Build/Version.csproj" Condition="Exists('../Build/Version.csproj')" />
<Import Project="../Build/Common.csproj" />
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.1.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.2" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.0.0-alpha1.19515.63" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp2.1'">
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.1" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.0.0-rc1.final" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.0.0-alpha1.19515.63" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.1.2" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.1" />
</ItemGroup>
</Project>

View File

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
@ -11,15 +13,31 @@ namespace BTCPayServer.Data
[MaxLength(50)]
public string Id
{
get; set;
get;
set;
}
[MaxLength(50)]
public string StoreId
{
get; set;
}
[MaxLength(50)] public string StoreId { get; set; }
[MaxLength(50)] public string UserId { get; set; }
public APIKeyType Type { get; set; } = APIKeyType.Legacy;
public byte[] Blob { get; set; }
public StoreData StoreData { get; set; }
public ApplicationUser User { get; set; }
public string Label { get; set; }
}
public class APIKeyBlob
{
public string[] Permissions { get; set; }
}
public enum APIKeyType
{
Legacy,
Permanent
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

View File

@ -2,20 +2,32 @@
using System.Linq;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Infrastructure;
using OpenIddict.EntityFrameworkCore.Models;
namespace BTCPayServer.Data
{
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<ApplicationDbContext>();
builder.UseSqlite("Data Source=temp.db");
return new ApplicationDbContext(builder.Options, true);
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
{
private readonly bool _designTime;
}
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, bool designTime = false)
: base(options)
{
_designTime = designTime;
}
public DbSet<InvoiceData> Invoices
@ -23,6 +35,9 @@ namespace BTCPayServer.Data
get; set;
}
public DbSet<PlannedTransaction> PlannedTransactions { get; set; }
public DbSet<PayjoinLock> PayjoinLocks { get; set; }
public DbSet<AppData> Apps
{
get; set;
@ -33,6 +48,8 @@ namespace BTCPayServer.Data
get; set;
}
public DbSet<OffchainTransactionData> OffchainTransactions { get; set; }
public DbSet<HistoricalAddressInvoiceData> HistoricalAddressInvoices
{
get; set;
@ -147,6 +164,12 @@ namespace BTCPayServer.Data
.HasOne(o => o.StoreData)
.WithMany(i => i.APIKeys)
.HasForeignKey(i => i.StoreId).OnDelete(DeleteBehavior.Cascade);
builder.Entity<APIKeyData>()
.HasOne(o => o.User)
.WithMany(i => i.APIKeys)
.HasForeignKey(i => i.UserId).OnDelete(DeleteBehavior.Cascade);
builder.Entity<APIKeyData>()
.HasIndex(o => o.StoreId);
@ -241,9 +264,27 @@ namespace BTCPayServer.Data
builder.Entity<WalletTransactionData>()
.HasOne(o => o.WalletData)
.WithMany(w => w.WalletTransactions).OnDelete(DeleteBehavior.Cascade);
builder.UseOpenIddict<BTCPayOpenIdClient, BTCPayOpenIdAuthorization, OpenIddictScope<string>, BTCPayOpenIdToken, string>();
if (Database.IsSqlite() && !_designTime)
{
// SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations
// here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations
// To work around this, when the Sqlite database provider is used, all model properties of type DateTimeOffset
// use the DateTimeOffsetToBinaryConverter
// Based on: https://github.com/aspnet/EntityFrameworkCore/issues/10784#issuecomment-415769754
// This only supports millisecond precision, but should be sufficient for most use cases.
foreach (var entityType in builder.Model.GetEntityTypes())
{
var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == typeof(DateTimeOffset));
foreach (var property in properties)
{
builder
.Entity(entityType.Name)
.Property(property.Name)
.HasConversion(new Microsoft.EntityFrameworkCore.Storage.ValueConversion.DateTimeOffsetToBinaryConverter());
}
}
}
}
}

View File

@ -45,11 +45,7 @@ namespace BTCPayServer.Data
class CustomNpgsqlMigrationsSqlGenerator : NpgsqlMigrationsSqlGenerator
{
#if NETCOREAPP21
public CustomNpgsqlMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies) : base(dependencies)
#else
public CustomNpgsqlMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, IMigrationsAnnotationProvider annotations, Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal.INpgsqlOptions opts) : base(dependencies, annotations, opts)
#endif
{
}

View File

@ -20,9 +20,7 @@ namespace BTCPayServer.Data
{
get; set;
}
public List<BTCPayOpenIdClient> OpenIdClients { get; set; }
public List<StoredFile> StoredFiles
{
get;
@ -30,5 +28,6 @@ namespace BTCPayServer.Data
}
public List<U2FDevice> U2FDevices { get; set; }
public List<APIKeyData> APIKeys { get; set; }
}
}

View File

@ -1,6 +0,0 @@
using OpenIddict.EntityFrameworkCore.Models;
namespace BTCPayServer.Data
{
public class BTCPayOpenIdAuthorization : OpenIddictAuthorization<string, BTCPayOpenIdClient, BTCPayOpenIdToken> { }
}

View File

@ -1,10 +0,0 @@
using OpenIddict.EntityFrameworkCore.Models;
namespace BTCPayServer.Data
{
public class BTCPayOpenIdClient: OpenIddictApplication<string, BTCPayOpenIdAuthorization, BTCPayOpenIdToken>
{
public string ApplicationUserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
}
}

View File

@ -1,6 +0,0 @@
using OpenIddict.EntityFrameworkCore.Models;
namespace BTCPayServer.Data
{
public class BTCPayOpenIdToken : OpenIddictToken<string, BTCPayOpenIdClient, BTCPayOpenIdAuthorization> { }
}

View File

@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace BTCPayServer.Data
{
public class OffchainTransactionData
{
[Key]
[MaxLength(32*2)]
public string Id { get; set; }
public byte[] Blob { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace BTCPayServer.Data
{
/// <summary>
/// We represent the locks of the PayjoinRepository
/// with this table. (Both, our utxo we locked as part of a payjoin
/// and the utxo of the payer which were used to pay us)
/// </summary>
public class PayjoinLock
{
[Key]
[MaxLength(100)]
public string Id { get; set; }
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

View File

@ -0,0 +1,15 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace BTCPayServer.Data
{
public class PlannedTransaction
{
[Key]
[MaxLength(100)]
// Id in the format [cryptocode]-[txid]
public string Id { get; set; }
public DateTimeOffset BroadcastAt { get; set; }
public byte[] Blob { get; set; }
}
}

View File

@ -12,11 +12,12 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
ConcurrencyStamp = table.Column<string>(nullable: true),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true)
@ -30,7 +31,7 @@ namespace BTCPayServer.Migrations
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
AccessFailedCount = table.Column<int>(nullable: false),
ConcurrencyStamp = table.Column<string>(nullable: true),
Email = table.Column<string>(maxLength: 256, nullable: true),
@ -55,7 +56,7 @@ namespace BTCPayServer.Migrations
name: "Stores",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
DerivationStrategy = table.Column<string>(nullable: true),
SpeedPolicy = table.Column<int>(nullable: false),
StoreCertificate = table.Column<byte[]>(nullable: true),
@ -75,7 +76,7 @@ namespace BTCPayServer.Migrations
.Annotation("Sqlite:Autoincrement", true),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true),
RoleId = table.Column<string>(nullable: false)
RoleId = table.Column<string>(nullable: false, maxLength: maxLength)
},
constraints: table =>
{
@ -96,7 +97,7 @@ namespace BTCPayServer.Migrations
.Annotation("Sqlite:Autoincrement", true),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false)
UserId = table.Column<string>(nullable: false, maxLength: maxLength)
},
constraints: table =>
{
@ -116,7 +117,7 @@ namespace BTCPayServer.Migrations
LoginProvider = table.Column<string>(nullable: false),
ProviderKey = table.Column<string>(nullable: false),
ProviderDisplayName = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false)
UserId = table.Column<string>(nullable: false, maxLength: maxLength)
},
constraints: table =>
{
@ -133,8 +134,8 @@ namespace BTCPayServer.Migrations
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
RoleId = table.Column<string>(nullable: false)
UserId = table.Column<string>(nullable: false, maxLength: maxLength),
RoleId = table.Column<string>(nullable: false, maxLength: maxLength)
},
constraints: table =>
{
@ -157,7 +158,7 @@ namespace BTCPayServer.Migrations
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
UserId = table.Column<string>(nullable: false, maxLength: maxLength),
LoginProvider = table.Column<string>(nullable: false),
Name = table.Column<string>(nullable: false),
Value = table.Column<string>(nullable: true)
@ -177,7 +178,7 @@ namespace BTCPayServer.Migrations
name: "Invoices",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Blob = table.Column<byte[]>(nullable: true),
Created = table.Column<DateTimeOffset>(nullable: false),
CustomerEmail = table.Column<string>(nullable: true),
@ -185,7 +186,7 @@ namespace BTCPayServer.Migrations
ItemCode = table.Column<string>(nullable: true),
OrderId = table.Column<string>(nullable: true),
Status = table.Column<string>(nullable: true),
StoreDataId = table.Column<string>(nullable: true)
StoreDataId = table.Column<string>(nullable: true, maxLength: maxLength)
},
constraints: table =>
{
@ -202,8 +203,8 @@ namespace BTCPayServer.Migrations
name: "UserStore",
columns: table => new
{
ApplicationUserId = table.Column<string>(nullable: false),
StoreDataId = table.Column<string>(nullable: false),
ApplicationUserId = table.Column<string>(nullable: false, maxLength: maxLength),
StoreDataId = table.Column<string>(nullable: false, maxLength: maxLength),
Role = table.Column<string>(nullable: true)
},
constraints: table =>
@ -227,9 +228,9 @@ namespace BTCPayServer.Migrations
name: "Payments",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Blob = table.Column<byte[]>(nullable: true),
InvoiceDataId = table.Column<string>(nullable: true)
InvoiceDataId = table.Column<string>(nullable: true, maxLength: maxLength)
},
constraints: table =>
{
@ -246,9 +247,9 @@ namespace BTCPayServer.Migrations
name: "RefundAddresses",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Blob = table.Column<byte[]>(nullable: true),
InvoiceDataId = table.Column<string>(nullable: true)
InvoiceDataId = table.Column<string>(nullable: true, maxLength: maxLength)
},
constraints: table =>
{

View File

@ -12,11 +12,12 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "Settings",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Value = table.Column<string>(nullable: true)
},
constraints: table =>

View File

@ -12,6 +12,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<bool>(
name: "RequiresEmailConfirmation",
table: "AspNetUsers",

View File

@ -12,12 +12,13 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "AddressInvoices",
columns: table => new
{
Address = table.Column<string>(nullable: false),
InvoiceDataId = table.Column<string>(nullable: true)
Address = table.Column<string>(nullable: false, maxLength: this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)512 : null),
InvoiceDataId = table.Column<string>(nullable: true, maxLength: maxLength)
},
constraints: table =>
{

View File

@ -12,17 +12,18 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "PairedSINData",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Facade = table.Column<string>(nullable: true),
Label = table.Column<string>(nullable: true),
Name = table.Column<string>(nullable: true),
PairingTime = table.Column<DateTimeOffset>(nullable: false),
SIN = table.Column<string>(nullable: true),
StoreDataId = table.Column<string>(nullable: true)
StoreDataId = table.Column<string>(nullable: true, maxLength: maxLength)
},
constraints: table =>
{
@ -33,14 +34,14 @@ namespace BTCPayServer.Migrations
name: "PairingCodes",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
DateCreated = table.Column<DateTime>(nullable: false),
Expiration = table.Column<DateTimeOffset>(nullable: false),
Facade = table.Column<string>(nullable: true),
Label = table.Column<string>(nullable: true),
Name = table.Column<string>(nullable: true),
SIN = table.Column<string>(nullable: true),
StoreDataId = table.Column<string>(nullable: true),
StoreDataId = table.Column<string>(nullable: true, maxLength: maxLength),
TokenValue = table.Column<string>(nullable: true)
},
constraints: table =>

View File

@ -12,6 +12,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
@ -26,7 +27,7 @@ namespace BTCPayServer.Migrations
name: "PendingInvoices",
columns: table => new
{
Id = table.Column<string>(nullable: false)
Id = table.Column<string>(nullable: false, maxLength: maxLength)
},
constraints: table =>
{

View File

@ -12,6 +12,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<byte[]>(
name: "StoreBlob",
table: "Stores",

View File

@ -12,6 +12,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<DateTimeOffset>(
name: "CreatedTime",
table: "AddressInvoices",
@ -21,7 +22,7 @@ namespace BTCPayServer.Migrations
name: "HistoricalAddressInvoices",
columns: table => new
{
InvoiceDataId = table.Column<string>(nullable: false),
InvoiceDataId = table.Column<string>(nullable: false, maxLength: maxLength),
Address = table.Column<string>(nullable: false),
Assigned = table.Column<DateTimeOffset>(nullable: false),
UnAssigned = table.Column<DateTimeOffset>(nullable: true)

View File

@ -12,6 +12,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<bool>(
name: "Accounted",
table: "Payments",

View File

@ -12,6 +12,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<string>(
name: "CryptoCode",
table: "HistoricalAddressInvoices",

View File

@ -12,6 +12,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<string>(
name: "DerivationStrategies",
table: "Stores",

View File

@ -12,6 +12,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<string>(
name: "DefaultCrypto",
table: "Stores",

View File

@ -12,12 +12,13 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "InvoiceEvents",
columns: table => new
{
InvoiceDataId = table.Column<string>(nullable: false),
UniqueId = table.Column<string>(nullable: false),
InvoiceDataId = table.Column<string>(nullable: false, maxLength: maxLength),
UniqueId = table.Column<string>(nullable: false, maxLength: maxLength),
Message = table.Column<string>(nullable: true),
Timestamp = table.Column<DateTimeOffset>(nullable: false)
},

View File

@ -12,16 +12,17 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "Apps",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
AppType = table.Column<string>(nullable: true),
Created = table.Column<DateTimeOffset>(nullable: false),
Name = table.Column<string>(nullable: true),
Settings = table.Column<string>(nullable: true),
StoreDataId = table.Column<string>(nullable: true)
StoreDataId = table.Column<string>(nullable: true, maxLength: maxLength)
},
constraints: table =>
{

View File

@ -12,6 +12,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "ApiKeys",
columns: table => new

View File

@ -10,6 +10,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
if (this.SupportDropForeignKey(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropForeignKey(

View File

@ -11,12 +11,13 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "PaymentRequests",
columns: table => new
{
Id = table.Column<string>(nullable: false),
StoreDataId = table.Column<string>(nullable: true),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
StoreDataId = table.Column<string>(nullable: true, maxLength: maxLength),
Status = table.Column<int>(nullable: false),
Blob = table.Column<byte[]>(nullable: true)
},

View File

@ -10,6 +10,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<bool>(
name: "TagAllInvoices",
table: "Apps",

View File

@ -11,6 +11,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "OpenIddictApplications",
columns: table => new
@ -20,13 +21,13 @@ namespace BTCPayServer.Migrations
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
ConsentType = table.Column<string>(nullable: true),
DisplayName = table.Column<string>(nullable: true),
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Permissions = table.Column<string>(nullable: true),
PostLogoutRedirectUris = table.Column<string>(nullable: true),
Properties = table.Column<string>(nullable: true),
RedirectUris = table.Column<string>(nullable: true),
Type = table.Column<string>(maxLength: 25, nullable: false),
ApplicationUserId = table.Column<string>(nullable: true)
ApplicationUserId = table.Column<string>(nullable: true, maxLength: maxLength)
},
constraints: table =>
{
@ -46,7 +47,7 @@ namespace BTCPayServer.Migrations
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
Description = table.Column<string>(nullable: true),
DisplayName = table.Column<string>(nullable: true),
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Name = table.Column<string>(maxLength: 200, nullable: false),
Properties = table.Column<string>(nullable: true),
Resources = table.Column<string>(nullable: true)
@ -60,9 +61,9 @@ namespace BTCPayServer.Migrations
name: "OpenIddictAuthorizations",
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true),
ApplicationId = table.Column<string>(nullable: true, maxLength: maxLength),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Properties = table.Column<string>(nullable: true),
Scopes = table.Column<string>(nullable: true),
Status = table.Column<string>(maxLength: 25, nullable: false),
@ -84,12 +85,12 @@ namespace BTCPayServer.Migrations
name: "OpenIddictTokens",
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true),
AuthorizationId = table.Column<string>(nullable: true),
ApplicationId = table.Column<string>(nullable: true, maxLength: maxLength),
AuthorizationId = table.Column<string>(nullable: true, maxLength: maxLength),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
CreationDate = table.Column<DateTimeOffset>(nullable: true),
ExpirationDate = table.Column<DateTimeOffset>(nullable: true),
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Payload = table.Column<string>(nullable: true),
Properties = table.Column<string>(nullable: true),
ReferenceId = table.Column<string>(maxLength: 100, nullable: true),

View File

@ -11,15 +11,16 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "Files",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
FileName = table.Column<string>(nullable: true),
StorageFileName = table.Column<string>(nullable: true),
Timestamp = table.Column<DateTime>(nullable: false),
ApplicationUserId = table.Column<string>(nullable: true)
ApplicationUserId = table.Column<string>(nullable: true, maxLength: maxLength)
},
constraints: table =>
{

View File

@ -11,6 +11,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
@ -22,13 +23,13 @@ namespace BTCPayServer.Migrations
name: "U2FDevices",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Name = table.Column<string>(nullable: true),
KeyHandle = table.Column<byte[]>(nullable: false),
PublicKey = table.Column<byte[]>(nullable: false),
AttestationCert = table.Column<byte[]>(nullable: false),
Counter = table.Column<int>(nullable: false),
ApplicationUserId = table.Column<string>(nullable: true)
ApplicationUserId = table.Column<string>(nullable: true, maxLength: maxLength)
},
constraints: table =>
{

View File

@ -11,6 +11,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<DateTimeOffset>(
name: "Created",
table: "PaymentRequests",

View File

@ -11,11 +11,12 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "Wallets",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Blob = table.Column<byte[]>(nullable: true)
},
constraints: table =>
@ -27,8 +28,8 @@ namespace BTCPayServer.Migrations
name: "WalletTransactions",
columns: table => new
{
WalletDataId = table.Column<string>(nullable: false),
TransactionId = table.Column<string>(nullable: false),
WalletDataId = table.Column<string>(nullable: false, maxLength: maxLength),
TransactionId = table.Column<string>(nullable: false, maxLength: maxLength),
Labels = table.Column<string>(nullable: true),
Blob = table.Column<byte[]>(nullable: true)
},

View File

@ -0,0 +1,244 @@
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20200110064617_OpenIddictUpdate")]
public partial class OpenIddictUpdate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (!migrationBuilder.IsSqlite())
{
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictTokens",
maxLength: 450,
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 450);
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictAuthorizations",
maxLength: 450,
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 450);
}
else
{
ReplaceOldTable(migrationBuilder, s =>
{
migrationBuilder.CreateTable(
name: s,
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true, maxLength: null),
AuthorizationId = table.Column<string>(nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
CreationDate = table.Column<DateTimeOffset>(nullable: true),
ExpirationDate = table.Column<DateTimeOffset>(nullable: true),
Id = table.Column<string>(nullable: false, maxLength: null),
Payload = table.Column<string>(nullable: true),
Properties = table.Column<string>(nullable: true),
ReferenceId = table.Column<string>(maxLength: 100, nullable: true),
Status = table.Column<string>(maxLength: 25, nullable: false),
Subject = table.Column<string>(maxLength: 450, nullable: true),
Type = table.Column<string>(maxLength: 25, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictTokens", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId",
column: x => x.AuthorizationId,
principalTable: "OpenIddictAuthorizations",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
}, "OpenIddictTokens");
ReplaceOldTable(migrationBuilder, s =>
{
migrationBuilder.CreateTable(
name: s,
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
Id = table.Column<string>(nullable: false, maxLength: null),
Properties = table.Column<string>(nullable: true),
Scopes = table.Column<string>(nullable: true),
Status = table.Column<string>(maxLength: 25, nullable: false),
Subject = table.Column<string>(maxLength: 450, nullable: true),
Type = table.Column<string>(maxLength: 25, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictAuthorizations_OpenIddictApplications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
}, "OpenIddictAuthorizations");
}
migrationBuilder.AddColumn<string>(
name: "Requirements",
table: "OpenIddictApplications",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
if (!migrationBuilder.IsSqlite())
{
migrationBuilder.DropColumn(
name: "Requirements",
table: "OpenIddictApplications");
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictTokens",
maxLength: 450,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 450,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictAuthorizations",
maxLength: 450,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 450,
oldNullable: true);
}
else
{
ReplaceOldTable(migrationBuilder, s =>
{
migrationBuilder.CreateTable(
name: s,
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true, maxLength: null),
AuthorizationId = table.Column<string>(nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
CreationDate = table.Column<DateTimeOffset>(nullable: true),
ExpirationDate = table.Column<DateTimeOffset>(nullable: true),
Id = table.Column<string>(nullable: false, maxLength: null),
Payload = table.Column<string>(nullable: true),
Properties = table.Column<string>(nullable: true),
ReferenceId = table.Column<string>(maxLength: 100, nullable: true),
Status = table.Column<string>(maxLength: 25, nullable: false),
Subject = table.Column<string>(maxLength: 450, nullable: false),
Type = table.Column<string>(maxLength: 25, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictTokens", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId",
column: x => x.AuthorizationId,
principalTable: "OpenIddictAuthorizations",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
}, "OpenIddictTokens", "WHERE Subject IS NOT NULL");
ReplaceOldTable(migrationBuilder, s =>
{
migrationBuilder.CreateTable(
name: s,
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
Id = table.Column<string>(nullable: false, maxLength: null),
Properties = table.Column<string>(nullable: true),
Scopes = table.Column<string>(nullable: true),
Status = table.Column<string>(maxLength: 25, nullable: false),
Subject = table.Column<string>(maxLength: 450, nullable: false),
Type = table.Column<string>(maxLength: 25, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictAuthorizations_OpenIddictApplications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
}, "OpenIddictAuthorizations", "WHERE Subject IS NOT NULL");
ReplaceOldTable(migrationBuilder, s =>
{
migrationBuilder.CreateTable(
name: s,
columns: table => new
{
ClientId = table.Column<string>(maxLength: 100, nullable: false),
ClientSecret = table.Column<string>(nullable: true),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
ConsentType = table.Column<string>(nullable: true),
DisplayName = table.Column<string>(nullable: true),
Id = table.Column<string>(nullable: false, maxLength: null),
Permissions = table.Column<string>(nullable: true),
PostLogoutRedirectUris = table.Column<string>(nullable: true),
Properties = table.Column<string>(nullable: true),
RedirectUris = table.Column<string>(nullable: true),
Type = table.Column<string>(maxLength: 25, nullable: false),
ApplicationUserId = table.Column<string>(nullable: true, maxLength: null)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictApplications", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictApplications_AspNetUsers_ApplicationUserId",
column: x => x.ApplicationUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
}, "OpenIddictApplications", "",
"ClientId, ClientSecret, ConcurrencyToken, ConsentType, DisplayName, Id, Permissions, PostLogoutRedirectUris, Properties, RedirectUris, Type, ApplicationUserId");
}
}
private void ReplaceOldTable(MigrationBuilder migrationBuilder, Action<string> createTable, string tableName,
string whereClause = "", string columns = "*")
{
createTable.Invoke($"New_{tableName}");
migrationBuilder.Sql(
$"INSERT INTO New_{tableName} {(columns == "*" ? string.Empty : $"({columns})")}SELECT {columns} FROM {tableName} {whereClause};");
migrationBuilder.Sql("PRAGMA foreign_keys=\"0\"", true);
migrationBuilder.Sql($"DROP TABLE {tableName}", true);
migrationBuilder.Sql($"ALTER TABLE New_{tableName} RENAME TO {tableName}", true);
migrationBuilder.Sql("PRAGMA foreign_keys=\"1\"", true);
}
}
}

View File

@ -0,0 +1,74 @@
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20200119130108_ExtendApiKeys")]
public partial class ExtendApiKeys : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Permissions",
table: "ApiKeys",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "Type",
table: "ApiKeys",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<string>(
name: "UserId",
table: "ApiKeys",
maxLength: 50,
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_ApiKeys_UserId",
table: "ApiKeys",
column: "UserId");
if (this.SupportAddForeignKey(migrationBuilder.ActiveProvider))
{
migrationBuilder.AddForeignKey(
name: "FK_ApiKeys_AspNetUsers_UserId",
table: "ApiKeys",
column: "UserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
protected override void Down(MigrationBuilder migrationBuilder)
{
if (this.SupportDropForeignKey(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropForeignKey(
name: "FK_ApiKeys_AspNetUsers_UserId",
table: "ApiKeys");
}
migrationBuilder.DropIndex(
name: "IX_ApiKeys_UserId",
table: "ApiKeys");
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
name: "Permissions",
table: "ApiKeys");
migrationBuilder.DropColumn(
name: "Type",
table: "ApiKeys");
migrationBuilder.DropColumn(
name: "UserId",
table: "ApiKeys");
}
}
}
}

View File

@ -0,0 +1,173 @@
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20200224134444_Remove_OpenIddict")]
public partial class Remove_OpenIddict : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "OpenIddictScopes");
migrationBuilder.DropTable(
name: "OpenIddictTokens");
migrationBuilder.DropTable(
name: "OpenIddictAuthorizations");
migrationBuilder.DropTable(
name: "OpenIddictApplications");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "OpenIddictApplications",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: maxLength),
ApplicationUserId = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxLength),
ClientId = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
ClientSecret = table.Column<string>(type: "TEXT", nullable: true),
ConcurrencyToken = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
ConsentType = table.Column<string>(type: "TEXT", nullable: true),
DisplayName = table.Column<string>(type: "TEXT", nullable: true),
Permissions = table.Column<string>(type: "TEXT", nullable: true),
PostLogoutRedirectUris = table.Column<string>(type: "TEXT", nullable: true),
Properties = table.Column<string>(type: "TEXT", nullable: true),
RedirectUris = table.Column<string>(type: "TEXT", nullable: true),
Requirements = table.Column<string>(type: "TEXT", nullable: true),
Type = table.Column<string>(type: "TEXT", maxLength: 25, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictApplications", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictApplications_AspNetUsers_ApplicationUserId",
column: x => x.ApplicationUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "OpenIddictScopes",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: maxLength),
ConcurrencyToken = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
Description = table.Column<string>(type: "TEXT", nullable: true),
DisplayName = table.Column<string>(type: "TEXT", nullable: true),
Name = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
Properties = table.Column<string>(type: "TEXT", nullable: true),
Resources = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictScopes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "OpenIddictAuthorizations",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: maxLength),
ApplicationId = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxLength),
ConcurrencyToken = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
Properties = table.Column<string>(type: "TEXT", nullable: true),
Scopes = table.Column<string>(type: "TEXT", nullable: true),
Status = table.Column<string>(type: "TEXT", maxLength: 25, nullable: false),
Subject = table.Column<string>(type: "TEXT", maxLength: 450, nullable: true),
Type = table.Column<string>(type: "TEXT", maxLength: 25, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictAuthorizations_OpenIddictApplications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "OpenIddictTokens",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: maxLength),
ApplicationId = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxLength),
AuthorizationId = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxLength),
ConcurrencyToken = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
CreationDate = table.Column<DateTimeOffset>(type: "TEXT", nullable: true),
ExpirationDate = table.Column<DateTimeOffset>(type: "TEXT", nullable: true),
Payload = table.Column<string>(type: "TEXT", nullable: true),
Properties = table.Column<string>(type: "TEXT", nullable: true),
ReferenceId = table.Column<string>(type: "TEXT", maxLength: 100, nullable: true),
Status = table.Column<string>(type: "TEXT", maxLength: 25, nullable: false),
Subject = table.Column<string>(type: "TEXT", maxLength: 450, nullable: true),
Type = table.Column<string>(type: "TEXT", maxLength: 25, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictTokens", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId",
column: x => x.AuthorizationId,
principalTable: "OpenIddictAuthorizations",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_OpenIddictApplications_ApplicationUserId",
table: "OpenIddictApplications",
column: "ApplicationUserId");
migrationBuilder.CreateIndex(
name: "IX_OpenIddictApplications_ClientId",
table: "OpenIddictApplications",
column: "ClientId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type",
table: "OpenIddictAuthorizations",
columns: new[] { "ApplicationId", "Status", "Subject", "Type" });
migrationBuilder.CreateIndex(
name: "IX_OpenIddictScopes_Name",
table: "OpenIddictScopes",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_OpenIddictTokens_AuthorizationId",
table: "OpenIddictTokens",
column: "AuthorizationId");
migrationBuilder.CreateIndex(
name: "IX_OpenIddictTokens_ReferenceId",
table: "OpenIddictTokens",
column: "ReferenceId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type",
table: "OpenIddictTokens",
columns: new[] { "ApplicationId", "Status", "Subject", "Type" });
}
}
}

View File

@ -0,0 +1,27 @@
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20200225133433_AddApiKeyLabel")]
public partial class AddApiKeyLabel : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Label",
table: "ApiKeys",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Label",
table: "ApiKeys");
}
}
}

View File

@ -0,0 +1,42 @@
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20200402065615_AddApiKeyBlob")]
public partial class AddApiKeyBlob : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
name: "Permissions",
table: "ApiKeys");
}
migrationBuilder.AddColumn<byte[]>(
name: "Blob",
table: "ApiKeys",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
name: "Blob",
table: "ApiKeys");
}
migrationBuilder.AddColumn<string>(
name: "Permissions",
table: "ApiKeys",
type: "TEXT",
nullable: true);
}
}
}

View File

@ -0,0 +1,59 @@
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20200413052418_PlannedTransactions")]
public partial class PlannedTransactions : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PlannedTransactions",
columns: table => new
{
Id = table.Column<string>(maxLength: 100, nullable: false),
BroadcastAt = table.Column<DateTimeOffset>(nullable: false),
Blob = table.Column<byte[]>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PlannedTransactions", x => x.Id);
});
migrationBuilder.CreateTable(
name: "PayjoinLocks",
columns: table => new
{
Id = table.Column<string>(maxLength: 100, nullable: false),
},
constraints: table =>
{
table.PrimaryKey("PK_PayjoinLocks", x => x.Id);
});
migrationBuilder.CreateTable(
name: "OffchainTransactions",
columns: table => new
{
Id = table.Column<string>(maxLength: 64, nullable: false),
Blob = table.Column<byte[]>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OffchainTransactions", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PayjoinLocks");
migrationBuilder.DropTable(
name: "PlannedTransactions");
migrationBuilder.DropTable(
name: "OffchainTransactions");
}
}
}

View File

@ -11,7 +11,10 @@ namespace BTCPayServer.Migrations
{
return activeProvider != "Microsoft.EntityFrameworkCore.Sqlite";
}
public static bool SupportAddForeignKey(this Microsoft.EntityFrameworkCore.Migrations.Migration migration, string activeProvider)
{
return activeProvider != "Microsoft.EntityFrameworkCore.Sqlite";
}
public static bool SupportDropForeignKey(this Microsoft.EntityFrameworkCore.Migrations.Migration migration, string activeProvider)
{
return activeProvider != "Microsoft.EntityFrameworkCore.Sqlite";
@ -20,5 +23,9 @@ namespace BTCPayServer.Migrations
{
return facade.ProviderName != "Microsoft.EntityFrameworkCore.Sqlite";
}
public static bool IsMySql(this Microsoft.EntityFrameworkCore.Migrations.Migration migration, string activeProvider)
{
return activeProvider == "Pomelo.EntityFrameworkCore.MySql";
}
}
}

View File

@ -0,0 +1,29 @@
namespace BTCPayServer.Rating
{
public enum RateSource
{
Coingecko,
Direct
}
public class AvailableRateProvider
{
public string Name { get; }
public string Url { get; }
public string Id { get; }
public string SourceId { get; }
public RateSource Source { get; }
public AvailableRateProvider(string id, string name, string url) : this(id, id, name, url, RateSource.Direct)
{
}
public AvailableRateProvider(string id, string sourceId, string name, string url, RateSource source)
{
Id = id;
SourceId = sourceId;
Name = name;
Url = url;
Source = source;
}
}
}

View File

@ -3,15 +3,10 @@
<Import Project="../Build/Common.csproj" />
<ItemGroup>
<Folder Include="Providers\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.9" Condition="'$(TargetFramework)' == 'netcoreapp2.1'" />
<FrameworkReference Include="Microsoft.AspNetCore.App" Condition="'$(TargetFramework)' != 'netcoreapp2.1'" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.3.1" Condition="'$(TargetFramework)' != 'netcoreapp2.1'" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" Condition="'$(TargetFramework)' != 'netcoreapp2.1'" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.4.0" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.6.3" />
</ItemGroup>

View File

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BTCPayServer.Rating
{

View File

@ -3,7 +3,6 @@ using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
namespace BTCPayServer.Rating
{
@ -12,7 +11,15 @@ namespace BTCPayServer.Rating
Dictionary<string, ExchangeRate> _AllRates = new Dictionary<string, ExchangeRate>();
public ExchangeRates()
{
}
public ExchangeRates(string exchangeName, IEnumerable<PairRate> rates)
{
foreach (var rate in rates)
{
Add(new ExchangeRate(exchangeName, rate.CurrencyPair, rate.BidAsk));
}
}
public ExchangeRates(IEnumerable<ExchangeRate> rates)
{
@ -219,6 +226,26 @@ namespace BTCPayServer.Rating
return $"({Bid.ToString(CultureInfo.InvariantCulture)} , {Ask.ToString(CultureInfo.InvariantCulture)})";
}
}
public class PairRate
{
public PairRate(CurrencyPair currencyPair, BidAsk bidAsk)
{
if (currencyPair == null)
throw new ArgumentNullException(nameof(currencyPair));
if (bidAsk == null)
throw new ArgumentNullException(nameof(bidAsk));
this.CurrencyPair = currencyPair;
this.BidAsk = bidAsk;
}
public CurrencyPair CurrencyPair { get; }
public BidAsk BidAsk { get; }
public override string ToString()
{
return $"{CurrencyPair} == {BidAsk}";
}
}
public class ExchangeRate
{
public ExchangeRate()

View File

@ -8,20 +8,65 @@ using BTCPayServer.Rating;
using System.Threading;
using Microsoft.Extensions.Logging.Abstractions;
using BTCPayServer.Logging;
using Newtonsoft.Json;
using System.Reflection;
using System.Globalization;
namespace BTCPayServer.Services.Rates
{
public class BackgroundFetcherState
{
public string ExchangeName { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? LastRequested { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? LastUpdated { get; set; }
[JsonProperty(ItemConverterType = typeof(BackgroundFetcherRateJsonConverter))]
public List<BackgroundFetcherRate> Rates { get; set; }
}
public class BackgroundFetcherRate
{
public CurrencyPair Pair { get; set; }
public BidAsk BidAsk { get; set; }
}
//This make the json more compact
class BackgroundFetcherRateJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(BackgroundFetcherRate).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var value = (string)reader.Value;
var parts = value.Split('|');
return new BackgroundFetcherRate()
{
Pair = CurrencyPair.Parse(parts[0]),
BidAsk = new BidAsk(decimal.Parse(parts[1], CultureInfo.InvariantCulture), decimal.Parse(parts[2], CultureInfo.InvariantCulture))
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var rate = (BackgroundFetcherRate)value;
writer.WriteValue($"{rate.Pair}|{rate.BidAsk.Bid.ToString(CultureInfo.InvariantCulture)}|{rate.BidAsk.Ask.ToString(CultureInfo.InvariantCulture)}");
}
}
/// <summary>
/// This class is a decorator which handle caching and pre-emptive query to the underlying rate provider
/// </summary>
public class BackgroundFetcherRateProvider : IRateProvider
{
public class LatestFetch
{
public ExchangeRates Latest;
public PairRate[] Latest;
public DateTimeOffset NextRefresh;
public TimeSpan Backoff = TimeSpan.FromSeconds(5.0);
public TimeSpan Backoff = TimeSpan.FromSeconds(5.0);
public DateTimeOffset Updated;
public DateTimeOffset Expiration;
public Exception Exception;
public string ExchangeName;
internal ExchangeRates GetResult()
internal PairRate[] GetResult()
{
if (Expiration <= DateTimeOffset.UtcNow)
{
@ -31,7 +76,7 @@ namespace BTCPayServer.Services.Rates
}
else
{
throw new InvalidOperationException($"The rate has expired ({ExchangeName})");
throw new InvalidOperationException($"The rate has expired");
}
}
return Latest;
@ -39,6 +84,7 @@ namespace BTCPayServer.Services.Rates
}
IRateProvider _Inner;
public IRateProvider Inner => _Inner;
public BackgroundFetcherRateProvider(IRateProvider inner)
{
@ -47,7 +93,46 @@ namespace BTCPayServer.Services.Rates
_Inner = inner;
}
public BackgroundFetcherState GetState()
{
var state = new BackgroundFetcherState()
{
LastRequested = LastRequested
};
if (_Latest is LatestFetch fetch && fetch.Latest is PairRate[])
{
state.LastUpdated = fetch.Updated;
state.Rates = fetch.Latest
.Select(r => new BackgroundFetcherRate()
{
Pair = r.CurrencyPair,
BidAsk = r.BidAsk
}).ToList();
}
return state;
}
public void LoadState(BackgroundFetcherState state)
{
if (state.LastRequested is DateTimeOffset lastRequested)
this.LastRequested = state.LastRequested;
if (state.LastUpdated is DateTimeOffset updated && state.Rates is List<BackgroundFetcherRate> rates)
{
var fetch = new LatestFetch()
{
Latest = rates.Select(r => new PairRate(r.Pair, r.BidAsk)).ToArray(),
Updated = updated,
NextRefresh = updated + RefreshRate,
Expiration = updated + ValidatyTime
};
_Latest = fetch;
}
}
TimeSpan _RefreshRate = TimeSpan.FromSeconds(30);
/// <summary>
/// The timespan after which <see cref="UpdateIfNecessary(CancellationToken)"/> will get the rates from the underlying rate provider
/// </summary>
public TimeSpan RefreshRate
{
get
@ -65,6 +150,9 @@ namespace BTCPayServer.Services.Rates
}
TimeSpan _ValidatyTime = TimeSpan.FromMinutes(10);
/// <summary>
/// The timespan after which calls to <see cref="GetRatesAsync(CancellationToken)"/> will query underlying provider if the rate has not been updated
/// </summary>
public TimeSpan ValidatyTime
{
get
@ -110,35 +198,46 @@ namespace BTCPayServer.Services.Rates
}
LatestFetch _Latest;
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
{
LastRequested = DateTimeOffset.UtcNow;
var latest = _Latest;
if (!DoNotAutoFetchIfExpired && latest != null && latest.Expiration <= DateTimeOffset.UtcNow + TimeSpan.FromSeconds(1.0))
{
Logs.PayServer.LogWarning($"GetRatesAsync was called on {GetExchangeName()} when the rate is outdated. It should never happen, let BTCPayServer developers know about this.");
latest = null;
}
return (latest ?? (await Fetch(cancellationToken))).GetResult();
}
private string GetExchangeName()
/// <summary>
/// The last time this rate provider has been used
/// </summary>
public DateTimeOffset? LastRequested { get; set; }
public DateTimeOffset? Expiration
{
if (_Inner is IHasExchangeName exchangeName)
return exchangeName.ExchangeName ?? "???";
return "???";
get
{
if (_Latest is LatestFetch f)
{
return f.Expiration;
}
return null;
}
}
private async Task<LatestFetch> Fetch(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var previous = _Latest;
var fetch = new LatestFetch();
fetch.ExchangeName = GetExchangeName();
try
{
var rates = await _Inner.GetRatesAsync(cancellationToken);
fetch.Latest = rates;
fetch.Expiration = DateTimeOffset.UtcNow + ValidatyTime;
fetch.NextRefresh = DateTimeOffset.UtcNow + RefreshRate;
fetch.Updated = DateTimeOffset.UtcNow;
fetch.Expiration = fetch.Updated + ValidatyTime;
fetch.NextRefresh = fetch.Updated + RefreshRate;
}
catch (Exception ex)
{

View File

@ -9,24 +9,22 @@ using Newtonsoft.Json.Linq;
namespace BTCPayServer.Services.Rates
{
public class BitbankRateProvider : IRateProvider, IHasExchangeName
public class BitbankRateProvider : IRateProvider
{
private readonly HttpClient _httpClient;
public BitbankRateProvider(HttpClient httpClient)
{
_httpClient = httpClient ?? new HttpClient();
}
public string ExchangeName => "bitbank";
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
{
var response = await _httpClient.GetAsync("https://public.bitbank.cc/prices", cancellationToken);
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
return new ExchangeRates(((jobj["data"] as JObject) ?? new JObject())
return ((jobj["data"] as JObject) ?? new JObject())
.Properties()
.Select(p => new ExchangeRate(ExchangeName, CurrencyPair.Parse(p.Name), CreateBidAsk(p)))
.ToArray());
.Select(p => new PairRate(CurrencyPair.Parse(p.Name), CreateBidAsk(p)))
.ToArray();
}
private static BidAsk CreateBidAsk(JProperty p)

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Rating;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Services.Rates
{
public class BitflyerRateProvider : IRateProvider
{
private readonly HttpClient _httpClient;
public BitflyerRateProvider(HttpClient httpClient)
{
_httpClient = httpClient ?? new HttpClient();
}
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
{
var response = await _httpClient.GetAsync("https://api.bitflyer.jp/v1/ticker", cancellationToken);
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
if (jobj.Property("error_message")?.Value?.Value<string>() is string err)
{
throw new Exception($"Error from bitflyer: {err}");
}
var bid = jobj.Property("best_bid").Value.Value<decimal>();
var ask = jobj.Property("best_ask").Value.Value<decimal>();
var rates = new PairRate[1];
rates[0] = new PairRate(CurrencyPair.Parse(jobj.Property("product_code").Value.Value<string>()), new BidAsk(bid, ask));
return rates;
}
}
}

View File

@ -10,26 +10,23 @@ using Newtonsoft.Json.Linq;
namespace BTCPayServer.Services.Rates
{
public class BitpayRateProvider : IRateProvider, IHasExchangeName
public class BitpayRateProvider : IRateProvider
{
public const string BitpayName = "bitpay";
private readonly HttpClient _httpClient;
public BitpayRateProvider(HttpClient httpClient)
{
_httpClient = httpClient ?? new HttpClient();
}
public string ExchangeName => BitpayName;
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
{
var response = await _httpClient.GetAsync("https://bitpay.com/rates", cancellationToken);
var jarray = (JArray)(await response.Content.ReadAsAsync<JObject>(cancellationToken))["data"];
return new ExchangeRates(jarray
return jarray
.Children<JObject>()
.Select(jobj => new ExchangeRate(ExchangeName, new CurrencyPair("BTC", jobj["code"].Value<string>()), new BidAsk(jobj["rate"].Value<decimal>())))
.Select(jobj => new PairRate(new CurrencyPair("BTC", jobj["code"].Value<string>()), new BidAsk(jobj["rate"].Value<decimal>())))
.Where(o => o.CurrencyPair.Right != "BTC")
.ToArray());
.ToArray();
}
}
}

View File

@ -7,21 +7,20 @@ using Newtonsoft.Json.Linq;
namespace BTCPayServer.Services.Rates
{
public class ByllsRateProvider : IRateProvider, IHasExchangeName
public class ByllsRateProvider : IRateProvider
{
private readonly HttpClient _httpClient;
public ByllsRateProvider(HttpClient httpClient)
{
_httpClient = httpClient ?? new HttpClient();
}
public string ExchangeName => "bylls";
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
{
var response = await _httpClient.GetAsync("https://bylls.com/api/price?from_currency=BTC&to_currency=CAD", cancellationToken);
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
var value = jobj["public_price"]["to_price"].Value<decimal>();
return new ExchangeRates(new[] { new ExchangeRate(ExchangeName, new CurrencyPair("BTC", "CAD"), new BidAsk(value)) });
return new[] { new PairRate(new CurrencyPair("BTC", "CAD"), new BidAsk(value)) };
}
}
}

View File

@ -1,53 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Rating;
using BTCPayServer.Services.Rates;
using Microsoft.Extensions.Caching.Memory;
namespace BTCPayServer.Services.Rates
{
public class CachedRateProvider : IRateProvider, IHasExchangeName
{
private IRateProvider _Inner;
private IMemoryCache _MemoryCache;
public CachedRateProvider(string exchangeName, IRateProvider inner, IMemoryCache memoryCache)
{
if (inner == null)
throw new ArgumentNullException(nameof(inner));
if (memoryCache == null)
throw new ArgumentNullException(nameof(memoryCache));
this._Inner = inner;
this.MemoryCache = memoryCache;
this.ExchangeName = exchangeName;
}
public IRateProvider Inner
{
get
{
return _Inner;
}
}
public string ExchangeName { get; }
public TimeSpan CacheSpan
{
get;
set;
} = TimeSpan.FromMinutes(1.0);
public IMemoryCache MemoryCache { get => _MemoryCache; set => _MemoryCache = value; }
public Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
{
return MemoryCache.GetOrCreateAsync("EXCHANGE_RATES_" + ExchangeName, (ICacheEntry entry) =>
{
entry.AbsoluteExpiration = DateTimeOffset.UtcNow + CacheSpan;
return _Inner.GetRatesAsync(cancellationToken);
});
}
}
}

View File

@ -1,238 +0,0 @@
using Newtonsoft.Json;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using BTCPayServer.Rating;
using System.Threading;
namespace BTCPayServer.Services.Rates
{
public class CoinAverageException : Exception
{
public CoinAverageException(string message) : base(message)
{
}
}
public class GetExchangeTickersResponse
{
public class Exchange
{
public string Name { get; set; }
[JsonProperty("display_name")]
public string DisplayName { get; set; }
public string[] Symbols { get; set; }
}
public bool Success { get; set; }
public Exchange[] Exchanges { get; set; }
}
public class RatesSetting
{
public string PublicKey { get; set; }
public string PrivateKey { get; set; }
[DefaultValue(15)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public int CacheInMinutes { get; set; } = 15;
}
public interface ICoinAverageAuthenticator
{
Task AddHeader(HttpRequestMessage message);
}
public class CoinAverageRateProvider : IRateProvider, IHasExchangeName
{
public const string CoinAverageName = "coinaverage";
public CoinAverageRateProvider()
{
}
public HttpClient HttpClient
{
get
{
return _LocalClient ?? _Client;
}
set
{
_LocalClient = value;
}
}
HttpClient _LocalClient;
static HttpClient _Client = new HttpClient();
public string Exchange { get; set; } = CoinAverageName;
public string CryptoCode { get; set; }
public string Market
{
get; set;
} = "global";
public ICoinAverageAuthenticator Authenticator { get; set; }
public string ExchangeName => Exchange ?? CoinAverageName;
private bool TryToBidAsk(JProperty p, out BidAsk bidAsk)
{
bidAsk = null;
if (Exchange == CoinAverageName)
{
JToken last = p.Value["last"];
if (!decimal.TryParse(last.Value<string>(), System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var v) ||
v <= 0)
return false;
bidAsk = new BidAsk(v);
return true;
}
else
{
JToken bid = p.Value["bid"];
JToken ask = p.Value["ask"];
if (bid == null || ask == null ||
!decimal.TryParse(bid.Value<string>(), System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var v1) ||
!decimal.TryParse(ask.Value<string>(), System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var v2) ||
v1 > v2 ||
v1 <= 0 || v2 <= 0)
return false;
bidAsk = new BidAsk(v1, v2);
return true;
}
}
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
{
string url = Exchange == CoinAverageName ? $"https://apiv2.bitcoinaverage.com/indices/{Market}/ticker/short"
: $"https://apiv2.bitcoinaverage.com/exchanges/{Exchange}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
var auth = Authenticator;
if (auth != null)
{
await auth.AddHeader(request);
}
var resp = await HttpClient.SendAsync(request, cancellationToken);
using (resp)
{
if ((int)resp.StatusCode == 401)
throw new CoinAverageException("Unauthorized access to the API");
if ((int)resp.StatusCode == 429)
throw new CoinAverageException("Exceed API limits");
if ((int)resp.StatusCode == 403)
throw new CoinAverageException("Unauthorized access to the API, premium plan needed");
resp.EnsureSuccessStatusCode();
var rates = JObject.Parse(await resp.Content.ReadAsStringAsync());
if (Exchange != CoinAverageName)
{
rates = (JObject)rates["symbols"];
}
var exchangeRates = new ExchangeRates();
foreach (var prop in rates.Properties())
{
ExchangeRate exchangeRate = new ExchangeRate();
exchangeRate.Exchange = Exchange;
if (!TryToBidAsk(prop, out var value))
continue;
exchangeRate.BidAsk = value;
if (CurrencyPair.TryParse(prop.Name, out var pair))
{
exchangeRate.CurrencyPair = pair;
exchangeRates.Add(exchangeRate);
}
}
return exchangeRates;
}
}
public async Task TestAuthAsync()
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://apiv2.bitcoinaverage.com/blockchain/tx_price/BTCUSD/8a3b4394ba811a9e2b0bbf3cc56888d053ea21909299b2703cdc35e156c860ff");
var auth = Authenticator;
if (auth != null)
{
await auth.AddHeader(request);
}
var resp = await HttpClient.SendAsync(request);
resp.EnsureSuccessStatusCode();
}
public async Task<GetRateLimitsResponse> GetRateLimitsAsync()
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://apiv2.bitcoinaverage.com/info/ratelimits");
var auth = Authenticator;
if (auth != null)
{
await auth.AddHeader(request);
}
var resp = await HttpClient.SendAsync(request);
resp.EnsureSuccessStatusCode();
var jobj = JObject.Parse(await resp.Content.ReadAsStringAsync());
var response = new GetRateLimitsResponse();
response.CounterReset = TimeSpan.FromSeconds(jobj["counter_reset"].Value<int>());
var totalPeriod = jobj["total_period"].Value<string>();
if (totalPeriod == "24h")
{
response.TotalPeriod = TimeSpan.FromHours(24);
}
else if (totalPeriod == "30d")
{
response.TotalPeriod = TimeSpan.FromDays(30);
}
else
{
response.TotalPeriod = TimeSpan.FromSeconds(jobj["total_period"].Value<int>());
}
response.RequestsLeft = jobj["requests_left"].Value<int>();
response.RequestsPerPeriod = jobj["requests_per_period"].Value<int>();
return response;
}
public async Task<GetExchangeTickersResponse> GetExchangeTickersAsync()
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://apiv2.bitcoinaverage.com/symbols/exchanges/ticker");
var auth = Authenticator;
if (auth != null)
{
await auth.AddHeader(request);
}
var resp = await HttpClient.SendAsync(request);
resp.EnsureSuccessStatusCode();
var jobj = JObject.Parse(await resp.Content.ReadAsStringAsync());
var response = new GetExchangeTickersResponse();
response.Success = jobj["success"].Value<bool>();
var exchanges = (JObject)jobj["exchanges"];
response.Exchanges = exchanges
.Properties()
.Select(p =>
{
var exchange = JsonConvert.DeserializeObject<GetExchangeTickersResponse.Exchange>(p.Value.ToString());
exchange.Name = p.Name;
return exchange;
})
.ToArray();
return response;
}
}
public class GetRateLimitsResponse
{
public TimeSpan CounterReset { get; set; }
public int RequestsLeft { get; set; }
public int RequestsPerPeriod { get; set; }
public TimeSpan TotalPeriod { get; set; }
}
}

View File

@ -1,166 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace BTCPayServer.Services.Rates
{
public class CoinAverageSettingsAuthenticator : ICoinAverageAuthenticator
{
CoinAverageSettings _Settings;
public CoinAverageSettingsAuthenticator(CoinAverageSettings settings)
{
_Settings = settings;
}
public Task AddHeader(HttpRequestMessage message)
{
return _Settings.AddHeader(message);
}
}
public class CoinAverageExchange
{
public CoinAverageExchange(string name, string display, string url)
{
Name = name;
Display = display;
Url = url;
}
public string Name { get; set; }
public string Display { get; set; }
public string Url
{
get;
set;
}
}
public class CoinAverageExchanges : Dictionary<string, CoinAverageExchange>
{
public CoinAverageExchanges()
{
}
public void Add(CoinAverageExchange exchange)
{
if (!TryAdd(exchange.Name, exchange))
{
this.Remove(exchange.Name);
this.Add(exchange.Name, exchange);
}
}
}
public class CoinAverageSettings : ICoinAverageAuthenticator
{
private static readonly DateTime _epochUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public (String PublicKey, String PrivateKey)? KeyPair { get; set; }
public CoinAverageExchanges AvailableExchanges { get; set; } = new CoinAverageExchanges();
public CoinAverageSettings()
{
//GENERATED BY:
//StringBuilder b = new StringBuilder();
//b.AppendLine("_coinAverageSettings.AvailableExchanges = new[] {");
//foreach (var availableExchange in _coinAverageSettings.AvailableExchanges)
//{
// b.AppendLine($"(DisplayName: \"{availableExchange.DisplayName}\", Name: \"{availableExchange.Name}\"),");
//}
//b.AppendLine("}.ToArray()");
AvailableExchanges = new CoinAverageExchanges();
foreach (var item in
new[] {
(DisplayName: "Idex", Name: "idex"),
(DisplayName: "Coinfloor", Name: "coinfloor"),
(DisplayName: "Okex", Name: "okex"),
(DisplayName: "Bitfinex", Name: "bitfinex"),
(DisplayName: "Bittylicious", Name: "bittylicious"),
(DisplayName: "BTC Markets", Name: "btcmarkets"),
(DisplayName: "Kucoin", Name: "kucoin"),
(DisplayName: "IDAX", Name: "idax"),
(DisplayName: "Kraken", Name: "kraken"),
(DisplayName: "Bit2C", Name: "bit2c"),
(DisplayName: "Mercado Bitcoin", Name: "mercado"),
(DisplayName: "CEX.IO", Name: "cex"),
(DisplayName: "Bitex.la", Name: "bitex"),
(DisplayName: "Quoine", Name: "quoine"),
(DisplayName: "Stex", Name: "stex"),
(DisplayName: "CoinTiger", Name: "cointiger"),
(DisplayName: "Poloniex", Name: "poloniex"),
(DisplayName: "Zaif", Name: "zaif"),
(DisplayName: "Huobi", Name: "huobi"),
(DisplayName: "QuickBitcoin", Name: "quickbitcoin"),
(DisplayName: "Tidex", Name: "tidex"),
(DisplayName: "Tokenomy", Name: "tokenomy"),
(DisplayName: "Bitcoin.co.id", Name: "bitcoin_co_id"),
(DisplayName: "Kryptono", Name: "kryptono"),
(DisplayName: "Bitso", Name: "bitso"),
(DisplayName: "Korbit", Name: "korbit"),
(DisplayName: "Yobit", Name: "yobit"),
(DisplayName: "BitBargain", Name: "bitbargain"),
(DisplayName: "Livecoin", Name: "livecoin"),
(DisplayName: "Hotbit", Name: "hotbit"),
(DisplayName: "Coincheck", Name: "coincheck"),
(DisplayName: "Binance", Name: "binance"),
(DisplayName: "Bit-Z", Name: "bitz"),
(DisplayName: "Coinbase Pro", Name: "coinbasepro"),
(DisplayName: "Rock Trading", Name: "rocktrading"),
(DisplayName: "Bittrex", Name: "bittrex"),
(DisplayName: "BitBay", Name: "bitbay"),
(DisplayName: "Tokenize", Name: "tokenize"),
(DisplayName: "Hitbtc", Name: "hitbtc"),
(DisplayName: "Upbit", Name: "upbit"),
(DisplayName: "Bitstamp", Name: "bitstamp"),
(DisplayName: "Luno", Name: "luno"),
(DisplayName: "Trade.io", Name: "tradeio"),
(DisplayName: "LocalBitcoins", Name: "localbitcoins"),
(DisplayName: "Independent Reserve", Name: "independentreserve"),
(DisplayName: "Coinsquare", Name: "coinsquare"),
(DisplayName: "Exmoney", Name: "exmoney"),
(DisplayName: "Coinegg", Name: "coinegg"),
(DisplayName: "FYB-SG", Name: "fybsg"),
(DisplayName: "Cryptonit", Name: "cryptonit"),
(DisplayName: "BTCTurk", Name: "btcturk"),
(DisplayName: "bitFlyer", Name: "bitflyer"),
(DisplayName: "Negocie Coins", Name: "negociecoins"),
(DisplayName: "OasisDEX", Name: "oasisdex"),
(DisplayName: "CoinMate", Name: "coinmate"),
(DisplayName: "BitForex", Name: "bitforex"),
(DisplayName: "Bitsquare", Name: "bitsquare"),
(DisplayName: "FYB-SE", Name: "fybse"),
(DisplayName: "itBit", Name: "itbit"),
})
{
AvailableExchanges.TryAdd(item.Name, new CoinAverageExchange(item.Name, item.DisplayName, $"https://apiv2.bitcoinaverage.com/exchanges/{item.Name}"));
}
// Keep back-compat
AvailableExchanges.Add(new CoinAverageExchange("gdax", string.Empty, $"https://apiv2.bitcoinaverage.com/exchanges/coinbasepro"));
}
public Task AddHeader(HttpRequestMessage message)
{
var signature = GetCoinAverageSignature();
if (signature != null)
{
message.Headers.Add("X-signature", signature);
}
return Task.CompletedTask;
}
public string GetCoinAverageSignature()
{
var keyPair = KeyPair;
if (!keyPair.HasValue)
return null;
if (string.IsNullOrEmpty(keyPair.Value.PublicKey) || string.IsNullOrEmpty(keyPair.Value.PrivateKey))
return null;
var timestamp = (int)((DateTime.UtcNow - _epochUtc).TotalSeconds);
var payload = timestamp + "." + keyPair.Value.PublicKey;
var digestValueBytes = new HMACSHA256(Encoding.ASCII.GetBytes(keyPair.Value.PrivateKey)).ComputeHash(Encoding.ASCII.GetBytes(payload));
var digestValueHex = NBitcoin.DataEncoders.Encoders.Hex.EncodeData(digestValueBytes);
return payload + "." + digestValueHex;
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Rating;
@ -11,18 +12,15 @@ using ExchangeSharp;
namespace BTCPayServer.Services.Rates
{
public class ExchangeSharpRateProvider : IRateProvider, IHasExchangeName
public class ExchangeSharpRateProvider<T> : IRateProvider where T : ExchangeAPI, new()
{
readonly ExchangeAPI _ExchangeAPI;
readonly string _ExchangeName;
public ExchangeSharpRateProvider(string exchangeName, ExchangeAPI exchangeAPI, bool reverseCurrencyPair = false)
HttpClient _httpClient;
public ExchangeSharpRateProvider(HttpClient httpClient, bool reverseCurrencyPair = false)
{
if (exchangeAPI == null)
throw new ArgumentNullException(nameof(exchangeAPI));
exchangeAPI.RequestTimeout = TimeSpan.FromSeconds(5.0);
_ExchangeAPI = exchangeAPI;
_ExchangeName = exchangeName;
if (httpClient == null)
throw new ArgumentNullException(nameof(httpClient));
ReverseCurrencyPair = reverseCurrencyPair;
_httpClient = httpClient;
}
public bool ReverseCurrencyPair
@ -30,45 +28,42 @@ namespace BTCPayServer.Services.Rates
get; set;
}
public string ExchangeName => _ExchangeName;
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
{
await new SynchronizationContextRemover();
var rates = await _ExchangeAPI.GetTickersAsync();
var exchangeRateTasks = rates
.Where(t => t.Value.Ask != 0m && t.Value.Bid != 0m)
.Select(t => CreateExchangeRate(t));
var exchangeAPI = new T();
exchangeAPI.RequestMaker = new HttpClientRequestMaker(exchangeAPI, _httpClient, cancellationToken);
var rates = await exchangeAPI.GetTickersAsync();
var exchangeRates = await Task.WhenAll(exchangeRateTasks);
return new ExchangeRates(exchangeRates
var exchangeRateTasks = rates
.Where(t => t.Value.Ask != 0m && t.Value.Bid != 0m)
.Select(t => CreateExchangeRate(exchangeAPI, t));
var exchangeRates = await Task.WhenAll(exchangeRateTasks);
return exchangeRates
.Where(t => t != null)
.ToArray());
.ToArray();
}
// ExchangeSymbolToGlobalSymbol throws exception which would kill perf
ConcurrentDictionary<string, string> notFoundSymbols = new ConcurrentDictionary<string, string>();
private async Task<ExchangeRate> CreateExchangeRate(KeyValuePair<string, ExchangeTicker> ticker)
private async Task<PairRate> CreateExchangeRate(T exchangeAPI, KeyValuePair<string, ExchangeTicker> ticker)
{
if (notFoundSymbols.TryGetValue(ticker.Key, out _))
return null;
try
{
var tickerName = await _ExchangeAPI.ExchangeMarketSymbolToGlobalMarketSymbolAsync(ticker.Key);
var tickerName = await exchangeAPI.ExchangeMarketSymbolToGlobalMarketSymbolAsync(ticker.Key);
if (!CurrencyPair.TryParse(tickerName, out var pair))
{
notFoundSymbols.TryAdd(ticker.Key, ticker.Key);
return null;
}
if(ReverseCurrencyPair)
if (ReverseCurrencyPair)
pair = new CurrencyPair(pair.Right, pair.Left);
var rate = new ExchangeRate();
rate.CurrencyPair = pair;
rate.Exchange = _ExchangeName;
rate.BidAsk = new BidAsk(ticker.Value.Bid, ticker.Value.Ask);
return rate;
return new PairRate(pair, new BidAsk(ticker.Value.Bid, ticker.Value.Ask));
}
catch (ArgumentException)
{

View File

@ -17,7 +17,7 @@ namespace BTCPayServer.Services.Rates
_Providers = providers;
}
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
{
foreach (var p in _Providers)
{
@ -31,7 +31,7 @@ namespace BTCPayServer.Services.Rates
}
catch(Exception ex) { Exceptions.Add(ex); }
}
return new ExchangeRates();
return Array.Empty<PairRate>();
}
public List<Exception> Exceptions { get; set; } = new List<Exception>();

View File

@ -0,0 +1,210 @@
using System;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using ExchangeSharp;
using System.Threading;
namespace BTCPayServer.Services.Rates
{
internal class HttpClientRequestMaker : IAPIRequestMaker
{
class InternalHttpWebRequest : IHttpWebRequest
{
internal readonly HttpWebRequest Request;
public Uri RequestUri => Request.RequestUri;
public string Method
{
get
{
return Request.Method;
}
set
{
Request.Method = value;
}
}
public int Timeout
{
get
{
return Request.Timeout;
}
set
{
Request.Timeout = value;
}
}
public int ReadWriteTimeout
{
get
{
return Request.ReadWriteTimeout;
}
set
{
Request.ReadWriteTimeout = value;
}
}
public InternalHttpWebRequest(Uri fullUri)
{
Request = ((WebRequest.Create(fullUri) as HttpWebRequest) ?? throw new NullReferenceException("Failed to create HttpWebRequest"));
Request.KeepAlive = false;
}
public void AddHeader(string header, string value)
{
switch (header.ToStringLowerInvariant())
{
case "content-type":
Request.ContentType = value;
break;
case "content-length":
Request.ContentLength = value.ConvertInvariant<long>(0L);
break;
case "user-agent":
Request.UserAgent = value;
break;
case "accept":
Request.Accept = value;
break;
case "connection":
Request.Connection = value;
break;
default:
Request.Headers[header] = value;
break;
}
}
public Task WriteAllAsync(byte[] data, int index, int length)
{
throw new NotImplementedException();
}
public HttpRequestMessage ToHttpRequestMessage()
{
var httpRequest = new HttpRequestMessage(HttpMethod.Get, Request.RequestUri);
CopyHeadersFrom(httpRequest, Request);
return httpRequest;
}
internal void CopyHeadersFrom(HttpRequestMessage message, HttpWebRequest request)
{
foreach (string headerName in request.Headers)
{
string[] headerValues = request.Headers.GetValues(headerName);
if (!message.Headers.TryAddWithoutValidation(headerName, headerValues))
{
if (message.Content != null)
message.Content.Headers.TryAddWithoutValidation(headerName, headerValues);
}
}
}
}
class InternalHttpWebResponse : IHttpWebResponse
{
public InternalHttpWebResponse(HttpResponseMessage httpResponseMessage)
{
var headers = new Dictionary<string, List<string>>();
foreach (var h in httpResponseMessage.Headers)
{
if (!headers.TryGetValue(h.Key, out var list))
{
list = new List<string>();
headers.Add(h.Key, list);
}
list.AddRange(h.Value);
}
Headers = new Dictionary<string, IReadOnlyList<string>>(headers.Count);
foreach (var item in headers)
{
Headers.Add(item.Key, item.Value.AsReadOnly());
}
}
public Dictionary<string, IReadOnlyList<string>> Headers { get; }
static IReadOnlyList<string> Empty = new List<string>().AsReadOnly();
public IReadOnlyList<string> GetHeader(string name)
{
Headers.TryGetValue(name, out var list);
return list ?? Empty;
}
}
private readonly IAPIRequestHandler api;
private readonly HttpClient _httpClient;
private readonly CancellationToken _cancellationToken;
public HttpClientRequestMaker(IAPIRequestHandler api, HttpClient httpClient, CancellationToken cancellationToken)
{
if (api == null)
throw new ArgumentNullException(nameof(api));
if (httpClient == null)
throw new ArgumentNullException(nameof(httpClient));
this.api = api;
_httpClient = httpClient;
_cancellationToken = cancellationToken;
}
public Action<IAPIRequestMaker, RequestMakerState, object> RequestStateChanged
{
get;
set;
}
public async Task<string> MakeRequestAsync(string url, string baseUrl = null, Dictionary<string, object> payload = null, string method = null)
{
await default(SynchronizationContextRemover);
await api.RateLimit.WaitToProceedAsync();
if (url[0] != '/')
{
url = "/" + url;
}
string uri2 = (baseUrl ?? api.BaseUrl) + url;
if (method == null)
{
method = api.RequestMethod;
}
Uri uri = api.ProcessRequestUrl(new UriBuilder(uri2), payload, method);
InternalHttpWebRequest request = new InternalHttpWebRequest(uri)
{
Method = method
};
request.AddHeader("content-type", api.RequestContentType);
request.AddHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36");
int num3 = request.Timeout = (request.ReadWriteTimeout = (int)api.RequestTimeout.TotalMilliseconds);
await api.ProcessRequestAsync(request, payload);
try
{
RequestStateChanged?.Invoke(this, RequestMakerState.Begin, uri.AbsoluteUri);
using var webHttpRequest = request.ToHttpRequestMessage();
using var webHttpResponse = await _httpClient.SendAsync(webHttpRequest, _cancellationToken);
string text = await webHttpResponse.Content.ReadAsStringAsync();
if (!webHttpResponse.IsSuccessStatusCode)
{
if (string.IsNullOrWhiteSpace(text))
{
throw new APIException($"{webHttpResponse.StatusCode.ConvertInvariant<int>(0)} - {webHttpResponse.StatusCode}");
}
throw new APIException(text);
}
api.ProcessResponse(new InternalHttpWebResponse(webHttpResponse));
RequestStateChanged?.Invoke(this, RequestMakerState.Finished, text);
return text;
}
catch (Exception arg)
{
RequestStateChanged?.Invoke(this, RequestMakerState.Error, arg);
throw;
}
}
}
}

View File

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BTCPayServer.Services.Rates
{
public interface IHasExchangeName
{
string ExchangeName { get; }
}
}

View File

@ -9,6 +9,6 @@ namespace BTCPayServer.Services.Rates
{
public interface IRateProvider
{
Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken);
Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken);
}
}

View File

@ -14,7 +14,7 @@ using Newtonsoft.Json.Linq;
namespace BTCPayServer.Services.Rates
{
// Make sure that only one request is sent to kraken in general
public class KrakenExchangeRateProvider : IRateProvider, IHasExchangeName
public class KrakenExchangeRateProvider : IRateProvider
{
public KrakenExchangeRateProvider()
{
@ -33,8 +33,6 @@ namespace BTCPayServer.Services.Rates
}
}
public string ExchangeName => "kraken";
HttpClient _LocalClient;
static HttpClient _Client = new HttpClient();
@ -87,9 +85,9 @@ namespace BTCPayServer.Services.Rates
{ "ZGBP", "GBP" }
};
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
{
var result = new ExchangeRates();
var result = new List<PairRate>();
var symbols = await GetSymbolsAsync(cancellationToken);
var normalizedPairsList = symbols.Where(s => !notFoundSymbols.ContainsKey(s)).Select(s => _Helper.NormalizeMarketSymbol(s)).ToList();
var csvPairsList = string.Join(",", normalizedPairsList);
@ -117,7 +115,7 @@ namespace BTCPayServer.Services.Rates
global = await _Helper.ExchangeMarketSymbolToGlobalMarketSymbolAsync(symbol);
}
if (CurrencyPair.TryParse(global, out var pair))
result.Add(new ExchangeRate("kraken", pair.Inverse(), new BidAsk(ticker.Bid, ticker.Ask)));
result.Add(new PairRate(pair.Inverse(), new BidAsk(ticker.Bid, ticker.Ask)));
else
notFoundSymbols.TryAdd(symbol, symbol);
}
@ -127,7 +125,7 @@ namespace BTCPayServer.Services.Rates
}
}
}
return result;
return result.ToArray();
}
private static ExchangeTicker ConvertToExchangeTicker(string symbol, JToken ticker)

View File

@ -21,9 +21,9 @@ namespace BTCPayServer.Services.Rates
return _Instance;
}
}
public Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
public Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
{
return Task.FromResult(new ExchangeRates());
return Task.FromResult(Array.Empty<PairRate>());
}
}
}

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