From bc4b1905f52ed9b8a27a5b6642f10d9286c3108c Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Fri, 1 Nov 2019 16:52:13 +0100 Subject: [PATCH 01/25] bch (+testnet): Bump backend 0.20.4 -> 0.20.5 --- configs/coins/bcash.json | 8 ++++---- configs/coins/bcash_testnet.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/configs/coins/bcash.json b/configs/coins/bcash.json index 3cc6f240..4be65abe 100644 --- a/configs/coins/bcash.json +++ b/configs/coins/bcash.json @@ -22,10 +22,10 @@ "package_name": "backend-bcash", "package_revision": "satoshilabs-1", "system_user": "bcash", - "version": "0.20.4", - "binary_url": "https://download.bitcoinabc.org/0.20.4/linux/bitcoin-abc-0.20.4-x86_64-linux-gnu.tar.gz", + "version": "0.20.5", + "binary_url": "https://download.bitcoinabc.org/0.20.5/linux/bitcoin-abc-0.20.5-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "7bc8db6e3018f624a67e0824c08db58165d63d97de83294be0808610cbfa5d5b", + "verification_source": "8197c0c26553d0c1dd5bc5e033aaec9dd7324ceab8218f075f7d48f4889e3aef", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -49,7 +49,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Bitcoin ABC:0.20.4/", + "subversion": "/Bitcoin ABC:0.20.5/", "address_format": "cashaddr", "mempool_workers": 8, "mempool_sub_workers": 2, diff --git a/configs/coins/bcash_testnet.json b/configs/coins/bcash_testnet.json index 34e2dbca..eb011390 100644 --- a/configs/coins/bcash_testnet.json +++ b/configs/coins/bcash_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-bcash-testnet", "package_revision": "satoshilabs-1", "system_user": "bcash", - "version": "0.20.4", - "binary_url": "https://download.bitcoinabc.org/0.20.4/linux/bitcoin-abc-0.20.4-x86_64-linux-gnu.tar.gz", + "version": "0.20.5", + "binary_url": "https://download.bitcoinabc.org/0.20.5/linux/bitcoin-abc-0.20.5-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "7bc8db6e3018f624a67e0824c08db58165d63d97de83294be0808610cbfa5d5b", + "verification_source": "8197c0c26553d0c1dd5bc5e033aaec9dd7324ceab8218f075f7d48f4889e3aef", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -49,7 +49,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Bitcoin ABC:0.20.4/", + "subversion": "/Bitcoin ABC:0.20.5/", "address_format": "cashaddr", "mempool_workers": 8, "mempool_sub_workers": 2, From f2e4e67c4dee82372a080ed56d7410b292b89105 Mon Sep 17 00:00:00 2001 From: WO Date: Wed, 6 Nov 2019 18:22:00 +0900 Subject: [PATCH 02/25] Bump Koto backend to 2.1.0 --- configs/coins/koto.json | 6 +++--- configs/coins/koto_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/koto.json b/configs/coins/koto.json index 48327749..16f0a088 100644 --- a/configs/coins/koto.json +++ b/configs/coins/koto.json @@ -22,10 +22,10 @@ "package_name": "backend-koto", "package_revision": "satoshilabs-1", "system_user": "koto", - "version": "2.0.7", - "binary_url": "https://github.com/KotoDevelopers/koto/releases/download/v2.0.7/koto-2.0.7-linux64.tar.gz", + "version": "2.1.0", + "binary_url": "https://github.com/KotoDevelopers/koto/releases/download/v2.1.0/koto-2.1.0-linux64.tar.gz", "verification_type": "gpg", - "verification_source": "https://github.com/KotoDevelopers/koto/releases/download/v2.0.7/koto-2.0.7-linux64.tar.gz.asc", + "verification_source": "https://github.com/KotoDevelopers/koto/releases/download/v2.1.0/koto-2.1.0-linux64.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/koto-qt" diff --git a/configs/coins/koto_testnet.json b/configs/coins/koto_testnet.json index 52cfc5be..108e9f46 100644 --- a/configs/coins/koto_testnet.json +++ b/configs/coins/koto_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-koto-testnet", "package_revision": "satoshilabs-1", "system_user": "koto", - "version": "2.0.7", - "binary_url": "https://github.com/KotoDevelopers/koto/releases/download/v2.0.7/koto-2.0.7-linux64.tar.gz", + "version": "2.1.0", + "binary_url": "https://github.com/KotoDevelopers/koto/releases/download/v2.1.0/koto-2.1.0-linux64.tar.gz", "verification_type": "gpg", - "verification_source": "https://github.com/KotoDevelopers/koto/releases/download/v2.0.7/koto-2.0.7-linux64.tar.gz.asc", + "verification_source": "https://github.com/KotoDevelopers/koto/releases/download/v2.1.0/koto-2.1.0-linux64.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/koto-qt" From 21fe8082ddb8c98ac2fe2b7568a14e81ead929ca Mon Sep 17 00:00:00 2001 From: thebevrishot Date: Tue, 5 Nov 2019 13:49:08 +0700 Subject: [PATCH 03/25] Bump Zcoin to 0.13.8.5 --- configs/coins/zcoin.json | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/configs/coins/zcoin.json b/configs/coins/zcoin.json index 8127ada0..c239f8a7 100644 --- a/configs/coins/zcoin.json +++ b/configs/coins/zcoin.json @@ -22,18 +22,25 @@ "package_name": "backend-zcoin", "package_revision": "satoshilabs-1", "system_user": "zcoin", - "version": "0.13.8.2", - "binary_url": "https://github.com/zcoinofficial/zcoin/releases/download/v0.13.8.2/zcoin-0.13.8.2-linux64.tar.gz", + "version": "0.13.8.5", + "binary_url": "https://github.com/zcoinofficial/zcoin/releases/download/v0.13.8.5/zcoin-0.13.8.5-linux64.tar.gz", "verification_type": "sha256", - "verification_source": "468a7b5c030a04e4c38ce23821d331f215ccf0665dec10c1001b357a8fbe196c", + "verification_source": "52f8a722bb1cc5c53e77edf81f6f1300f3b15d53f9cb4fcee90367f11ad84dd9", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ + "bin/tor", + "bin/tor-gencert", + "bin/tor-print-ed-signing-cert", + "bin/tor-resolve", "bin/zcoin-qt", "bin/zcoin-tx", + "etc/tor/torrc.sample", "include/bitcoinconsensus.h", "lib/libbitcoinconsensus.so", "lib/libbitcoinconsensus.so.0", - "lib/libbitcoinconsensus.so.0.0.0" + "lib/libbitcoinconsensus.so.0.0.0", + "share/tor/geoip", + "share/tor/geoip6" ], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/zcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log", From 1c929f2a4050f60da921c1aecaeda5e37fa76a65 Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Thu, 7 Nov 2019 16:20:09 +0100 Subject: [PATCH 04/25] eth (+testnet): Bump backend 1.9.6 -> 1.9.7 --- configs/coins/ethereum.json | 6 +++--- configs/coins/ethereum_testnet_ropsten.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 8bc4a47e..5c89ef26 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -21,10 +21,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.9.6-bd059680", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.6-bd059680.tar.gz", + "version": "1.9.7-a718daa6", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.7-a718daa6.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.6-bd059680.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.7-a718daa6.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ipcdisable --syncmode full --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --wsaddr 0.0.0.0 --wsport {{.Ports.BackendRPC}} --wsorigins \"*\" --rpc --rpcport 8136 -rpcaddr 0.0.0.0 --rpccorsdomain \"*\" --rpcvhosts \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 52f8a4c2..1d193182 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -20,10 +20,10 @@ "package_name": "backend-ethereum-testnet-ropsten", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.9.6-bd059680", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.6-bd059680.tar.gz", + "version": "1.9.7-a718daa6", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.7-a718daa6.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.6-bd059680.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.7-a718daa6.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --testnet --syncmode full --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --wsaddr 0.0.0.0 --wsport {{.Ports.BackendRPC}} --wsorigins \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From 42a208be158957d4179bc7b6cf40aee22c1cfefb Mon Sep 17 00:00:00 2001 From: Mykola Date: Sun, 10 Nov 2019 23:53:07 -0800 Subject: [PATCH 05/25] zcash (+testnet): Bump backend 2.0.7-3 -> 2.1.0-1 --- configs/coins/zcash.json | 4 ++-- configs/coins/zcash_testnet.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index f3b63620..f190aa37 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -23,9 +23,9 @@ "package_revision": "satoshilabs-1", "system_user": "zcash", "version": "2.0.7-3", - "binary_url": "https://z.cash/downloads/zcash-2.0.7-3-linux64-debian-stretch.tar.gz", + "binary_url": "https://z.cash/downloads/zcash-2.1.0-1-linux64-debian-stretch.tar.gz", "verification_type": "sha256", - "verification_source": "b6961c0073c968dc30ed10a752ba657cb76acb22265dbc5f4bae7cc0395b35e4", + "verification_source": "aec6b3c4178c5ceb4e7b7ed3aaa96078b88b872640c7bb38208d5bce48e3f75c", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/zcashd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", diff --git a/configs/coins/zcash_testnet.json b/configs/coins/zcash_testnet.json index 48c32c77..38a2fa4a 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -23,9 +23,9 @@ "package_revision": "satoshilabs-1", "system_user": "zcash", "version": "2.0.7-3", - "binary_url": "https://z.cash/downloads/zcash-2.0.7-3-linux64-debian-stretch.tar.gz", + "binary_url": "https://z.cash/downloads/zcash-2.1.0-1-linux64-debian-stretch.tar.gz", "verification_type": "sha256", - "verification_source": "b6961c0073c968dc30ed10a752ba657cb76acb22265dbc5f4bae7cc0395b35e4", + "verification_source": "aec6b3c4178c5ceb4e7b7ed3aaa96078b88b872640c7bb38208d5bce48e3f75c", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/zcashd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", From ff415ae394f8a28a62d3174bde928db3e844353f Mon Sep 17 00:00:00 2001 From: Mykola Date: Mon, 11 Nov 2019 13:04:39 -0800 Subject: [PATCH 06/25] Fix release number --- configs/coins/zcash.json | 2 +- configs/coins/zcash_testnet.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index f190aa37..99a31db3 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -23,7 +23,7 @@ "package_revision": "satoshilabs-1", "system_user": "zcash", "version": "2.0.7-3", - "binary_url": "https://z.cash/downloads/zcash-2.1.0-1-linux64-debian-stretch.tar.gz", + "binary_url": "https://z.cash/downloads/zcash-2.1.0-linux64-debian-stretch.tar.gz", "verification_type": "sha256", "verification_source": "aec6b3c4178c5ceb4e7b7ed3aaa96078b88b872640c7bb38208d5bce48e3f75c", "extract_command": "tar -C backend --strip 1 -xf", diff --git a/configs/coins/zcash_testnet.json b/configs/coins/zcash_testnet.json index 38a2fa4a..84850a11 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -23,7 +23,7 @@ "package_revision": "satoshilabs-1", "system_user": "zcash", "version": "2.0.7-3", - "binary_url": "https://z.cash/downloads/zcash-2.1.0-1-linux64-debian-stretch.tar.gz", + "binary_url": "https://z.cash/downloads/zcash-2.1.0-linux64-debian-stretch.tar.gz", "verification_type": "sha256", "verification_source": "aec6b3c4178c5ceb4e7b7ed3aaa96078b88b872640c7bb38208d5bce48e3f75c", "extract_command": "tar -C backend --strip 1 -xf", From bd0848dbbe8be727266668f0ae38ece43e0553de Mon Sep 17 00:00:00 2001 From: Mykola Date: Mon, 11 Nov 2019 13:12:18 -0800 Subject: [PATCH 07/25] Return to release tag 2.0.1-1 --- configs/coins/zcash.json | 2 +- configs/coins/zcash_testnet.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index 99a31db3..f190aa37 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -23,7 +23,7 @@ "package_revision": "satoshilabs-1", "system_user": "zcash", "version": "2.0.7-3", - "binary_url": "https://z.cash/downloads/zcash-2.1.0-linux64-debian-stretch.tar.gz", + "binary_url": "https://z.cash/downloads/zcash-2.1.0-1-linux64-debian-stretch.tar.gz", "verification_type": "sha256", "verification_source": "aec6b3c4178c5ceb4e7b7ed3aaa96078b88b872640c7bb38208d5bce48e3f75c", "extract_command": "tar -C backend --strip 1 -xf", diff --git a/configs/coins/zcash_testnet.json b/configs/coins/zcash_testnet.json index 84850a11..38a2fa4a 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -23,7 +23,7 @@ "package_revision": "satoshilabs-1", "system_user": "zcash", "version": "2.0.7-3", - "binary_url": "https://z.cash/downloads/zcash-2.1.0-linux64-debian-stretch.tar.gz", + "binary_url": "https://z.cash/downloads/zcash-2.1.0-1-linux64-debian-stretch.tar.gz", "verification_type": "sha256", "verification_source": "aec6b3c4178c5ceb4e7b7ed3aaa96078b88b872640c7bb38208d5bce48e3f75c", "extract_command": "tar -C backend --strip 1 -xf", From eb4f049912ed4195a954a9c8ad6c85fb64a4a089 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 13 Nov 2019 13:43:06 +0100 Subject: [PATCH 08/25] Fix version in Zcash and Zcash-testnet configs --- configs/coins/zcash.json | 2 +- configs/coins/zcash_testnet.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index f190aa37..fecc9586 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,7 +22,7 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "2.0.7-3", + "version": "2.1.0-1", "binary_url": "https://z.cash/downloads/zcash-2.1.0-1-linux64-debian-stretch.tar.gz", "verification_type": "sha256", "verification_source": "aec6b3c4178c5ceb4e7b7ed3aaa96078b88b872640c7bb38208d5bce48e3f75c", diff --git a/configs/coins/zcash_testnet.json b/configs/coins/zcash_testnet.json index 38a2fa4a..58d8a0cd 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -22,7 +22,7 @@ "package_name": "backend-zcash-testnet", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "2.0.7-3", + "version": "2.1.0-1", "binary_url": "https://z.cash/downloads/zcash-2.1.0-1-linux64-debian-stretch.tar.gz", "verification_type": "sha256", "verification_source": "aec6b3c4178c5ceb4e7b7ed3aaa96078b88b872640c7bb38208d5bce48e3f75c", From bb1b909361424dcf2631c7b7552436a5aa0a4d14 Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Thu, 14 Nov 2019 16:47:55 +0100 Subject: [PATCH 09/25] bch (+testnet): Bump backend 0.20.5 -> 0.20.6 --- configs/coins/bcash.json | 8 ++++---- configs/coins/bcash_testnet.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/configs/coins/bcash.json b/configs/coins/bcash.json index 4be65abe..35a2d8e5 100644 --- a/configs/coins/bcash.json +++ b/configs/coins/bcash.json @@ -22,10 +22,10 @@ "package_name": "backend-bcash", "package_revision": "satoshilabs-1", "system_user": "bcash", - "version": "0.20.5", - "binary_url": "https://download.bitcoinabc.org/0.20.5/linux/bitcoin-abc-0.20.5-x86_64-linux-gnu.tar.gz", + "version": "0.20.6", + "binary_url": "https://download.bitcoinabc.org/0.20.6/linux/bitcoin-abc-0.20.6-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "8197c0c26553d0c1dd5bc5e033aaec9dd7324ceab8218f075f7d48f4889e3aef", + "verification_source": "f5fb4a3dac6164709dbf0632f64adfe56414d260e4bf192effc0049da9c43bac", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -49,7 +49,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Bitcoin ABC:0.20.5/", + "subversion": "/Bitcoin ABC:0.20.6/", "address_format": "cashaddr", "mempool_workers": 8, "mempool_sub_workers": 2, diff --git a/configs/coins/bcash_testnet.json b/configs/coins/bcash_testnet.json index eb011390..a7c90fed 100644 --- a/configs/coins/bcash_testnet.json +++ b/configs/coins/bcash_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-bcash-testnet", "package_revision": "satoshilabs-1", "system_user": "bcash", - "version": "0.20.5", - "binary_url": "https://download.bitcoinabc.org/0.20.5/linux/bitcoin-abc-0.20.5-x86_64-linux-gnu.tar.gz", + "version": "0.20.6", + "binary_url": "https://download.bitcoinabc.org/0.20.6/linux/bitcoin-abc-0.20.6-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "8197c0c26553d0c1dd5bc5e033aaec9dd7324ceab8218f075f7d48f4889e3aef", + "verification_source": "f5fb4a3dac6164709dbf0632f64adfe56414d260e4bf192effc0049da9c43bac", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -49,7 +49,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Bitcoin ABC:0.20.5/", + "subversion": "/Bitcoin ABC:0.20.6/", "address_format": "cashaddr", "mempool_workers": 8, "mempool_sub_workers": 2, From 4134934031e3b5ecc29006d37fa90defa7cb622c Mon Sep 17 00:00:00 2001 From: Liam Alford Date: Mon, 18 Nov 2019 16:51:45 +0000 Subject: [PATCH 10/25] Add DeepOnion (#298) * Add DeepOnion * Fix config bugs * Use base pack/unpck TX * Fix empty array causing test failure. * Fix config files and executable. * Fix sync issue * Fix integration tetsts --- bchain/coins/blockchain.go | 2 + bchain/coins/deeponion/deeponionparser.go | 68 ++++++ .../coins/deeponion/deeponionparser_test.go | 201 ++++++++++++++++++ bchain/coins/deeponion/deeponionrpc.go | 109 ++++++++++ build/templates/backend/config/deeponion.conf | 29 +++ configs/coins/deeponion.json | 67 ++++++ docs/ports.md | 3 +- tests/rpc/testdata/deeponion.json | 52 +++++ tests/sync/testdata/deeponion.json | 96 +++++++++ tests/tests.json | 5 + 10 files changed, 631 insertions(+), 1 deletion(-) create mode 100644 bchain/coins/deeponion/deeponionparser.go create mode 100644 bchain/coins/deeponion/deeponionparser_test.go create mode 100644 bchain/coins/deeponion/deeponionrpc.go create mode 100644 build/templates/backend/config/deeponion.conf create mode 100644 configs/coins/deeponion.json create mode 100644 tests/rpc/testdata/deeponion.json create mode 100644 tests/sync/testdata/deeponion.json diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index df1514bd..0ce5e626 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -9,6 +9,7 @@ import ( "blockbook/bchain/coins/cpuchain" "blockbook/bchain/coins/dash" "blockbook/bchain/coins/dcr" + "blockbook/bchain/coins/deeponion" "blockbook/bchain/coins/digibyte" "blockbook/bchain/coins/divi" "blockbook/bchain/coins/dogecoin" @@ -103,6 +104,7 @@ func init() { BlockChainFactories["Divi"] = divi.NewDiviRPC BlockChainFactories["CPUchain"] = cpuchain.NewCPUchainRPC BlockChainFactories["Unobtanium"] = unobtanium.NewUnobtaniumRPC + BlockChainFactories["DeepOnion"] = deeponion.NewDeepOnionRPC } // GetCoinNameFromConfig gets coin name and coin shortcut from config file diff --git a/bchain/coins/deeponion/deeponionparser.go b/bchain/coins/deeponion/deeponionparser.go new file mode 100644 index 00000000..7e63afe3 --- /dev/null +++ b/bchain/coins/deeponion/deeponionparser.go @@ -0,0 +1,68 @@ +package deeponion + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + + "github.com/martinboehm/btcd/wire" + "github.com/martinboehm/btcutil/chaincfg" +) + +// magic numbers +const ( + MainnetMagic wire.BitcoinNet = 0xf2dbf1d1 +) + +// chain parameters +var ( + MainNetParams chaincfg.Params +) + +func init() { + MainNetParams = chaincfg.MainNetParams + MainNetParams.Net = MainnetMagic + MainNetParams.PubKeyHashAddrID = []byte{31} + MainNetParams.ScriptHashAddrID = []byte{78} + MainNetParams.Bech32HRPSegwit = "dpn" +} + +// DeepOnionParser handle +type DeepOnionParser struct { + *btc.BitcoinParser + baseparser *bchain.BaseParser +} + +// NewDeepOnionParser returns new DeepOnionParser instance +func NewDeepOnionParser(params *chaincfg.Params, c *btc.Configuration) *DeepOnionParser { + return &DeepOnionParser{ + BitcoinParser: btc.NewBitcoinParser(params, c), + baseparser: &bchain.BaseParser{}, + } +} + +// GetChainParams contains network parameters for the main DeepOnion network, +func GetChainParams(chain string) *chaincfg.Params { + // register bitcoin parameters in addition to deeponion parameters + // deeponion has dual standard of addresses and we want to be able to + // parse both standards + if !chaincfg.IsRegistered(&chaincfg.MainNetParams) { + chaincfg.RegisterBitcoinParams() + } + if !chaincfg.IsRegistered(&MainNetParams) { + err := chaincfg.Register(&MainNetParams) + if err != nil { + panic(err) + } + } + return &MainNetParams +} + +// PackTx packs transaction to byte array using protobuf +func (p *DeepOnionParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { + return p.baseparser.PackTx(tx, height, blockTime) +} + +// UnpackTx unpacks transaction from protobuf byte array +func (p *DeepOnionParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { + return p.baseparser.UnpackTx(buf) +} diff --git a/bchain/coins/deeponion/deeponionparser_test.go b/bchain/coins/deeponion/deeponionparser_test.go new file mode 100644 index 00000000..a19fb097 --- /dev/null +++ b/bchain/coins/deeponion/deeponionparser_test.go @@ -0,0 +1,201 @@ +// +build unittest + +package deeponion + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "encoding/hex" + "math/big" + "os" + "reflect" + "testing" + + "github.com/martinboehm/btcutil/chaincfg" +) + +func TestMain(m *testing.M) { + c := m.Run() + chaincfg.ResetParams() + os.Exit(c) +} + + +func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) { + type args struct { + address string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "P2PKH1", + args: args{address: "DYPyxvq57iSRA5xUXzSVfsTENPz4DKFr5S"}, + want: "76a9142afc25b8b5d4ed490026d38b3b464c140a32dc7588ac", + wantErr: false, + }, + { + name: "P2PKH2", + args: args{address: "DshhBSub7vexDFNm45UtG2wBJFt8cm5Uwr"}, + want: "76a914fec0038b0db67c1b304f6c25b3e860277a96226188ac", + wantErr: false, + }, + { + name: "P2SH1", + args: args{address: "YYDTMNJmKqajnWjFPjenzs2awwE4cwYHtC"}, + want: "a91461190c0272b059b2c09b352da81b1712dd83305e87", + wantErr: false, + }, + { + name: "P2SH2", + args: args{address: "Yh1qpMEA4EFMTB4BmhkeyivJ92WiGr3ETX"}, + want: "a914c19ff0bfc8f4387bee48e2cd3628bf72f7053cd787", + wantErr: false, + }, + } + parser := NewDeepOnionParser(GetChainParams("main"), &btc.Configuration{}) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parser.GetAddrDescFromAddress(tt.args.address) + if (err != nil) != tt.wantErr { + t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr) + return + } + h := hex.EncodeToString(got) + if !reflect.DeepEqual(h, tt.want) { + t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want) + } + }) + } +} + +var ( + testTx1 bchain.Tx + + testTxPacked1 = "0a206ba18524d81af732d0226ffdb63d2bcdc0d58a35ac97b5ad731057932d324e1412b401010000001134415d0114caae2bf9a7808aee0798e6245a347405d46c8131dbf55cbbbc689bbee367e902000000484730440220280f3fa80b4e93834fe0a8d9884105310eaa8d36d77b9aff113b6c498138e5bb02204578409f0a14fa1950ea4951314fd495fd503b42a6325efb5c139a6c8253912401ffffffff0200000000000000000005f22f5904000000232102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07ac000000001891e884ea05200028b88a5432760a001220e967e3be9b68bcbb5cf5db31816cd40574345a24e69807ee8a80a7f92baeca14180222484730440220280f3fa80b4e93834fe0a8d9884105310eaa8d36d77b9aff113b6c498138e5bb02204578409f0a14fa1950ea4951314fd495fd503b42a6325efb5c139a6c825391240128ffffffff0f3a0210003a520a0504583af7fb10011a232102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07ac2222446d343835624e4a6169474a6d4556746832426e5a345931796763756644736934454001" +) + +func init() { + testTx1 = bchain.Tx{ + Hex: "010000001134415d0114caae2bf9a7808aee0798e6245a347405d46c8131dbf55cbbbc689bbee367e902000000484730440220280f3fa80b4e93834fe0a8d9884105310eaa8d36d77b9aff113b6c498138e5bb02204578409f0a14fa1950ea4951314fd495fd503b42a6325efb5c139a6c8253912401ffffffff0200000000000000000005f22f5904000000232102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07ac00000000", + Blocktime: 1564554257, + Txid: "6ba18524d81af732d0226ffdb63d2bcdc0d58a35ac97b5ad731057932d324e14", + LockTime: 0, + Time: 1564554257, + Version: 1, + Vin: []bchain.Vin{ + { + ScriptSig: bchain.ScriptSig{ + Hex: "4730440220280f3fa80b4e93834fe0a8d9884105310eaa8d36d77b9aff113b6c498138e5bb02204578409f0a14fa1950ea4951314fd495fd503b42a6325efb5c139a6c8253912401", + }, + Txid: "e967e3be9b68bcbb5cf5db31816cd40574345a24e69807ee8a80a7f92baeca14", + Vout: 2, + Sequence: 4294967295, + }, + }, + Vout: []bchain.Vout{ + { + ValueSat: *big.NewInt(0), + N: 0, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "", + }, + }, + { + ValueSat: *big.NewInt(18660128763), + N: 1, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "2102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07ac", + Addresses: []string{ + "Dm485bNJaiGJmEVth2BnZ4Y1ygcufDsi4E", + }, + }, + }, + }, + } +} + +func Test_PackTx(t *testing.T) { + type args struct { + tx bchain.Tx + height uint32 + blockTime int64 + parser *DeepOnionParser + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "deeponion-1", + args: args{ + tx: testTx1, + height: 1377592, + blockTime: 1564554257, + parser: NewDeepOnionParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: testTxPacked1, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.args.parser.PackTx(&tt.args.tx, tt.args.height, tt.args.blockTime) + if (err != nil) != tt.wantErr { + t.Errorf("packTx() error = %v, wantErr %v", err, tt.wantErr) + return + } + h := hex.EncodeToString(got) + if !reflect.DeepEqual(h, tt.want) { + t.Errorf("packTx() = %v, want %v", h, tt.want) + } + }) + } +} + +func Test_UnpackTx(t *testing.T) { + type args struct { + packedTx string + parser *DeepOnionParser + } + tests := []struct { + name string + args args + want *bchain.Tx + want1 uint32 + wantErr bool + }{ + { + name: "deeponion-1", + args: args{ + packedTx: testTxPacked1, + parser: NewDeepOnionParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: &testTx1, + want1: 1377592, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b, _ := hex.DecodeString(tt.args.packedTx) + got, got1, err := tt.args.parser.UnpackTx(b) + if (err != nil) != tt.wantErr { + t.Errorf("unpackTx(1) error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("unpackTx(2) got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("unpackTx(3) got1 = %v, want %v", got1, tt.want1) + } + }) + } +} diff --git a/bchain/coins/deeponion/deeponionrpc.go b/bchain/coins/deeponion/deeponionrpc.go new file mode 100644 index 00000000..6fab0c49 --- /dev/null +++ b/bchain/coins/deeponion/deeponionrpc.go @@ -0,0 +1,109 @@ +package deeponion + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "encoding/json" + + "github.com/golang/glog" + "github.com/juju/errors" +) + +// DeepOnionRPC is an interface to JSON-RPC bitcoind service. +type DeepOnionRPC struct { + *btc.BitcoinRPC +} + +// NewDeepOnionRPC returns new DeepOnionRPC instance. +func NewDeepOnionRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { + b, err := btc.NewBitcoinRPC(config, pushHandler) + if err != nil { + return nil, err + } + + s := &DeepOnionRPC{ + b.(*btc.BitcoinRPC), + } + s.RPCMarshaler = btc.JSONMarshalerV2{} + s.ChainConfig.SupportsEstimateFee = false + + return s, nil +} + +// Initialize initializes DeepOnionRPC instance. +func (b *DeepOnionRPC) Initialize() error { + ci, err := b.GetChainInfo() + if err != nil { + return err + } + chainName := ci.Chain + + glog.Info("Chain name ", chainName) + params := GetChainParams(chainName) + + // always create parser + b.Parser = NewDeepOnionParser(params, b.ChainConfig) + + // parameters for getInfo request + if params.Net == MainnetMagic { + b.Testnet = false + b.Network = "livenet" + } else { + b.Testnet = true + b.Network = "testnet" + } + + glog.Info("rpc: block chain ", params.Name) + + return nil +} + +// GetBlock returns block with given hash. +func (s *DeepOnionRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { + var err error + if hash == "" && height > 0 { + hash, err = s.GetBlockHash(height) + if err != nil { + return nil, err + } + } + + glog.V(1).Info("rpc: getblock (verbosity=1) ", hash) + + res := btc.ResGetBlockThin{} + req := btc.CmdGetBlock{Method: "getblock"} + req.Params.BlockHash = hash + req.Params.Verbosity = 1 + err = s.Call(&req, &res) + + if err != nil { + return nil, errors.Annotatef(err, "hash %v", hash) + } + if res.Error != nil { + return nil, errors.Annotatef(res.Error, "hash %v", hash) + } + + txs := make([]bchain.Tx, 0, len(res.Result.Txids)) + for _, txid := range res.Result.Txids { + tx, err := s.GetTransaction(txid) + if err != nil { + if err == bchain.ErrTxNotFound { + glog.Errorf("rpc: getblock: skipping transanction in block %s due error: %s", hash, err) + continue + } + return nil, err + } + txs = append(txs, *tx) + } + block := &bchain.Block{ + BlockHeader: res.Result.BlockHeader, + Txs: txs, + } + return block, nil +} + +// GetTransactionForMempool returns a transaction by the transaction ID. +// It could be optimized for mempool, i.e. without block time and confirmations +func (s *DeepOnionRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) { + return s.GetTransaction(txid) +} diff --git a/build/templates/backend/config/deeponion.conf b/build/templates/backend/config/deeponion.conf new file mode 100644 index 00000000..ca92d14f --- /dev/null +++ b/build/templates/backend/config/deeponion.conf @@ -0,0 +1,29 @@ +{{define "main" -}} +daemon=1 +server=1 +{{if .Backend.Mainnet}}mainnet=1{{else}}testnet=1{{end}} +rpcuser={{.IPC.RPCUser}} +rpcpassword={{.IPC.RPCPass}} +rpcport={{.Ports.BackendRPC}} +txindex=1 + +zmqpubhashtx={{template "IPC.MessageQueueBindingTemplate" .}} +zmqpubhashblock={{template "IPC.MessageQueueBindingTemplate" .}} + +rpcworkqueue=1100 +maxmempool=2000 +dbcache=1000 + +{{- if .Backend.AdditionalParams}} +# generated from additional_params +{{- range $name, $value := .Backend.AdditionalParams}} +{{- if eq $name "addnode"}} +{{- range $index, $node := $value}} +addnode={{$node}} +{{- end}} +{{- else}} +{{$name}}={{$value}} +{{- end}} +{{- end}} +{{- end}} +{{end}} diff --git a/configs/coins/deeponion.json b/configs/coins/deeponion.json new file mode 100644 index 00000000..d5c42fd2 --- /dev/null +++ b/configs/coins/deeponion.json @@ -0,0 +1,67 @@ +{ + "coin": { + "name": "DeepOnion", + "shortcut": "ONION", + "label": "DeepOnion", + "alias": "deeponion" + }, + "ports": { + "backend_rpc": 8091, + "backend_message_queue": 38391, + "blockbook_internal": 9091, + "blockbook_public": 9191 + }, + "ipc": { + "rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_user": "rpc", + "rpc_pass": "rpc", + "rpc_timeout": 25, + "message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}" + }, + "backend": { + "package_name": "backend-deeponion", + "package_revision": "satoshilabs-1", + "system_user": "deeponion", + "version": "2.0.5", + "binary_url": "https://github.com/deeponion/deeponion/releases/download/v2.0.5/deeponion-2.0.5-x86_64-linux-gnu.tar.gz", + "extract_command": "tar -C backend --strip 1 -xpf", + "exclude_files": [ + ], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/DeepOniond -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log", + "postinst_script_template": "", + "service_type": "forking", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "deeponion.conf", + "client_config_file": "bitcoin_like_client.conf", + "additional_params": { + "whitelist": "127.0.0.1" + } + }, + "blockbook": { + "package_name": "blockbook-deeponion", + "system_user": "blockbook-deeponion", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "subversion": "/DeepOnionCore:2.0.5/", + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "xpub_magic": 27108450, + "xpub_magic_segwit_p2sh": 28471030, + "xpub_magic_segwit_native": 78792518, + "slip44": 305, + "additional_params": {} + } + }, + "meta": { + "package_maintainer": "DeepOnion", + "package_maintainer_email": "info@deeponion.org" + } +} diff --git a/docs/ports.md b/docs/ports.md index 835d5e07..0c63c18b 100644 --- a/docs/ports.md +++ b/docs/ports.md @@ -8,7 +8,7 @@ | Dash | 9033 | 9133 | 8033 | 38333 | | Litecoin | 9034 | 9134 | 8034 | 38334 | | Bitcoin Gold | 9035 | 9135 | 8035 | 38335 | -| Ethereum | 9036 | 9136 | 8036 | 8136 http, 38336 p2p | +| Ethereum | 9036 | 9136 | 8036 | 38336 p2p, 8136 http | | Ethereum Classic | 9037 | 9137 | 8037 | | | Dogecoin | 9038 | 9138 | 8038 | 38338 | | Namecoin | 9039 | 9139 | 8039 | 38339 | @@ -38,6 +38,7 @@ | Qtum | 9088 | 9188 | 8088 | 38388 | | Divi Project | 9089 | 9189 | 8089 | 38389 | | CPUchain | 9090 | 9190 | 8090 | 38390 | +| DeepOnion | 9091 | 9191 | 8091 | 38391 | | Unobtanium | 9092 | 9192 | 65535 | 38392 | | Bitcoin Testnet | 19030 | 19130 | 18030 | 48330 | | Bitcoin Cash Testnet | 19031 | 19131 | 18031 | 48331 | diff --git a/tests/rpc/testdata/deeponion.json b/tests/rpc/testdata/deeponion.json new file mode 100644 index 00000000..4fe9943d --- /dev/null +++ b/tests/rpc/testdata/deeponion.json @@ -0,0 +1,52 @@ +{ + "blockHeight": 1377592, + "blockHash": "9993684c15a793130f43120839e5c2c711907631ef4ad7b43ba535322e4c6649", + "blockTime": 1564554257, + "blockTxs": [ + "200b681e57d19131a87c3aa4172b9e2e128cc48ecdf54cc91dcbc44e96e8f7cf", + "6ba18524d81af732d0226ffdb63d2bcdc0d58a35ac97b5ad731057932d324e14" + ], + "txDetails": { + "e967e3be9b68bcbb5cf5db31816cd40574345a24e69807ee8a80a7f92baeca14": { + "hex": "0100000015cc175d016012730925e8e7eaff1a7cbd18e255f749e936730c001ca4b900a9a93230abbd0100000048473044022066a828d9d7d3e03dba2ad028bfdcad4e989ab76f11a7a033702759c37525bd8502206480c14e3c39a1d244e00c4113557bd9651d8b2b944426d88f8d7f95f8957c5601ffffffff03000000000000000000c0be295804000000232102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07acfbf73a5804000000232102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07ac00000000", + "txid": "e967e3be9b68bcbb5cf5db31816cd40574345a24e69807ee8a80a7f92baeca14", + "blocktime": 1561840661, + "time": 1561840661, + "locktime": 0, + "version": 1, + "vin": [ + { + "txid": "bdab3032a9a900b9a41c000c7336e949f755e218bd7c1affeae7e82509731260", + "vout": 1, + "scriptSig": { + "hex": "473044022066a828d9d7d3e03dba2ad028bfdcad4e989ab76f11a7a033702759c37525bd8502206480c14e3c39a1d244e00c4113557bd9651d8b2b944426d88f8d7f95f8957c5601" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00000000, + "n": 0, + "scriptPubKey": { + "hex": "" + } + }, + { + "value": 186.59, + "n": 1, + "scriptPubKey": { + "hex": "2102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07ac" + } + }, + { + "value": 186.60128763, + "n": 2, + "scriptPubKey": { + "hex": "2102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07ac" + } + } + ] + } + } +} diff --git a/tests/sync/testdata/deeponion.json b/tests/sync/testdata/deeponion.json new file mode 100644 index 00000000..b307dca7 --- /dev/null +++ b/tests/sync/testdata/deeponion.json @@ -0,0 +1,96 @@ +{ + "connectBlocks": { + "syncRanges": [ + {"lower": 1003776, "upper": 1003796} + ], + "blocks": { + "1003776": { + "height": 1003776, + "hash": "b9775282a6def6a186d29584af8907c991e27e91603ce461bf5c79890f020a45", + "noTxs": 2, + "txDetails": [ + { + "txid": "9f3400d241a594a5d7b039cad7c535226d481e486c8e54cf0e88d47149a53a79", + "version": 1, + "vin": [ + { + "txid": "c2b57977801b69472ac635e2d53cb53e652ff432caa91c47ae9c70bc8d4b27a6", + "vout": 0, + "scriptSig": { + "hex": "47304402203e68a85b0ae7fd4f6b4ab6abe44e091d08aba37dd787fc271467ffaee8c9733402205264badf478dbc18051331b2f53a43447f53f950ea98db5fdcca28ef179c122201" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00000000, + "n": 0, + "scriptPubKey": { + "hex": "" + } + }, + { + "value": 118.27, + "n": 1, + "scriptPubKey": { + "hex": "2103fb4259bbe5c1e956a6b804ab19812f0cd0938cf6151be81aa14e3ffa955dfcd6ac" + } + }, + { + "value": 118.28219178, + "n": 2, + "scriptPubKey": { + "hex": "2103fb4259bbe5c1e956a6b804ab19812f0cd0938cf6151be81aa14e3ffa955dfcd6ac" + } + } + ], + "hex": "010000006eed435c01a6274b8dbc709cae471ca9ca32f42f653eb53cd5e235c62a47691b807779b5c2010000004847304402203e68a85b0ae7fd4f6b4ab6abe44e091d08aba37dd787fc271467ffaee8c9733402205264badf478dbc18051331b2f53a43447f53f950ea98db5fdcca28ef179c122201ffffffff03000000000000000000c0b2f1c002000000232103fb4259bbe5c1e956a6b804ab19812f0cd0938cf6151be81aa14e3ffa955dfcd6ac2a4d04c102000000232103fb4259bbe5c1e956a6b804ab19812f0cd0938cf6151be81aa14e3ffa955dfcd6ac00000000", + "time": 1547955566, + "blocktime": 1547955566 + } + ] + }, + "1003796": { + "height": 1003796, + "hash": "8ec7b5006dd4b6683c6b58185064e94ab0b87f083ed4e58706f6f928cb4f9d58", + "noTxs": 2, + "txDetails": [ + { + "txid": "1d8a926a59f64b0b5a5efcb3872801f74dfe30d0ed68330bd98295f05f6c0580", + "version": 1, + "vin": [ + { + "txid": "7807e471847d0f66616653601d02b2ea482e97385d2c03daec77e000b62c10a1", + "vout": 1, + "scriptSig": { + "hex": "473044022033a999110dcadb95223db2028f5fcbf3a5b37ec48498ec4678eaff0f6c8787b002207100681343a265c9995681702f3694cbbffaadaf1bc42a3b6fd69e3a79896f1701" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00000000, + "n": 0, + "scriptPubKey": { + "hex": "" + } + }, + { + "value": 459.47739724, + "n": 1, + "scriptPubKey": { + "hex": "21025f48dc22185bc3f9428068600d779b9bbbe208a49ffbbe6af551000e70979cb7ac" + } + } + ], + "hex": "010000006eed435c01a6274b8dbc709cae471ca9ca32f42f653eb53cd5e235c62a47691b807779b5c2010000004847304402203e68a85b0ae7fd4f6b4ab6abe44e091d08aba37dd787fc271467ffaee8c9733402205264badf478dbc18051331b2f53a43447f53f950ea98db5fdcca28ef179c122201ffffffff03000000000000000000c0b2f1c002000000232103fb4259bbe5c1e956a6b804ab19812f0cd0938cf6151be81aa14e3ffa955dfcd6ac2a4d04c102000000232103fb4259bbe5c1e956a6b804ab19812f0cd0938cf6151be81aa14e3ffa955dfcd6ac00000000", + "time": 1547956478, + "blocktime": 1547956478 + } + ] + } + } + } +} diff --git a/tests/tests.json b/tests/tests.json index d555f383..7f935426 100644 --- a/tests/tests.json +++ b/tests/tests.json @@ -52,6 +52,11 @@ "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"], "sync": ["ConnectBlocksParallel", "ConnectBlocks"] }, + "deeponion": { + "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", + "EstimateSmartFee", "EstimateFee"], + "sync": ["ConnectBlocksParallel", "ConnectBlocks"] + }, "digibyte": { "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"] From dfafd780dc9c4c12cbc918d7b602fa5c7f107722 Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Mon, 18 Nov 2019 19:43:02 +0100 Subject: [PATCH 11/25] doge: Bump backend 0.14.0 -> 0.14.2 --- configs/coins/dogecoin.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/dogecoin.json b/configs/coins/dogecoin.json index 17f26379..7ed865c6 100644 --- a/configs/coins/dogecoin.json +++ b/configs/coins/dogecoin.json @@ -22,10 +22,10 @@ "package_name": "backend-dogecoin", "package_revision": "satoshilabs-1", "system_user": "dogecoin", - "version": "1.14.0", - "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.0/dogecoin-1.14.0-x86_64-linux-gnu.tar.gz", + "version": "1.14.2", + "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.2/dogecoin-1.14.2-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "ed7baccafab98b5ce452bd3fd2cf7ab5e757269105350283e2bab91e4ccb7366", + "verification_source": "10c400c8f2039b1f804b8a533266201a9e4e3b32a8854501e8a43792e1ee78e6", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dogecoin-qt" From c45312edf16813dd70aa61d51c5cfe9d4332ccd6 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 19 Nov 2019 11:13:15 +0100 Subject: [PATCH 12/25] Fix incorrect registration of network params in unobtanium and viacoin --- bchain/coins/dogecoin/dogecoinparser_test.go | 81 ++++++++++++++++++++ bchain/coins/unobtanium/unobtaniumparser.go | 12 +-- bchain/coins/viacoin/viacoinparser.go | 19 +++-- 3 files changed, 99 insertions(+), 13 deletions(-) diff --git a/bchain/coins/dogecoin/dogecoinparser_test.go b/bchain/coins/dogecoin/dogecoinparser_test.go index 5738290a..e2803fd3 100644 --- a/bchain/coins/dogecoin/dogecoinparser_test.go +++ b/bchain/coins/dogecoin/dogecoinparser_test.go @@ -46,6 +46,12 @@ func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) { want: "76a914efb6158f75743c611858fdfd0f4aaec6cc6196bc88ac", wantErr: false, }, + { + name: "P2PKH3", + args: args{address: "DHobAps6DjZ5n4xMV75n7kJv299Zi85FCG"}, + want: "76a9148ae937291e72f7368421dbaa966c44950eb14db788ac", + wantErr: false, + }, { name: "P2SH1", args: args{address: "9tg1kVUk339Tk58ewu5T8QT82Z6cE4UvSU"}, @@ -76,6 +82,81 @@ func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) { } } +func Test_GetAddressesFromAddrDesc_Mainnet(t *testing.T) { + type args struct { + script string + } + tests := []struct { + name string + args args + want []string + want2 bool + wantErr bool + }{ + { + name: "P2PKH1", + args: args{script: "76a9148841590909747c0f97af158f22fadacb1652522088ac"}, + want: []string{"DHZYinsaM9nW5piCMN639ELRKbZomThPnZ"}, + want2: true, + wantErr: false, + }, + { + name: "P2PKH2", + args: args{script: "76a914efb6158f75743c611858fdfd0f4aaec6cc6196bc88ac"}, + want: []string{"DSzaAYEYyy9ngjoJ294r7jzFM3xhD6bKHK"}, + want2: true, + wantErr: false, + }, + { + name: "P2PKH3", + args: args{script: "76a91450e86eeac599ad023b8981296d01b50bdabcdd9788ac"}, + want: []string{"DCWu3MLz9xBGFuuLyNDf6QjuGp49f5tfc9"}, + want2: true, + wantErr: false, + }, + { + name: "P2SH1", + args: args{script: "a9141889a089400ea25d28694fd98aa7702b21eeeab187"}, + want: []string{"9tg1kVUk339Tk58ewu5T8QT82Z6cE4UvSU"}, + want2: true, + wantErr: false, + }, + { + name: "OP_RETURN ascii", + args: args{script: "6a0461686f6a"}, + want: []string{"OP_RETURN (ahoj)"}, + want2: false, + wantErr: false, + }, + { + name: "OP_RETURN hex", + args: args{script: "6a072020f1686f6a20"}, + want: []string{"OP_RETURN 2020f1686f6a20"}, + want2: false, + wantErr: false, + }, + } + + parser := NewDogecoinParser(GetChainParams("main"), &btc.Configuration{}) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b, _ := hex.DecodeString(tt.args.script) + got, got2, err := parser.GetAddressesFromAddrDesc(b) + if (err != nil) != tt.wantErr { + t.Errorf("GetAddressesFromAddrDesc() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got, tt.want) + } + if !reflect.DeepEqual(got2, tt.want2) { + t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.want2) + } + }) + } +} + var ( testTx1 bchain.Tx testTxPacked1 = "00030e6d8ba8d7aa2001000000016b3c0c53267964120acf7f7e72217e3f463e52ce622f89659f6a6bb8e69a4d91000000006c493046022100a96454237e3a020994534583e28c04757881374bceac89f933ea9ff00b4db259022100fbb757ff7ea4f02c4e42556b2834c61eba1f1af605db089d836a0614d90a3b46012103cebdde6d1046e285df4f48497bc50dc20a4a258ca5b7308cb0a929c9fdadcd9dffffffff0217e823ca7f0200001976a914eef21768a546590993e313c7f3dfadf6a6efa1e888acaddf4cba010000001976a914e0fee2ea29dd9c6c759d8341bd0da4c4f738cced88ac00000000" diff --git a/bchain/coins/unobtanium/unobtaniumparser.go b/bchain/coins/unobtanium/unobtaniumparser.go index eac2cc95..aab02aa4 100644 --- a/bchain/coins/unobtanium/unobtaniumparser.go +++ b/bchain/coins/unobtanium/unobtaniumparser.go @@ -27,11 +27,6 @@ func init() { // Mainnet address encoding magics MainNetParams.PubKeyHashAddrID = []byte{130} MainNetParams.ScriptHashAddrID = []byte{30} - - err := chaincfg.Register(&MainNetParams) - if err != nil { - panic(err) - } } // UnobtaniumParser handle @@ -44,7 +39,14 @@ func NewUnobtaniumParser(params *chaincfg.Params, c *btc.Configuration) *Unobtan return &UnobtaniumParser{BitcoinParser: btc.NewBitcoinParser(params, c)} } +// GetChainParams returns network parameters func GetChainParams(chain string) *chaincfg.Params { + if !chaincfg.IsRegistered(&MainNetParams) { + err := chaincfg.Register(&MainNetParams) + if err != nil { + panic(err) + } + } switch chain { default: return &MainNetParams diff --git a/bchain/coins/viacoin/viacoinparser.go b/bchain/coins/viacoin/viacoinparser.go index d0583ecd..3617e90a 100644 --- a/bchain/coins/viacoin/viacoinparser.go +++ b/bchain/coins/viacoin/viacoinparser.go @@ -38,14 +38,6 @@ func init() { RegtestParams.PubKeyHashAddrID = []byte{111} // base58 prefix: m or n RegtestParams.ScriptHashAddrID = []byte{196} // base58 prefix: 2 RegtestParams.Bech32HRPSegwit = "tvia" - - err := chaincfg.Register(&MainNetParams) - if err == nil { - err = chaincfg.Register(&RegtestParams) - } - if err != nil { - panic(err) - } } // ViacoinParser handle @@ -58,7 +50,18 @@ func NewViacoinParser(params *chaincfg.Params, c *btc.Configuration) *ViacoinPar return &ViacoinParser{BitcoinParser: btc.NewBitcoinParser(params, c)} } +// GetChainParams returns network parameters func GetChainParams(chain string) *chaincfg.Params { + if !chaincfg.IsRegistered(&MainNetParams) { + err := chaincfg.Register(&MainNetParams) + if err == nil { + err = chaincfg.Register(&RegtestParams) + } + if err != nil { + panic(err) + } + } + switch chain { case "regtest": return &RegtestParams From f7bbffa4c9083c2b5ba83cace6acde82da593653 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 19 Nov 2019 11:15:00 +0100 Subject: [PATCH 13/25] Fix formatting/linting issues --- bchain/coins/blockchain.go | 4 +- bchain/coins/deeponion/deeponionparser.go | 6 +- .../coins/deeponion/deeponionparser_test.go | 3 +- bchain/coins/deeponion/deeponionrpc.go | 74 +++++++++---------- 4 files changed, 43 insertions(+), 44 deletions(-) diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 0ce5e626..328ddeb5 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -9,7 +9,7 @@ import ( "blockbook/bchain/coins/cpuchain" "blockbook/bchain/coins/dash" "blockbook/bchain/coins/dcr" - "blockbook/bchain/coins/deeponion" + "blockbook/bchain/coins/deeponion" "blockbook/bchain/coins/digibyte" "blockbook/bchain/coins/divi" "blockbook/bchain/coins/dogecoin" @@ -104,7 +104,7 @@ func init() { BlockChainFactories["Divi"] = divi.NewDiviRPC BlockChainFactories["CPUchain"] = cpuchain.NewCPUchainRPC BlockChainFactories["Unobtanium"] = unobtanium.NewUnobtaniumRPC - BlockChainFactories["DeepOnion"] = deeponion.NewDeepOnionRPC + BlockChainFactories["DeepOnion"] = deeponion.NewDeepOnionRPC } // GetCoinNameFromConfig gets coin name and coin shortcut from config file diff --git a/bchain/coins/deeponion/deeponionparser.go b/bchain/coins/deeponion/deeponionparser.go index 7e63afe3..fedfdeea 100644 --- a/bchain/coins/deeponion/deeponionparser.go +++ b/bchain/coins/deeponion/deeponionparser.go @@ -1,7 +1,7 @@ package deeponion import ( - "blockbook/bchain" + "blockbook/bchain" "blockbook/bchain/coins/btc" "github.com/martinboehm/btcd/wire" @@ -35,8 +35,8 @@ type DeepOnionParser struct { // NewDeepOnionParser returns new DeepOnionParser instance func NewDeepOnionParser(params *chaincfg.Params, c *btc.Configuration) *DeepOnionParser { return &DeepOnionParser{ - BitcoinParser: btc.NewBitcoinParser(params, c), - baseparser: &bchain.BaseParser{}, + BitcoinParser: btc.NewBitcoinParser(params, c), + baseparser: &bchain.BaseParser{}, } } diff --git a/bchain/coins/deeponion/deeponionparser_test.go b/bchain/coins/deeponion/deeponionparser_test.go index a19fb097..94c5594f 100644 --- a/bchain/coins/deeponion/deeponionparser_test.go +++ b/bchain/coins/deeponion/deeponionparser_test.go @@ -20,7 +20,6 @@ func TestMain(m *testing.M) { os.Exit(c) } - func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) { type args struct { address string @@ -191,7 +190,7 @@ func Test_UnpackTx(t *testing.T) { return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("unpackTx(2) got = %v, want %v", got, tt.want) + t.Errorf("unpackTx(2) got = %v, want %v", got, tt.want) } if got1 != tt.want1 { t.Errorf("unpackTx(3) got1 = %v, want %v", got1, tt.want1) diff --git a/bchain/coins/deeponion/deeponionrpc.go b/bchain/coins/deeponion/deeponionrpc.go index 6fab0c49..b651f93b 100644 --- a/bchain/coins/deeponion/deeponionrpc.go +++ b/bchain/coins/deeponion/deeponionrpc.go @@ -60,50 +60,50 @@ func (b *DeepOnionRPC) Initialize() error { // GetBlock returns block with given hash. func (s *DeepOnionRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { - var err error - if hash == "" && height > 0 { - hash, err = s.GetBlockHash(height) - if err != nil { - return nil, err - } - } + var err error + if hash == "" && height > 0 { + hash, err = s.GetBlockHash(height) + if err != nil { + return nil, err + } + } - glog.V(1).Info("rpc: getblock (verbosity=1) ", hash) + glog.V(1).Info("rpc: getblock (verbosity=1) ", hash) - res := btc.ResGetBlockThin{} - req := btc.CmdGetBlock{Method: "getblock"} - req.Params.BlockHash = hash - req.Params.Verbosity = 1 - err = s.Call(&req, &res) + res := btc.ResGetBlockThin{} + req := btc.CmdGetBlock{Method: "getblock"} + req.Params.BlockHash = hash + req.Params.Verbosity = 1 + err = s.Call(&req, &res) - if err != nil { - return nil, errors.Annotatef(err, "hash %v", hash) - } - if res.Error != nil { - return nil, errors.Annotatef(res.Error, "hash %v", hash) - } + if err != nil { + return nil, errors.Annotatef(err, "hash %v", hash) + } + if res.Error != nil { + return nil, errors.Annotatef(res.Error, "hash %v", hash) + } - txs := make([]bchain.Tx, 0, len(res.Result.Txids)) - for _, txid := range res.Result.Txids { - tx, err := s.GetTransaction(txid) - if err != nil { - if err == bchain.ErrTxNotFound { - glog.Errorf("rpc: getblock: skipping transanction in block %s due error: %s", hash, err) - continue - } - return nil, err - } - txs = append(txs, *tx) - } - block := &bchain.Block{ - BlockHeader: res.Result.BlockHeader, - Txs: txs, - } - return block, nil + txs := make([]bchain.Tx, 0, len(res.Result.Txids)) + for _, txid := range res.Result.Txids { + tx, err := s.GetTransaction(txid) + if err != nil { + if err == bchain.ErrTxNotFound { + glog.Errorf("rpc: getblock: skipping transanction in block %s due error: %s", hash, err) + continue + } + return nil, err + } + txs = append(txs, *tx) + } + block := &bchain.Block{ + BlockHeader: res.Result.BlockHeader, + Txs: txs, + } + return block, nil } // GetTransactionForMempool returns a transaction by the transaction ID. // It could be optimized for mempool, i.e. without block time and confirmations func (s *DeepOnionRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) { - return s.GetTransaction(txid) + return s.GetTransaction(txid) } From e4c6d233894617fed26773546098ca6ce988b5b1 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 19 Nov 2019 11:56:40 +0100 Subject: [PATCH 14/25] Fix error message informing about missing Ethereum xpub support #314 --- server/public.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/public.go b/server/public.go index a966347b..ba73c09b 100644 --- a/server/public.go +++ b/server/public.go @@ -682,6 +682,9 @@ func (s *PublicServer) explorerXpub(w http.ResponseWriter, r *http.Request) (tpl // do not allow txsOnPage and details to be changed by query params address, err := s.api.GetXpubAddress(xpub, page, txsOnPage, api.AccountDetailsTxHistoryLight, filter, gap) if err != nil { + if err == api.ErrUnsupportedXpub { + err = api.NewAPIError("XPUB functionality is not supported", true) + } return errorTpl, nil, err } data := s.newTemplateData() @@ -992,6 +995,9 @@ func (s *PublicServer) apiXpub(r *http.Request, apiVersion int) (interface{}, er if err == nil && apiVersion == apiV1 { return s.api.AddressToV1(address), nil } + if err == api.ErrUnsupportedXpub { + err = api.NewAPIError("XPUB functionality is not supported", true) + } return address, err } From 0f4f71d2b09ebe5168f028974795076f0142586b Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Fri, 22 Nov 2019 19:07:35 +0100 Subject: [PATCH 15/25] dash (+testnet): Bump backend 0.14.0.3 -> 0.14.0.4 --- configs/coins/dash.json | 8 ++++---- configs/coins/dash_testnet.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/configs/coins/dash.json b/configs/coins/dash.json index b9eb8f1f..a08da006 100644 --- a/configs/coins/dash.json +++ b/configs/coins/dash.json @@ -22,10 +22,10 @@ "package_name": "backend-dash", "package_revision": "satoshilabs-1", "system_user": "dash", - "version": "0.14.0.3", - "binary_url": "https://github.com/dashpay/dash/releases/download/v0.14.0.3/dashcore-0.14.0.3-x86_64-linux-gnu.tar.gz", + "version": "0.14.0.4", + "binary_url": "https://github.com/dashpay/dash/releases/download/v0.14.0.4/dashcore-0.14.0.4-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v0.14.0.3/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v0.14.0.4/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" @@ -52,7 +52,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Dash Core:0.14.0.3/", + "subversion": "/Dash Core:0.14.0.4/", "mempool_workers": 8, "mempool_sub_workers": 2, "block_addresses_to_keep": 300, diff --git a/configs/coins/dash_testnet.json b/configs/coins/dash_testnet.json index ba60b887..32ae5f07 100644 --- a/configs/coins/dash_testnet.json +++ b/configs/coins/dash_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-dash-testnet", "package_revision": "satoshilabs-1", "system_user": "dash", - "version": "0.14.0.3", - "binary_url": "https://github.com/dashpay/dash/releases/download/v0.14.0.3/dashcore-0.14.0.3-x86_64-linux-gnu.tar.gz", + "version": "0.14.0.4", + "binary_url": "https://github.com/dashpay/dash/releases/download/v0.14.0.4/dashcore-0.14.0.4-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v0.14.0.3/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v0.14.0.4/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" @@ -52,7 +52,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Dash Core:0.14.0.3/", + "subversion": "/Dash Core:0.14.0.4/", "mempool_workers": 8, "mempool_sub_workers": 2, "block_addresses_to_keep": 300, From 0baf02c6b8e7af59f897dd52609ac754f7ed7465 Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Sun, 24 Nov 2019 23:09:42 +0100 Subject: [PATCH 16/25] btc (+testnet): Bump backend 0.18.1 -> 0.19.0.1 --- configs/coins/bitcoin.json | 10 +++++----- configs/coins/bitcoin_testnet.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index ef6e2f3d..c9096f75 100644 --- a/configs/coins/bitcoin.json +++ b/configs/coins/bitcoin.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "0.18.1", - "binary_url": "https://bitcoin.org/bin/bitcoin-core-0.18.1/bitcoin-0.18.1-x86_64-linux-gnu.tar.gz", + "version": "0.19.0.1", + "binary_url": "https://bitcoin.org/bin/bitcoin-core-0.19.0.1/bitcoin-0.19.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://bitcoin.org/bin/bitcoin-core-0.18.1/SHA256SUMS.asc", + "verification_source": "https://bitcoin.org/bin/bitcoin-core-0.19.0.1/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -65,7 +65,7 @@ } }, "meta": { - "package_maintainer": "Martin Bohm", - "package_maintainer_email": "martin.bohm@satoshilabs.com" + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" } } diff --git a/configs/coins/bitcoin_testnet.json b/configs/coins/bitcoin_testnet.json index 8a86a1f1..2fa70af7 100644 --- a/configs/coins/bitcoin_testnet.json +++ b/configs/coins/bitcoin_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-testnet", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "0.18.1", - "binary_url": "https://bitcoin.org/bin/bitcoin-core-0.18.1/bitcoin-0.18.1-x86_64-linux-gnu.tar.gz", + "version": "0.19.0.1", + "binary_url": "https://bitcoin.org/bin/bitcoin-core-0.19.0.1/bitcoin-0.19.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://bitcoin.org/bin/bitcoin-core-0.18.1/SHA256SUMS.asc", + "verification_source": "https://bitcoin.org/bin/bitcoin-core-0.19.0.1/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -63,7 +63,7 @@ } }, "meta": { - "package_maintainer": "Martin Bohm", - "package_maintainer_email": "martin.bohm@satoshilabs.com" + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" } } From b3367f8f8cabaf0ff98a55952ae05dfa48219437 Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Wed, 27 Nov 2019 09:59:42 +0100 Subject: [PATCH 17/25] eth (+testnet): Bump backend 1.9.7 -> 1.9.8 --- configs/coins/ethereum.json | 6 +++--- configs/coins/ethereum_testnet_ropsten.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 5c89ef26..9af6568c 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -21,10 +21,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.9.7-a718daa6", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.7-a718daa6.tar.gz", + "version": "1.9.8-d62e9b28", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.8-d62e9b28.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.7-a718daa6.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.8-d62e9b28.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ipcdisable --syncmode full --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --wsaddr 0.0.0.0 --wsport {{.Ports.BackendRPC}} --wsorigins \"*\" --rpc --rpcport 8136 -rpcaddr 0.0.0.0 --rpccorsdomain \"*\" --rpcvhosts \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 1d193182..00b796dd 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -20,10 +20,10 @@ "package_name": "backend-ethereum-testnet-ropsten", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.9.7-a718daa6", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.7-a718daa6.tar.gz", + "version": "1.9.8-d62e9b28", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.8-d62e9b28.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.7-a718daa6.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.8-d62e9b28.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --testnet --syncmode full --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --wsaddr 0.0.0.0 --wsport {{.Ports.BackendRPC}} --wsorigins \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From f94878b234548b87c1185d64de36a6f3c351e5fe Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Fri, 29 Nov 2019 10:41:16 +0100 Subject: [PATCH 18/25] bch (+testnet): Bump backend 0.20.6 -> 0.20.7 --- configs/coins/bcash.json | 8 ++++---- configs/coins/bcash_testnet.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/configs/coins/bcash.json b/configs/coins/bcash.json index 35a2d8e5..5e5d29e2 100644 --- a/configs/coins/bcash.json +++ b/configs/coins/bcash.json @@ -22,10 +22,10 @@ "package_name": "backend-bcash", "package_revision": "satoshilabs-1", "system_user": "bcash", - "version": "0.20.6", - "binary_url": "https://download.bitcoinabc.org/0.20.6/linux/bitcoin-abc-0.20.6-x86_64-linux-gnu.tar.gz", + "version": "0.20.7", + "binary_url": "https://download.bitcoinabc.org/0.20.7/linux/bitcoin-abc-0.20.7-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "f5fb4a3dac6164709dbf0632f64adfe56414d260e4bf192effc0049da9c43bac", + "verification_source": "a8ccb15b2eb37e7dc0790fb72e5f31fba9c7df5134df2110eaf59724636b64ef", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -49,7 +49,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Bitcoin ABC:0.20.6/", + "subversion": "/Bitcoin ABC:0.20.7/", "address_format": "cashaddr", "mempool_workers": 8, "mempool_sub_workers": 2, diff --git a/configs/coins/bcash_testnet.json b/configs/coins/bcash_testnet.json index a7c90fed..471fed14 100644 --- a/configs/coins/bcash_testnet.json +++ b/configs/coins/bcash_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-bcash-testnet", "package_revision": "satoshilabs-1", "system_user": "bcash", - "version": "0.20.6", - "binary_url": "https://download.bitcoinabc.org/0.20.6/linux/bitcoin-abc-0.20.6-x86_64-linux-gnu.tar.gz", + "version": "0.20.7", + "binary_url": "https://download.bitcoinabc.org/0.20.7/linux/bitcoin-abc-0.20.7-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "f5fb4a3dac6164709dbf0632f64adfe56414d260e4bf192effc0049da9c43bac", + "verification_source": "a8ccb15b2eb37e7dc0790fb72e5f31fba9c7df5134df2110eaf59724636b64ef", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -49,7 +49,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Bitcoin ABC:0.20.6/", + "subversion": "/Bitcoin ABC:0.20.7/", "address_format": "cashaddr", "mempool_workers": 8, "mempool_sub_workers": 2, From 630ef1d3088bc2f415656cd0f3b9a021eb16aa35 Mon Sep 17 00:00:00 2001 From: thebevrishot Date: Tue, 3 Dec 2019 13:11:46 +0700 Subject: [PATCH 19/25] Bump Zcoin to 0.13.8.8 --- configs/coins/zcoin.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/configs/coins/zcoin.json b/configs/coins/zcoin.json index c239f8a7..420dcdd1 100644 --- a/configs/coins/zcoin.json +++ b/configs/coins/zcoin.json @@ -22,15 +22,16 @@ "package_name": "backend-zcoin", "package_revision": "satoshilabs-1", "system_user": "zcoin", - "version": "0.13.8.5", - "binary_url": "https://github.com/zcoinofficial/zcoin/releases/download/v0.13.8.5/zcoin-0.13.8.5-linux64.tar.gz", + "version": "0.13.8.8", + "binary_url": "https://github.com/zcoinofficial/zcoin/releases/download/v0.13.8.8/zcoin-0.13.8.8-linux64.tar.gz", "verification_type": "sha256", - "verification_source": "52f8a722bb1cc5c53e77edf81f6f1300f3b15d53f9cb4fcee90367f11ad84dd9", + "verification_source": "badab4c7e42cb7ce567c1e02a700b52f7ea3f55780e4e180b89596ad940f7189", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/tor", "bin/tor-gencert", "bin/tor-print-ed-signing-cert", + "bin/torify", "bin/tor-resolve", "bin/zcoin-qt", "bin/zcoin-tx", From f09b8ef683fb8f4ff454c0f7c728f6188755ee15 Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Fri, 6 Dec 2019 13:20:55 +0100 Subject: [PATCH 20/25] eth (+testnet): Bump backend 1.9.8 -> 1.9.9 --- configs/coins/ethereum.json | 6 +++--- configs/coins/ethereum_testnet_ropsten.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 9af6568c..cafac864 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -21,10 +21,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.9.8-d62e9b28", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.8-d62e9b28.tar.gz", + "version": "1.9.9-01744997", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.9-01744997.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.8-d62e9b28.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.9-01744997.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ipcdisable --syncmode full --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --wsaddr 0.0.0.0 --wsport {{.Ports.BackendRPC}} --wsorigins \"*\" --rpc --rpcport 8136 -rpcaddr 0.0.0.0 --rpccorsdomain \"*\" --rpcvhosts \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 00b796dd..9ed31ec8 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -20,10 +20,10 @@ "package_name": "backend-ethereum-testnet-ropsten", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.9.8-d62e9b28", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.8-d62e9b28.tar.gz", + "version": "1.9.9-01744997", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.9-01744997.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.8-d62e9b28.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.9-01744997.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --testnet --syncmode full --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --wsaddr 0.0.0.0 --wsport {{.Ports.BackendRPC}} --wsorigins \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From 2a818d8780e3ac5229c993da397f9ad47f847912 Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Sat, 7 Dec 2019 11:30:00 +0100 Subject: [PATCH 21/25] nmc: Bump backend 0.18.0 -> 0.19.0 --- configs/coins/namecoin.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/namecoin.json b/configs/coins/namecoin.json index 5e73cae7..d77ba27e 100644 --- a/configs/coins/namecoin.json +++ b/configs/coins/namecoin.json @@ -22,10 +22,10 @@ "package_name": "backend-namecoin", "package_revision": "satoshilabs-1", "system_user": "namecoin", - "version": "0.18.0", - "binary_url": "https://beta.namecoin.org/files/namecoin-core/namecoin-core-0.18.0/namecoin-0.18.0-x86_64-linux-gnu.tar.gz", + "version": "0.19.0", + "binary_url": "https://www.namecoin.org/files/namecoin-core/namecoin-core-0.19.0/namecoin-0.19.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "27a1bbe2fe89bd60519adf48c08058f7b572ebc158a91454541d3f6174ab7256", + "verification_source": "43d3a0084a5cffce819911162a3191165942db9fcd5da8783fe48ca21327c1bb", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/namecoin-qt" From d092c42e216f3183efaa16e9056fd29942fd5ede Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Tue, 10 Dec 2019 19:59:13 +0100 Subject: [PATCH 22/25] nmc: Bump backend 0.19.0 -> 0.19.0.1 --- configs/coins/namecoin.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/namecoin.json b/configs/coins/namecoin.json index d77ba27e..7c2bc93b 100644 --- a/configs/coins/namecoin.json +++ b/configs/coins/namecoin.json @@ -22,10 +22,10 @@ "package_name": "backend-namecoin", "package_revision": "satoshilabs-1", "system_user": "namecoin", - "version": "0.19.0", - "binary_url": "https://www.namecoin.org/files/namecoin-core/namecoin-core-0.19.0/namecoin-0.19.0-x86_64-linux-gnu.tar.gz", + "version": "0.19.0.1", + "binary_url": "https://www.namecoin.org/files/namecoin-core/namecoin-core-0.19.0.1/namecoin-0.19.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "43d3a0084a5cffce819911162a3191165942db9fcd5da8783fe48ca21327c1bb", + "verification_source": "16acef267c5458ea3f5a7fdfd3482bb32102143ce24a94951a044dc53bc7021b", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/namecoin-qt" From 80aa7bc4a4e77072b28b967a00a39463d938ff61 Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Tue, 10 Dec 2019 20:22:08 +0100 Subject: [PATCH 23/25] dash (+testnet): Bump backend 0.14.0.4 -> 0.14.0.5 --- configs/coins/dash.json | 8 ++++---- configs/coins/dash_testnet.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/configs/coins/dash.json b/configs/coins/dash.json index a08da006..b4e8ba65 100644 --- a/configs/coins/dash.json +++ b/configs/coins/dash.json @@ -22,10 +22,10 @@ "package_name": "backend-dash", "package_revision": "satoshilabs-1", "system_user": "dash", - "version": "0.14.0.4", - "binary_url": "https://github.com/dashpay/dash/releases/download/v0.14.0.4/dashcore-0.14.0.4-x86_64-linux-gnu.tar.gz", + "version": "0.14.0.5", + "binary_url": "https://github.com/dashpay/dash/releases/download/v0.14.0.5/dashcore-0.14.0.5-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v0.14.0.4/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v0.14.0.5/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" @@ -52,7 +52,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Dash Core:0.14.0.4/", + "subversion": "/Dash Core:0.14.0.5/", "mempool_workers": 8, "mempool_sub_workers": 2, "block_addresses_to_keep": 300, diff --git a/configs/coins/dash_testnet.json b/configs/coins/dash_testnet.json index 32ae5f07..7fbb63ae 100644 --- a/configs/coins/dash_testnet.json +++ b/configs/coins/dash_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-dash-testnet", "package_revision": "satoshilabs-1", "system_user": "dash", - "version": "0.14.0.4", - "binary_url": "https://github.com/dashpay/dash/releases/download/v0.14.0.4/dashcore-0.14.0.4-x86_64-linux-gnu.tar.gz", + "version": "0.14.0.5", + "binary_url": "https://github.com/dashpay/dash/releases/download/v0.14.0.5/dashcore-0.14.0.5-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v0.14.0.4/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v0.14.0.5/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" @@ -52,7 +52,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Dash Core:0.14.0.4/", + "subversion": "/Dash Core:0.14.0.5/", "mempool_workers": 8, "mempool_sub_workers": 2, "block_addresses_to_keep": 300, From e2b34afb9c2f6eb157d23216f137afb16dbdcce6 Mon Sep 17 00:00:00 2001 From: Jan Hrnko Date: Wed, 11 Dec 2019 13:49:45 +0100 Subject: [PATCH 24/25] bch (+testnet): Bump backend 0.20.7 -> 0.20.8 --- configs/coins/bcash.json | 8 ++++---- configs/coins/bcash_testnet.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/configs/coins/bcash.json b/configs/coins/bcash.json index 5e5d29e2..99cb6599 100644 --- a/configs/coins/bcash.json +++ b/configs/coins/bcash.json @@ -22,10 +22,10 @@ "package_name": "backend-bcash", "package_revision": "satoshilabs-1", "system_user": "bcash", - "version": "0.20.7", - "binary_url": "https://download.bitcoinabc.org/0.20.7/linux/bitcoin-abc-0.20.7-x86_64-linux-gnu.tar.gz", + "version": "0.20.8", + "binary_url": "https://download.bitcoinabc.org/0.20.8/linux/bitcoin-abc-0.20.8-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "a8ccb15b2eb37e7dc0790fb72e5f31fba9c7df5134df2110eaf59724636b64ef", + "verification_source": "cb420f65244dd7514ed3f71be710e5704f075a97c35fd54b470b87b29440eb3b", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -49,7 +49,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Bitcoin ABC:0.20.7/", + "subversion": "/Bitcoin ABC:0.20.8/", "address_format": "cashaddr", "mempool_workers": 8, "mempool_sub_workers": 2, diff --git a/configs/coins/bcash_testnet.json b/configs/coins/bcash_testnet.json index 471fed14..658b97a7 100644 --- a/configs/coins/bcash_testnet.json +++ b/configs/coins/bcash_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-bcash-testnet", "package_revision": "satoshilabs-1", "system_user": "bcash", - "version": "0.20.7", - "binary_url": "https://download.bitcoinabc.org/0.20.7/linux/bitcoin-abc-0.20.7-x86_64-linux-gnu.tar.gz", + "version": "0.20.8", + "binary_url": "https://download.bitcoinabc.org/0.20.8/linux/bitcoin-abc-0.20.8-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "a8ccb15b2eb37e7dc0790fb72e5f31fba9c7df5134df2110eaf59724636b64ef", + "verification_source": "cb420f65244dd7514ed3f71be710e5704f075a97c35fd54b470b87b29440eb3b", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -49,7 +49,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Bitcoin ABC:0.20.7/", + "subversion": "/Bitcoin ABC:0.20.8/", "address_format": "cashaddr", "mempool_workers": 8, "mempool_sub_workers": 2, From f6111af5da2621d4167ba197dcc011388ac2cbaf Mon Sep 17 00:00:00 2001 From: Vladyslav Burzakovskyy Date: Tue, 17 Dec 2019 10:40:02 +0100 Subject: [PATCH 25/25] Add fiat rates functionality (#316) * Add initial commit for fiat rates functionality * templates.go: use bash from current user's environment * bitcoinrpc.go: add FiatRates and FiatRatesParams to config * blockbook.go: add initFiatRatesDownloader kickoff * bitcoin.json: add coingecko API URL * rockdb.go: add FindTicker and StoreTicker functions * rocksdb_test.go: add a simple test for storing and getting FiatRate tickers * rocksdb: add FindLastTicker and convertDate, make FindTicker return strings * rocksdb: add ConvertDate function and CoinGeckoTicker struct, update tests * blockbook.go, fiat: finalize the CoinGecko downloader * coingecko.go: do not stop syncing when encountered an error * rocksdb_test: fix the exported function name * worker.go: make getBlockInfoFromBlockID a public function * public.go: apiTickers kickoff * rocksdb_test: fix the unittest comment * coingecko.go: update comments * blockbook.go, fiat: reword CoinGecko -> FiatRates, fix binary search upper bound, remove assignment of goroutine call result * rename coingecko -> fiat_rates * fiat_rates: export only the necessary methods * blockbook.go: update log message * bitcoinrpc.go: remove fiatRates settings * use CurrencyRatesTicker structure everywhere, fix time format string, update tests, use UTC time * add /api/v2/tickers tests, store rates as strings (json.Number) * fiat_rates: add more tests, metrics and tickers-list endpoint, make the "currency" parameter mandatory * public, worker: move FiatRates API logic to worker.go * fiat_rates: add a future date test, fix comments, add more checks, store time as a pointer * rocksdb_test: remove unneeded code * fiat_rates: add a "ping" call to check server availability * fiat_rates: do not return empty ticker, return nil instead if not found add a test for non-existent ticker * rocksdb_test: remove Sleep from tests * worker.go: do not propagate all API errors to the client * move InitTestFiatRates from rocksdb.go to public_test.go * public.go: fix FiatRatesFindLastTicker result check * fiat_rates: mock API server responses * remove commented-out code * fiat_rates: add comment explaining what periodSeconds attribute is used for * websocket.go: implement fiatRates websocket endpoints & add tests * fiatRates: add getFiatRatesTickersList websocket endpoint & test * fiatRates: make websocket getFiatRatesByDate accept an array of dates, add more tests * fiatRates: remove getFiatRatesForBlockID from websocket endpoints * fiatRates: remove "if test", use custom startTime instead Update tests and mock data * fiatRates: finalize websocket functionality add "date" parameter to TickerList return data timestamps where needed fix sync bugs (nil timestamp, duplicate save) * fiatRates: add FiatRates configs for different coins * worker.go: make GetBlockInfoFromBlockID private again * fiatRates: wait & retry on errors, remove Ping function * websocket.go: remove incorrect comment * fiatRates: move coingecko-related code to a separate file, use interface * fiatRates: if the new rates are the same as previous, try five more times, and only then store them * coingecko: fix getting actual rates, add a timestamp parameter to get uncached responses * vertcoin_testnet.json: remove fiat rates parameters * fiat_rates: add timestamp to log message about skipping the repeating rates --- api/worker.go | 141 ++++++++++ bchain/coins/btc/bitcoinrpc.go | 4 +- blockbook.go | 78 ++++-- build/tools/templates.go | 2 +- configs/coins/bcash.json | 7 +- configs/coins/bgold.json | 7 +- configs/coins/bitcoin.json | 6 +- configs/coins/bitcoin_testnet.json | 5 +- configs/coins/dash.json | 7 +- configs/coins/digibyte.json | 7 +- configs/coins/dogecoin.json | 7 +- configs/coins/ethereum-classic.json | 6 +- configs/coins/ethereum.json | 4 +- configs/coins/ethereum_testnet_ropsten.json | 4 +- configs/coins/litecoin.json | 7 +- configs/coins/namecoin.json | 7 +- configs/coins/vertcoin.json | 7 +- configs/coins/zcash.json | 7 +- db/rocksdb.go | 132 ++++++++- db/rocksdb_test.go | 72 +++++ fiat/coingecko.go | 136 ++++++++++ fiat/fiat_rates.go | 214 +++++++++++++++ fiat/fiat_rates_test.go | 176 ++++++++++++ fiat/mock_data/01-02-2013.json | 1 + fiat/mock_data/01-05-2013.json | 1 + fiat/mock_data/04-04-2013.json | 1 + fiat/mock_data/05-05-2013.json | 1 + fiat/mock_data/05-06-2013.json | 1 + fiat/mock_data/07-10-2013.json | 1 + fiat/mock_data/13-06-2014.json | 1 + fiat/mock_data/20-04-2013.json | 1 + fiat/mock_data/20-11-2019.json | 1 + fiat/mock_data/21-11-2019.json | 1 + fiat/mock_data/22-11-2019.json | 1 + fiat/mock_data/23-09-2011.json | 1 + fiat/mock_data/27-04-2013.json | 1 + fiat/mock_data/28-04-2013.json | 1 + fiat/mock_data/29-04-2013.json | 1 + fiat/mock_data/current.json | 1 + server/public.go | 44 +++ server/public_test.go | 285 ++++++++++++++++++++ server/websocket.go | 181 +++++++++++-- static/test-websocket.html | 118 +++++++- 43 files changed, 1612 insertions(+), 75 deletions(-) create mode 100644 fiat/coingecko.go create mode 100644 fiat/fiat_rates.go create mode 100644 fiat/fiat_rates_test.go create mode 100644 fiat/mock_data/01-02-2013.json create mode 100644 fiat/mock_data/01-05-2013.json create mode 100644 fiat/mock_data/04-04-2013.json create mode 100644 fiat/mock_data/05-05-2013.json create mode 100644 fiat/mock_data/05-06-2013.json create mode 100644 fiat/mock_data/07-10-2013.json create mode 100644 fiat/mock_data/13-06-2014.json create mode 100644 fiat/mock_data/20-04-2013.json create mode 100644 fiat/mock_data/20-11-2019.json create mode 100644 fiat/mock_data/21-11-2019.json create mode 100644 fiat/mock_data/22-11-2019.json create mode 100644 fiat/mock_data/23-09-2011.json create mode 100644 fiat/mock_data/27-04-2013.json create mode 100644 fiat/mock_data/28-04-2013.json create mode 100644 fiat/mock_data/29-04-2013.json create mode 100644 fiat/mock_data/current.json diff --git a/api/worker.go b/api/worker.go index b2032c58..b2fe1fdd 100644 --- a/api/worker.go +++ b/api/worker.go @@ -13,6 +13,7 @@ import ( "os" "sort" "strconv" + "strings" "time" "github.com/golang/glog" @@ -972,6 +973,146 @@ func (w *Worker) GetBlocks(page int, blocksOnPage int) (*Blocks, error) { return r, nil } +// getFiatRatesResult checks if CurrencyRatesTicker contains all necessary data and returns formatted result +func (w *Worker) getFiatRatesResult(currency string, ticker *db.CurrencyRatesTicker) (*db.ResultTickerAsString, error) { + resultRates := make(map[string]json.Number) + timeFormatted := ticker.Timestamp.Format(db.FiatRatesTimeFormat) + + // Check if both USD rate and the desired currency rate exist in the result + for _, currencySymbol := range []string{"usd", currency} { + if _, found := ticker.Rates[currencySymbol]; !found { + availableCurrencies := make([]string, 0, len(ticker.Rates)) + for availableCurrency := range ticker.Rates { + availableCurrencies = append(availableCurrencies, string(availableCurrency)) + } + sort.Strings(availableCurrencies) // sort to get deterministic results + availableCurrenciesString := strings.Join(availableCurrencies, ", ") + return nil, NewAPIError(fmt.Sprintf("Currency %q is not available for timestamp %s. Available currencies are: %s.", currency, timeFormatted, availableCurrenciesString), true) + } + resultRates[currencySymbol] = ticker.Rates[currencySymbol] + if currencySymbol == "usd" && currency == "usd" { + break + } + } + + result := &db.ResultTickerAsString{ + Timestamp: timeFormatted, + Rates: resultRates, + } + return result, nil +} + +// GetFiatRatesForBlockID returns fiat rates for block height or block hash +func (w *Worker) GetFiatRatesForBlockID(bid string, currency string) (*db.ResultTickerAsString, error) { + if currency == "" { + return nil, NewAPIError("Missing or empty \"currency\" parameter", true) + } + ticker := &db.CurrencyRatesTicker{} + bi, err := w.getBlockInfoFromBlockID(bid) + if err != nil { + if err == bchain.ErrBlockNotFound { + return nil, NewAPIError(fmt.Sprintf("Block %v not found", bid), true) + } + return nil, NewAPIError(fmt.Sprintf("Block %v not found, error: %v", bid, err), false) + } + dbi := &db.BlockInfo{Time: bi.Time} // get timestamp from block + tm := time.Unix(dbi.Time, 0) // convert it to Time object + ticker, err = w.db.FiatRatesFindTicker(&tm) + if err != nil { + return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false) + } else if ticker == nil { + return nil, NewAPIError(fmt.Sprintf("No tickers available for %s (%s)", tm, currency), true) + } + result, err := w.getFiatRatesResult(currency, ticker) + if err != nil { + return nil, err + } + return result, nil +} + +// GetCurrentFiatRates returns current fiat rates +func (w *Worker) GetCurrentFiatRates(currency string) (*db.ResultTickerAsString, error) { + if currency == "" { + return nil, NewAPIError("Missing or empty \"currency\" parameter", true) + } + ticker, err := w.db.FiatRatesFindLastTicker() + if err != nil { + return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false) + } else if ticker == nil { + return nil, NewAPIError(fmt.Sprintf("No tickers found!"), true) + } + result, err := w.getFiatRatesResult(currency, ticker) + if err != nil { + return nil, err + } + return result, nil +} + +// GetFiatRatesForDates returns fiat rates for each of the provided dates +func (w *Worker) GetFiatRatesForDates(dateStrings []string, currency string) (*db.ResultTickersAsString, error) { + if currency == "" { + return nil, NewAPIError("Missing or empty \"currency\" parameter", true) + } else if len(dateStrings) == 0 { + return nil, NewAPIError("No dates provided", true) + } + + ret := &db.ResultTickersAsString{} + for _, dateString := range dateStrings { + date, err := db.FiatRatesConvertDate(dateString) + if err != nil { + ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Error: fmt.Sprintf("%v", err)}) + continue + } + ticker, err := w.db.FiatRatesFindTicker(date) + if err != nil { + glog.Errorf("Error finding ticker by date %v. Error: %v", dateString, err) + ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Error: "Ticker not found."}) + continue + } else if ticker == nil { + ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Error: fmt.Sprintf("No tickers available for %s (%s)", date, currency)}) + continue + } + result, err := w.getFiatRatesResult(currency, ticker) + if err != nil { + ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Error: fmt.Sprintf("%v", err)}) + continue + } + ret.Tickers = append(ret.Tickers, *result) + } + return ret, nil +} + +// GetFiatRatesTickersList returns the list of available fiatRates tickers +func (w *Worker) GetFiatRatesTickersList(dateString string) (*db.ResultTickerListAsString, error) { + if dateString == "" { + return nil, NewAPIError("Missing or empty \"date\" parameter", true) + } + date, err := db.FiatRatesConvertDate(dateString) + if err != nil { + return nil, err + } + + ticker, err := w.db.FiatRatesFindTicker(date) + if err != nil { + return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false) + } else if ticker == nil { + return nil, NewAPIError(fmt.Sprintf("No tickers found for date %v.", date), true) + } + + keys := make([]string, 0, len(ticker.Rates)) + for k := range ticker.Rates { + keys = append(keys, k) + } + sort.Strings(keys) // sort to get deterministic results + timeFormatted := ticker.Timestamp.Format(db.FiatRatesTimeFormat) + + return &db.ResultTickerListAsString{ + Timestamp: timeFormatted, + Tickers: keys, + }, nil +} + +// getBlockInfoFromBlockID returns block info from block height or block hash func (w *Worker) getBlockInfoFromBlockID(bid string) (*bchain.BlockInfo, error) { // try to decide if passed string (bid) is block height or block hash // if it's a number, must be less than int32 diff --git a/bchain/coins/btc/bitcoinrpc.go b/bchain/coins/btc/bitcoinrpc.go index ef50c710..2a66cb21 100644 --- a/bchain/coins/btc/bitcoinrpc.go +++ b/bchain/coins/btc/bitcoinrpc.go @@ -55,8 +55,8 @@ type Configuration struct { XPubMagicSegwitP2sh uint32 `json:"xpub_magic_segwit_p2sh,omitempty"` XPubMagicSegwitNative uint32 `json:"xpub_magic_segwit_native,omitempty"` Slip44 uint32 `json:"slip44,omitempty"` - AlternativeEstimateFee string `json:"alternativeEstimateFee,omitempty"` - AlternativeEstimateFeeParams string `json:"alternativeEstimateFeeParams,omitempty"` + AlternativeEstimateFee string `json:"alternative_estimate_fee,omitempty"` + AlternativeEstimateFeeParams string `json:"alternative_estimate_fee_params,omitempty"` MinimumCoinbaseConfirmations int `json:"minimumCoinbaseConfirmations,omitempty"` } diff --git a/blockbook.go b/blockbook.go index 444f60f4..08268081 100644 --- a/blockbook.go +++ b/blockbook.go @@ -6,9 +6,12 @@ import ( "blockbook/bchain/coins" "blockbook/common" "blockbook/db" + "blockbook/fiat" "blockbook/server" "context" + "encoding/json" "flag" + "io/ioutil" "log" "math/rand" "net/http" @@ -81,23 +84,24 @@ var ( ) var ( - chanSyncIndex = make(chan struct{}) - chanSyncMempool = make(chan struct{}) - chanStoreInternalState = make(chan struct{}) - chanSyncIndexDone = make(chan struct{}) - chanSyncMempoolDone = make(chan struct{}) - chanStoreInternalStateDone = make(chan struct{}) - chain bchain.BlockChain - mempool bchain.Mempool - index *db.RocksDB - txCache *db.TxCache - metrics *common.Metrics - syncWorker *db.SyncWorker - internalState *common.InternalState - callbacksOnNewBlock []bchain.OnNewBlockFunc - callbacksOnNewTxAddr []bchain.OnNewTxAddrFunc - chanOsSignal chan os.Signal - inShutdown int32 + chanSyncIndex = make(chan struct{}) + chanSyncMempool = make(chan struct{}) + chanStoreInternalState = make(chan struct{}) + chanSyncIndexDone = make(chan struct{}) + chanSyncMempoolDone = make(chan struct{}) + chanStoreInternalStateDone = make(chan struct{}) + chain bchain.BlockChain + mempool bchain.Mempool + index *db.RocksDB + txCache *db.TxCache + metrics *common.Metrics + syncWorker *db.SyncWorker + internalState *common.InternalState + callbacksOnNewBlock []bchain.OnNewBlockFunc + callbacksOnNewTxAddr []bchain.OnNewTxAddrFunc + callbacksOnNewFiatRatesTicker []fiat.OnNewFiatRatesTicker + chanOsSignal chan os.Signal + inShutdown int32 ) func init() { @@ -295,6 +299,7 @@ func mainWithExitCode() int { // start full public interface callbacksOnNewBlock = append(callbacksOnNewBlock, publicServer.OnNewBlock) callbacksOnNewTxAddr = append(callbacksOnNewTxAddr, publicServer.OnNewTxAddr) + callbacksOnNewFiatRatesTicker = append(callbacksOnNewFiatRatesTicker, publicServer.OnNewFiatRatesTicker) publicServer.ConnectFullPublicInterface() } @@ -317,6 +322,8 @@ func mainWithExitCode() int { } if internalServer != nil || publicServer != nil || chain != nil { + // start fiat rates downloader only if not shutting down immediately + initFiatRatesDownloader(index, *blockchain) waitForSignalAndShutdown(internalServer, publicServer, chain, 10*time.Second) } @@ -521,6 +528,12 @@ func onNewBlockHash(hash string, height uint32) { } } +func onNewFiatRatesTicker(ticker *db.CurrencyRatesTicker) { + for _, c := range callbacksOnNewFiatRatesTicker { + c(ticker) + } +} + func syncMempoolLoop() { defer close(chanSyncMempoolDone) glog.Info("syncMempoolLoop starting") @@ -650,3 +663,34 @@ func computeFeeStats(stopCompute chan os.Signal, blockFrom, blockTo int, db *db. glog.Info("computeFeeStats finished in ", time.Since(start)) return err } + +func initFiatRatesDownloader(db *db.RocksDB, configfile string) { + data, err := ioutil.ReadFile(configfile) + if err != nil { + glog.Errorf("Error reading file %v, %v", configfile, err) + return + } + + var config struct { + FiatRates string `json:"fiat_rates"` + FiatRatesParams string `json:"fiat_rates_params"` + } + + err = json.Unmarshal(data, &config) + if err != nil { + glog.Errorf("Error parsing config file %v, %v", configfile, err) + return + } + + if config.FiatRates == "" || config.FiatRatesParams == "" { + glog.Infof("FiatRates config (%v) is empty, so the functionality is disabled.", configfile) + } else { + fiatRates, err := fiat.NewFiatRatesDownloader(db, config.FiatRates, config.FiatRatesParams, nil, onNewFiatRatesTicker) + if err != nil { + glog.Errorf("NewFiatRatesDownloader Init error: %v", err) + return + } + glog.Infof("Starting %v FiatRates downloader...", config.FiatRates) + go fiatRates.Run() + } +} diff --git a/build/tools/templates.go b/build/tools/templates.go index b8101ada..3e58aaa8 100644 --- a/build/tools/templates.go +++ b/build/tools/templates.go @@ -98,7 +98,7 @@ func jsonToString(msg json.RawMessage) (string, error) { } func generateRPCAuth(user, pass string) (string, error) { - cmd := exec.Command("/bin/bash", "-c", "build/scripts/rpcauth.py \"$0\" \"$1\" | sed -n -e 2p", user, pass) + cmd := exec.Command("/usr/bin/env", "bash", "-c", "build/scripts/rpcauth.py \"$0\" \"$1\" | sed -n -e 2p", user, pass) var out bytes.Buffer cmd.Stdout = &out err := cmd.Run() diff --git a/configs/coins/bcash.json b/configs/coins/bcash.json index 99cb6599..440d3467 100644 --- a/configs/coins/bcash.json +++ b/configs/coins/bcash.json @@ -56,11 +56,14 @@ "block_addresses_to_keep": 300, "xpub_magic": 76067358, "slip44": 145, - "additional_params": {} + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin-cash\", \"periodSeconds\": 60}" + } } }, "meta": { "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/bgold.json b/configs/coins/bgold.json index e518024c..8807de5f 100644 --- a/configs/coins/bgold.json +++ b/configs/coins/bgold.json @@ -252,11 +252,14 @@ "xpub_magic_segwit_p2sh": 77429938, "xpub_magic_segwit_native": 78792518, "slip44": 156, - "additional_params": {} + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin-gold\", \"periodSeconds\": 60}" + } } }, "meta": { "package_maintainer": "Jakub Matys", "package_maintainer_email": "jakub.matys@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index c9096f75..3362d51e 100644 --- a/configs/coins/bitcoin.json +++ b/configs/coins/bitcoin.json @@ -59,8 +59,10 @@ "xpub_magic_segwit_p2sh": 77429938, "xpub_magic_segwit_native": 78792518, "additional_params": { - "alternativeEstimateFee": "whatthefee-disabled", - "alternativeEstimateFeeParams": "{\"url\": \"https://whatthefee.io/data.json\", \"periodSeconds\": 60}" + "alternative_estimate_fee": "whatthefee-disabled", + "alternative_estimate_fee_params": "{\"url\": \"https://whatthefee.io/data.json\", \"periodSeconds\": 60}", + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin\", \"periodSeconds\": 60}" } } }, diff --git a/configs/coins/bitcoin_testnet.json b/configs/coins/bitcoin_testnet.json index 2fa70af7..de057cf5 100644 --- a/configs/coins/bitcoin_testnet.json +++ b/configs/coins/bitcoin_testnet.json @@ -59,7 +59,10 @@ "xpub_magic_segwit_p2sh": 71979618, "xpub_magic_segwit_native": 73342198, "slip44": 1, - "additional_params": {} + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin\", \"periodSeconds\": 60}" + } } }, "meta": { diff --git a/configs/coins/dash.json b/configs/coins/dash.json index b4e8ba65..fd8b7537 100644 --- a/configs/coins/dash.json +++ b/configs/coins/dash.json @@ -58,11 +58,14 @@ "block_addresses_to_keep": 300, "xpub_magic": 50221772, "slip44": 5, - "additional_params": {} + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"dash\", \"periodSeconds\": 60}" + } } }, "meta": { "package_maintainer": "IT Admin", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/digibyte.json b/configs/coins/digibyte.json index e9e01c3a..97f3d119 100644 --- a/configs/coins/digibyte.json +++ b/configs/coins/digibyte.json @@ -59,11 +59,14 @@ "xpub_magic_segwit_p2sh": 77429938, "xpub_magic_segwit_native": 78792518, "slip44": 20, - "additional_params": {} + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"digibyte\", \"periodSeconds\": 60}" + } } }, "meta": { "package_maintainer": "Martin Bohm", "package_maintainer_email": "martin.bohm@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/dogecoin.json b/configs/coins/dogecoin.json index 7ed865c6..cd42ca65 100644 --- a/configs/coins/dogecoin.json +++ b/configs/coins/dogecoin.json @@ -60,11 +60,14 @@ "block_addresses_to_keep": 300, "xpub_magic": 49990397, "slip44": 3, - "additional_params": {} + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"dogecoin\", \"periodSeconds\": 60}" + } } }, "meta": { "package_maintainer": "IT Admin", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum-classic.json b/configs/coins/ethereum-classic.json index 77a7c8da..0e72b03d 100644 --- a/configs/coins/ethereum-classic.json +++ b/configs/coins/ethereum-classic.json @@ -63,7 +63,9 @@ "block_addresses_to_keep": 300, "additional_params": { "mempoolTxTimeoutHours": 48, - "queryBackendOnMempoolResync": true + "queryBackendOnMempoolResync": true, + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum-classic\", \"periodSeconds\": 60}" } } }, @@ -71,4 +73,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index cafac864..9c530a2c 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -51,7 +51,9 @@ "block_addresses_to_keep": 300, "additional_params": { "mempoolTxTimeoutHours": 48, - "queryBackendOnMempoolResync": false + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\", \"periodSeconds\": 60}" } } }, diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 9ed31ec8..27aa6feb 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -50,7 +50,9 @@ "block_addresses_to_keep": 300, "additional_params": { "mempoolTxTimeoutHours": 12, - "queryBackendOnMempoolResync": false + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\", \"periodSeconds\": 60}" } } }, diff --git a/configs/coins/litecoin.json b/configs/coins/litecoin.json index cfe51de5..121c5093 100644 --- a/configs/coins/litecoin.json +++ b/configs/coins/litecoin.json @@ -59,11 +59,14 @@ "xpub_magic_segwit_p2sh": 28471030, "xpub_magic_segwit_native": 78792518, "slip44": 2, - "additional_params": {} + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"litecoin\", \"periodSeconds\": 60}" + } } }, "meta": { "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/namecoin.json b/configs/coins/namecoin.json index 7c2bc93b..441777b3 100644 --- a/configs/coins/namecoin.json +++ b/configs/coins/namecoin.json @@ -64,11 +64,14 @@ "block_addresses_to_keep": 300, "xpub_magic": 76067358, "slip44": 7, - "additional_params": {} + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"namecoin\", \"periodSeconds\": 60}" + } } }, "meta": { "package_maintainer": "IT Admin", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/vertcoin.json b/configs/coins/vertcoin.json index 1ce7fa77..216c744c 100644 --- a/configs/coins/vertcoin.json +++ b/configs/coins/vertcoin.json @@ -57,11 +57,14 @@ "xpub_magic_segwit_p2sh": 77429938, "xpub_magic_segwit_native": 78792518, "slip44": 28, - "additional_params": {} + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"vertcoin\", \"periodSeconds\": 60}" + } } }, "meta": { "package_maintainer": "Petr Kracik", "package_maintainer_email": "petr.kracik@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index fecc9586..4845c822 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -57,11 +57,14 @@ "block_addresses_to_keep": 300, "xpub_magic": 76067358, "slip44": 133, - "additional_params": {} + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"zcash\", \"periodSeconds\": 60}" + } } }, "meta": { "package_maintainer": "IT Admin", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/db/rocksdb.go b/db/rocksdb.go index 64a8815f..14438096 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -6,6 +6,7 @@ import ( "bytes" "encoding/binary" "encoding/hex" + "encoding/json" "fmt" "math/big" "os" @@ -30,6 +31,34 @@ const maxAddrDescLen = 1024 // when doing huge scan, it is better to close it and reopen from time to time to free the resources const refreshIterator = 5000000 +// FiatRatesTimeFormat is a format string for storing FiatRates timestamps in rocksdb +const FiatRatesTimeFormat = "20060102150405" // YYYYMMDDhhmmss + +// CurrencyRatesTicker contains coin ticker data fetched from API +type CurrencyRatesTicker struct { + Timestamp *time.Time // return as unix timestamp in API + Rates map[string]json.Number +} + +// ResultTickerAsString contains formatted CurrencyRatesTicker data +type ResultTickerAsString struct { + Timestamp string `json:"data_timestamp,omitempty"` + Rates map[string]json.Number `json:"rates,omitempty"` + Error string `json:"error,omitempty"` +} + +// ResultTickersAsString contains a formatted CurrencyRatesTicker list +type ResultTickersAsString struct { + Tickers []ResultTickerAsString `json:"tickers"` +} + +// ResultTickerListAsString contains formatted data about available currency tickers +type ResultTickerListAsString struct { + Timestamp string `json:"data_timestamp,omitempty"` + Tickers []string `json:"available_currencies"` + Error string `json:"error,omitempty"` +} + // RepairRocksDB calls RocksDb db repair function func RepairRocksDB(name string) error { glog.Infof("rocksdb: repair") @@ -77,6 +106,7 @@ const ( cfAddresses cfBlockTxs cfTransactions + cfFiatRates // BitcoinType cfAddressBalance cfTxAddresses @@ -86,7 +116,7 @@ const ( // common columns var cfNames []string -var cfBaseNames = []string{"default", "height", "addresses", "blockTxs", "transactions"} +var cfBaseNames = []string{"default", "height", "addresses", "blockTxs", "transactions", "fiatRates"} // type specific columns var cfNamesBitcoinType = []string{"addressBalance", "txAddresses"} @@ -99,7 +129,7 @@ func openDB(path string, c *gorocksdb.Cache, openFiles int) (*gorocksdb.DB, []*g // from documentation: if most of your queries are executed using iterators, you shouldn't set bloom filter optsAddresses := createAndSetDBOptions(0, c, openFiles) // default, height, addresses, blockTxids, transactions - cfOptions := []*gorocksdb.Options{opts, opts, optsAddresses, opts, opts} + cfOptions := []*gorocksdb.Options{opts, opts, optsAddresses, opts, opts, opts} // append type specific options count := len(cfNames) - len(cfOptions) for i := 0; i < count; i++ { @@ -146,6 +176,104 @@ func (d *RocksDB) closeDB() error { return nil } +// FiatRatesConvertDate checks if the date is in correct format and returns the Time object. +// Possible formats are: YYYYMMDDhhmmss, YYYYMMDDhhmm, YYYYMMDDhh, YYYYMMDD +func FiatRatesConvertDate(date string) (*time.Time, error) { + for format := FiatRatesTimeFormat; len(format) >= 8; format = format[:len(format)-2] { + convertedDate, err := time.Parse(format, date) + if err == nil { + return &convertedDate, nil + } + } + msg := "Date \"" + date + "\" does not match any of available formats. " + msg += "Possible formats are: YYYYMMDDhhmmss, YYYYMMDDhhmm, YYYYMMDDhh, YYYYMMDD" + return nil, errors.New(msg) +} + +// FiatRatesStoreTicker stores ticker data at the specified time +func (d *RocksDB) FiatRatesStoreTicker(ticker *CurrencyRatesTicker) error { + if len(ticker.Rates) == 0 { + return errors.New("Error storing ticker: empty rates") + } else if ticker.Timestamp == nil { + return errors.New("Error storing ticker: empty timestamp") + } + ratesMarshalled, err := json.Marshal(ticker.Rates) + if err != nil { + glog.Error("Error marshalling ticker rates: ", err) + return err + } + timeFormatted := ticker.Timestamp.UTC().Format(FiatRatesTimeFormat) + err = d.db.PutCF(d.wo, d.cfh[cfFiatRates], []byte(timeFormatted), ratesMarshalled) + if err != nil { + glog.Error("Error storing ticker: ", err) + return err + } + return nil +} + +// FiatRatesFindTicker gets FiatRates data closest to the specified timestamp +func (d *RocksDB) FiatRatesFindTicker(tickerTime *time.Time) (*CurrencyRatesTicker, error) { + ticker := &CurrencyRatesTicker{} + tickerTimeFormatted := tickerTime.UTC().Format(FiatRatesTimeFormat) + it := d.db.NewIteratorCF(d.ro, d.cfh[cfFiatRates]) + defer it.Close() + + for it.Seek([]byte(tickerTimeFormatted)); it.Valid(); it.Next() { + timeObj, err := time.Parse(FiatRatesTimeFormat, string(it.Key().Data())) + if err != nil { + glog.Error("FiatRatesFindTicker time parse error: ", err) + return nil, err + } + timeObj = timeObj.UTC() + ticker.Timestamp = &timeObj + err = json.Unmarshal(it.Value().Data(), &ticker.Rates) + if err != nil { + glog.Error("FiatRatesFindTicker error unpacking rates: ", err) + return nil, err + } + break + } + if err := it.Err(); err != nil { + glog.Error("FiatRatesFindTicker Iterator error: ", err) + return nil, err + } + if !it.Valid() { + return nil, nil // ticker not found + } + return ticker, nil +} + +// FiatRatesFindLastTicker gets the last FiatRates record +func (d *RocksDB) FiatRatesFindLastTicker() (*CurrencyRatesTicker, error) { + ticker := &CurrencyRatesTicker{} + it := d.db.NewIteratorCF(d.ro, d.cfh[cfFiatRates]) + defer it.Close() + + for it.SeekToLast(); it.Valid(); it.Next() { + timeObj, err := time.Parse(FiatRatesTimeFormat, string(it.Key().Data())) + if err != nil { + glog.Error("FiatRatesFindTicker time parse error: ", err) + return nil, err + } + timeObj = timeObj.UTC() + ticker.Timestamp = &timeObj + err = json.Unmarshal(it.Value().Data(), &ticker.Rates) + if err != nil { + glog.Error("FiatRatesFindTicker error unpacking rates: ", err) + return nil, err + } + break + } + if err := it.Err(); err != nil { + glog.Error("FiatRatesFindLastTicker Iterator error: ", err) + return ticker, err + } + if !it.Valid() { + return nil, nil // ticker not found + } + return ticker, nil +} + // Close releases the RocksDB environment opened in NewRocksDB. func (d *RocksDB) Close() error { if d.db != nil { diff --git a/db/rocksdb_test.go b/db/rocksdb_test.go index 5a6137e5..4f5766b2 100644 --- a/db/rocksdb_test.go +++ b/db/rocksdb_test.go @@ -9,6 +9,7 @@ import ( "blockbook/tests/dbtestdata" "encoding/binary" "encoding/hex" + "encoding/json" "io/ioutil" "math/big" "os" @@ -16,6 +17,7 @@ import ( "sort" "strings" "testing" + "time" vlq "github.com/bsm/go-vlq" "github.com/juju/errors" @@ -1071,3 +1073,73 @@ func Test_packAddrBalance_unpackAddrBalance(t *testing.T) { }) } } + +func TestRocksTickers(t *testing.T) { + d := setupRocksDB(t, &testBitcoinParser{ + BitcoinParser: bitcoinTestnetParser(), + }) + defer closeAndDestroyRocksDB(t, d) + + // Test valid formats + for _, date := range []string{"20190130", "2019013012", "201901301250", "20190130125030"} { + _, err := FiatRatesConvertDate(date) + if err != nil { + t.Errorf("%v", err) + } + } + + // Test invalid formats + for _, date := range []string{"01102019", "10201901", "", "abc", "20190130xxx"} { + _, err := FiatRatesConvertDate(date) + if err == nil { + t.Errorf("Wrongly-formatted date \"%v\" marked as valid!", date) + } + } + + // Test storing & finding tickers + key, _ := time.Parse(FiatRatesTimeFormat, "20190627000000") + futureKey, _ := time.Parse(FiatRatesTimeFormat, "20190630000000") + + ts1, _ := time.Parse(FiatRatesTimeFormat, "20190628000000") + ticker1 := &CurrencyRatesTicker{ + Timestamp: &ts1, + Rates: map[string]json.Number{ + "usd": "20000", + }, + } + + ts2, _ := time.Parse(FiatRatesTimeFormat, "20190629000000") + ticker2 := &CurrencyRatesTicker{ + Timestamp: &ts2, + Rates: map[string]json.Number{ + "usd": "30000", + }, + } + d.FiatRatesStoreTicker(ticker1) + d.FiatRatesStoreTicker(ticker2) + + ticker, err := d.FiatRatesFindTicker(&key) // should find the closest key (ticker1) + if err != nil { + t.Errorf("TestRocksTickers err: %+v", err) + } else if ticker == nil { + t.Errorf("Ticker not found") + } else if ticker.Timestamp.Format(FiatRatesTimeFormat) != ticker1.Timestamp.Format(FiatRatesTimeFormat) { + t.Errorf("Incorrect ticker found. Expected: %v, found: %+v", ticker1.Timestamp, ticker.Timestamp) + } + + ticker, err = d.FiatRatesFindLastTicker() // should find the last key (ticker2) + if err != nil { + t.Errorf("TestRocksTickers err: %+v", err) + } else if ticker == nil { + t.Errorf("Ticker not found") + } else if ticker.Timestamp.Format(FiatRatesTimeFormat) != ticker2.Timestamp.Format(FiatRatesTimeFormat) { + t.Errorf("Incorrect ticker found. Expected: %v, found: %+v", ticker1.Timestamp, ticker.Timestamp) + } + + ticker, err = d.FiatRatesFindTicker(&futureKey) // should not find anything + if err != nil { + t.Errorf("TestRocksTickers err: %+v", err) + } else if ticker != nil { + t.Errorf("Ticker found, but the timestamp is older than the last ticker entry.") + } +} diff --git a/fiat/coingecko.go b/fiat/coingecko.go new file mode 100644 index 00000000..84139db6 --- /dev/null +++ b/fiat/coingecko.go @@ -0,0 +1,136 @@ +package fiat + +import ( + "blockbook/db" + "encoding/json" + "errors" + "io/ioutil" + "net/http" + "strconv" + "time" + + "github.com/golang/glog" +) + +// Coingecko is a structure that implements RatesDownloaderInterface +type Coingecko struct { + url string + coin string + httpTimeoutSeconds time.Duration + timeFormat string +} + +// NewCoinGeckoDownloader creates a coingecko structure that implements the RatesDownloaderInterface +func NewCoinGeckoDownloader(url string, coin string, timeFormat string) RatesDownloaderInterface { + return &Coingecko{ + url: url, + coin: coin, + httpTimeoutSeconds: 15 * time.Second, + timeFormat: timeFormat, + } +} + +// makeRequest retrieves the response from Coingecko API at the specified date. +// If timestamp is nil, it fetches the latest market data available. +func (cg *Coingecko) makeRequest(timestamp *time.Time) ([]byte, error) { + requestURL := cg.url + "/coins/" + cg.coin + if timestamp != nil { + requestURL += "/history" + } + + req, err := http.NewRequest("GET", requestURL, nil) + if err != nil { + glog.Errorf("Error creating a new request for %v: %v", requestURL, err) + return nil, err + } + req.Close = true + req.Header.Set("Content-Type", "application/json") + + // Add query parameters + q := req.URL.Query() + + // Add a unix timestamp to query parameters to get uncached responses + currentTimestamp := strconv.FormatInt(time.Now().UTC().UnixNano(), 10) + q.Add("current_timestamp", currentTimestamp) + + if timestamp == nil { + q.Add("market_data", "true") + q.Add("localization", "false") + q.Add("tickers", "false") + q.Add("community_data", "false") + q.Add("developer_data", "false") + } else { + timestampFormatted := timestamp.Format(cg.timeFormat) + q.Add("date", timestampFormatted) + } + req.URL.RawQuery = q.Encode() + + client := &http.Client{ + Timeout: cg.httpTimeoutSeconds, + } + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return nil, errors.New("Invalid response status: " + string(resp.Status)) + } + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return bodyBytes, nil +} + +// GetData gets fiat rates from API at the specified date and returns a CurrencyRatesTicker +// If timestamp is nil, it will download the current fiat rates. +func (cg *Coingecko) getTicker(timestamp *time.Time) (*db.CurrencyRatesTicker, error) { + dataTimestamp := timestamp + if timestamp == nil { + timeNow := time.Now() + dataTimestamp = &timeNow + } + dataTimestampUTC := dataTimestamp.UTC() + ticker := &db.CurrencyRatesTicker{Timestamp: &dataTimestampUTC} + bodyBytes, err := cg.makeRequest(timestamp) + if err != nil { + return nil, err + } + + type FiatRatesResponse struct { + MarketData struct { + Prices map[string]json.Number `json:"current_price"` + } `json:"market_data"` + } + + var data FiatRatesResponse + err = json.Unmarshal(bodyBytes, &data) + if err != nil { + glog.Errorf("Error parsing FiatRates response: %v", err) + return nil, err + } + ticker.Rates = data.MarketData.Prices + return ticker, nil +} + +// MarketDataExists checks if there's data available for the specific timestamp. +func (cg *Coingecko) marketDataExists(timestamp *time.Time) (bool, error) { + resp, err := cg.makeRequest(timestamp) + if err != nil { + glog.Error("Error getting market data: ", err) + return false, err + } + type FiatRatesResponse struct { + MarketData struct { + Prices map[string]interface{} `json:"current_price"` + } `json:"market_data"` + } + var data FiatRatesResponse + err = json.Unmarshal(resp, &data) + if err != nil { + glog.Errorf("Error parsing Coingecko response: %v", err) + return false, err + } + return len(data.MarketData.Prices) != 0, nil +} diff --git a/fiat/fiat_rates.go b/fiat/fiat_rates.go new file mode 100644 index 00000000..a3e01889 --- /dev/null +++ b/fiat/fiat_rates.go @@ -0,0 +1,214 @@ +package fiat + +import ( + "blockbook/db" + "encoding/json" + "errors" + "fmt" + "reflect" + "time" + + "github.com/golang/glog" +) + +// OnNewFiatRatesTicker is used to send notification about a new FiatRates ticker +type OnNewFiatRatesTicker func(ticker *db.CurrencyRatesTicker) + +// RatesDownloaderInterface provides method signatures for specific fiat rates downloaders +type RatesDownloaderInterface interface { + getTicker(timestamp *time.Time) (*db.CurrencyRatesTicker, error) + marketDataExists(timestamp *time.Time) (bool, error) +} + +// RatesDownloader stores FiatRates API parameters +type RatesDownloader struct { + periodSeconds time.Duration + db *db.RocksDB + startTime *time.Time // a starting timestamp for tests to be deterministic (time.Now() for production) + timeFormat string + callbackOnNewTicker OnNewFiatRatesTicker + downloader RatesDownloaderInterface +} + +// NewFiatRatesDownloader initiallizes the downloader for FiatRates API. +// If the startTime is nil, the downloader will start from the beginning. +func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, startTime *time.Time, callback OnNewFiatRatesTicker) (*RatesDownloader, error) { + var rd = &RatesDownloader{} + type fiatRatesParams struct { + URL string `json:"url"` + Coin string `json:"coin"` + PeriodSeconds int `json:"periodSeconds"` + } + rdParams := &fiatRatesParams{} + err := json.Unmarshal([]byte(params), &rdParams) + if err != nil { + return nil, err + } + if rdParams.URL == "" || rdParams.PeriodSeconds == 0 { + return nil, errors.New("Missing parameters") + } + rd.timeFormat = "02-01-2006" // Layout string for FiatRates date formatting (DD-MM-YYYY) + rd.periodSeconds = time.Duration(rdParams.PeriodSeconds) * time.Second // Time period for syncing the latest market data + rd.db = db + rd.callbackOnNewTicker = callback + if startTime == nil { + timeNow := time.Now().UTC() + rd.startTime = &timeNow + } else { + rd.startTime = startTime // If startTime is nil, time.Now() will be used + } + if apiType == "coingecko" { + rd.downloader = NewCoinGeckoDownloader(rdParams.URL, rdParams.Coin, rd.timeFormat) + } else { + return nil, fmt.Errorf("NewFiatRatesDownloader: incorrect API type %q", apiType) + } + return rd, nil +} + +// Run starts the FiatRates downloader. If there are tickers available, it continues from the last record. +// If there are no tickers, it finds the earliest market data available on API and downloads historical data. +// When historical data is downloaded, it continues to fetch the latest ticker prices. +func (rd *RatesDownloader) Run() error { + var timestamp *time.Time + + // Check if there are any tickers stored in database + glog.Infof("Finding last available ticker...") + ticker, err := rd.db.FiatRatesFindLastTicker() + if err != nil { + glog.Errorf("RatesDownloader FindTicker error: %v", err) + return err + } + + if ticker == nil { + // If no tickers found, start downloading from the beginning + glog.Infof("No tickers found! Looking up the earliest market data available on API and downloading from there.") + timestamp, err = rd.findEarliestMarketData() + if err != nil { + glog.Errorf("Error looking up earliest market data: %v", err) + return err + } + } else { + // If found, continue downloading data from the next day of the last available record + glog.Infof("Last available ticker: %v", ticker.Timestamp) + timestamp = ticker.Timestamp + } + err = rd.syncHistorical(timestamp) + if err != nil { + glog.Errorf("RatesDownloader syncHistorical error: %v", err) + return err + } + if err := rd.syncLatest(); err != nil { + glog.Errorf("RatesDownloader syncLatest error: %v", err) + return err + } + return nil +} + +// FindEarliestMarketData uses binary search to find the oldest market data available on API. +func (rd *RatesDownloader) findEarliestMarketData() (*time.Time, error) { + minDateString := "03-01-2009" + minDate, err := time.Parse(rd.timeFormat, minDateString) + if err != nil { + glog.Error("Error parsing date: ", err) + return nil, err + } + maxDate := rd.startTime.Add(time.Duration(-24) * time.Hour) // today's historical tickers may not be ready yet, so set to yesterday + currentDate := maxDate + for { + var dataExists bool = false + for { + dataExists, err = rd.downloader.marketDataExists(¤tDate) + if err != nil { + glog.Errorf("Error checking if market data exists for date %v. Error: %v. Retrying in %v seconds.", currentDate, err, rd.periodSeconds) + timer := time.NewTimer(rd.periodSeconds) + <-timer.C + } + break + } + dateDiff := currentDate.Sub(minDate) + if dataExists { + if dateDiff < time.Hour*24 { + maxDate := time.Date(maxDate.Year(), maxDate.Month(), maxDate.Day(), 0, 0, 0, 0, maxDate.Location()) // truncate time to day + return &maxDate, nil + } + maxDate = currentDate + currentDate = currentDate.Add(-1 * dateDiff / 2) + } else { + minDate = currentDate + currentDate = currentDate.Add(maxDate.Sub(currentDate) / 2) + } + } +} + +// syncLatest downloads the latest FiatRates data every rd.PeriodSeconds +func (rd *RatesDownloader) syncLatest() error { + timer := time.NewTimer(rd.periodSeconds) + var lastTickerRates map[string]json.Number = nil + sameTickerCounter := 0 + for { + ticker, err := rd.downloader.getTicker(nil) + if err != nil { + // Do not exit on GET error, log it, wait and try again + glog.Errorf("syncLatest GetData error: %v", err) + <-timer.C + timer.Reset(rd.periodSeconds) + continue + } + + if sameTickerCounter < 5 && reflect.DeepEqual(ticker.Rates, lastTickerRates) { + // If rates are the same as previous, do not store them + glog.Infof("syncLatest: ticker rates for %v are the same as previous, skipping...", ticker.Timestamp) + <-timer.C + timer.Reset(rd.periodSeconds) + sameTickerCounter++ + continue + } + lastTickerRates = ticker.Rates + sameTickerCounter = 0 + + glog.Infof("syncLatest: storing ticker for %v", ticker.Timestamp) + err = rd.db.FiatRatesStoreTicker(ticker) + if err != nil { + // If there's an error storing ticker (like missing rates), log it, wait and try again + glog.Errorf("syncLatest StoreTicker error: %v", err) + } else if rd.callbackOnNewTicker != nil { + rd.callbackOnNewTicker(ticker) + } + <-timer.C + timer.Reset(rd.periodSeconds) + } +} + +// syncHistorical downloads all the historical data since the specified timestamp till today, +// then continues to download the latest rates +func (rd *RatesDownloader) syncHistorical(timestamp *time.Time) error { + period := time.Duration(1) * time.Second + timer := time.NewTimer(period) + for { + if rd.startTime.Sub(*timestamp) < time.Duration(time.Hour*24) { + break + } + + ticker, err := rd.downloader.getTicker(timestamp) + if err != nil { + // Do not exit on GET error, log it, wait and try again + glog.Errorf("syncHistorical GetData error: %v", err) + <-timer.C + timer.Reset(rd.periodSeconds) + continue + } + + glog.Infof("syncHistorical: storing ticker for %v", ticker.Timestamp) + err = rd.db.FiatRatesStoreTicker(ticker) + if err != nil { + // If there's an error storing ticker (like missing rates), log it and continue to the next day + glog.Errorf("syncHistorical error storing ticker for %v: %v", timestamp, err) + } + + *timestamp = timestamp.Add(time.Hour * 24) // go to the next day + + <-timer.C + timer.Reset(period) + } + return nil +} diff --git a/fiat/fiat_rates_test.go b/fiat/fiat_rates_test.go new file mode 100644 index 00000000..a131cb4a --- /dev/null +++ b/fiat/fiat_rates_test.go @@ -0,0 +1,176 @@ +// +build unittest + +package fiat + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "blockbook/common" + "blockbook/db" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "os" + "testing" + "time" + + "github.com/golang/glog" + "github.com/martinboehm/btcutil/chaincfg" +) + +func TestMain(m *testing.M) { + // set the current directory to blockbook root so that ./static/ works + if err := os.Chdir(".."); err != nil { + glog.Fatal("Chdir error:", err) + } + c := m.Run() + chaincfg.ResetParams() + os.Exit(c) +} + +func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *common.InternalState, string) { + tmp, err := ioutil.TempDir("", "testdb") + if err != nil { + t.Fatal(err) + } + d, err := db.NewRocksDB(tmp, 100000, -1, parser, nil) + if err != nil { + t.Fatal(err) + } + is, err := d.LoadInternalState("fakecoin") + if err != nil { + t.Fatal(err) + } + d.SetInternalState(is) + return d, is, tmp +} + +func closeAndDestroyRocksDB(t *testing.T, db *db.RocksDB, dbpath string) { + // destroy db + if err := db.Close(); err != nil { + t.Fatal(err) + } + os.RemoveAll(dbpath) +} + +type testBitcoinParser struct { + *btc.BitcoinParser +} + +func bitcoinTestnetParser() *btc.BitcoinParser { + return btc.NewBitcoinParser( + btc.GetChainParams("test"), + &btc.Configuration{BlockAddressesToKeep: 1}) +} + +// getFiatRatesMockData reads a stub JSON response from a file and returns its content as string +func getFiatRatesMockData(dateParam string) (string, error) { + var filename string + if dateParam == "current" { + filename = "fiat/mock_data/current.json" + } else { + filename = "fiat/mock_data/" + dateParam + ".json" + } + mockFile, err := os.Open(filename) + if err != nil { + glog.Errorf("Cannot open file %v", filename) + return "", err + } + b, err := ioutil.ReadAll(mockFile) + if err != nil { + glog.Errorf("Cannot read file %v", filename) + return "", err + } + return string(b), nil +} + +func TestFiatRates(t *testing.T) { + d, _, tmp := setupRocksDB(t, &testBitcoinParser{ + BitcoinParser: bitcoinTestnetParser(), + }) + defer closeAndDestroyRocksDB(t, d, tmp) + + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var err error + var mockData string + + if r.URL.Path == "/ping" { + w.WriteHeader(200) + } else if r.URL.Path == "/coins/bitcoin/history" { + date := r.URL.Query()["date"][0] + mockData, err = getFiatRatesMockData(date) // get stub rates by date + } else if r.URL.Path == "/coins/bitcoin" { + mockData, err = getFiatRatesMockData("current") // get "latest" stub rates + } else { + t.Errorf("Unknown URL path: %v", r.URL.Path) + } + + if err != nil { + t.Errorf("Error loading stub data: %v", err) + } + fmt.Fprintln(w, mockData) + })) + defer mockServer.Close() + + // real CoinGecko API + //configJSON := `{"fiat_rates": "coingecko", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin\", \"periodSeconds\": 60}"}` + + // mocked CoinGecko API + configJSON := `{"fiat_rates": "coingecko", "fiat_rates_params": "{\"url\": \"` + mockServer.URL + `\", \"coin\": \"bitcoin\", \"periodSeconds\": 60}"}` + + type fiatRatesConfig struct { + FiatRates string `json:"fiat_rates"` + FiatRatesParams string `json:"fiat_rates_params"` + } + + var config fiatRatesConfig + err := json.Unmarshal([]byte(configJSON), &config) + if err != nil { + t.Errorf("Error parsing config: %v", err) + } + + if config.FiatRates == "" || config.FiatRatesParams == "" { + t.Errorf("Error parsing FiatRates config - empty parameter") + return + } + testStartTime := time.Date(2019, 11, 22, 16, 0, 0, 0, time.UTC) + fiatRates, err := NewFiatRatesDownloader(d, config.FiatRates, config.FiatRatesParams, &testStartTime, nil) + if err != nil { + t.Errorf("FiatRates init error: %v\n", err) + } + if config.FiatRates == "coingecko" { + timestamp, err := fiatRates.findEarliestMarketData() + if err != nil { + t.Errorf("Error looking up earliest market data: %v", err) + return + } + earliestTimestamp, _ := time.Parse(db.FiatRatesTimeFormat, "20130429000000") + if *timestamp != earliestTimestamp { + t.Errorf("Incorrect earliest available timestamp found. Wanted: %v, got: %v", earliestTimestamp, timestamp) + return + } + + // After verifying that findEarliestMarketData works correctly, + // set the earliest available timestamp to 2 days ago for easier testing + *timestamp = fiatRates.startTime.Add(time.Duration(-24*2) * time.Hour) + + err = fiatRates.syncHistorical(timestamp) + if err != nil { + t.Errorf("RatesDownloader syncHistorical error: %v", err) + return + } + ticker, err := fiatRates.downloader.getTicker(fiatRates.startTime) + if err != nil { + // Do not exit on GET error, log it, wait and try again + glog.Errorf("Sync GetData error: %v", err) + return + } + err = fiatRates.db.FiatRatesStoreTicker(ticker) + if err != nil { + glog.Errorf("Sync StoreTicker error %v", err) + return + } + } +} diff --git a/fiat/mock_data/01-02-2013.json b/fiat/mock_data/01-02-2013.json new file mode 100644 index 00000000..94ba1bd5 --- /dev/null +++ b/fiat/mock_data/01-02-2013.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"}} diff --git a/fiat/mock_data/01-05-2013.json b/fiat/mock_data/01-05-2013.json new file mode 100644 index 00000000..060f0e08 --- /dev/null +++ b/fiat/mock_data/01-05-2013.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"},"market_data":{"current_price":{"aud":112.481,"brl":232.8687,"btc":1.0,"cad":117.617,"chf":108.7145,"cny":718.7368,"dkk":661.3731,"eur":88.6291,"gbp":74.9767,"hkd":903.2559,"idr":1130568.3956,"inr":6274.6092,"jpy":11364.3607,"krw":128625.969,"mxn":1412.9046,"myr":353.6681,"nzd":136.2101,"php":4792.6186,"pln":368.8928,"rub":3623.3519,"sek":758.5144,"sgd":143.534,"twd":3433.0342,"usd":117.0,"xag":4.9088,"xau":0.0808,"xdr":76.8864,"zar":1049.0856},"market_cap":{"aud":1248780934.15,"brl":2585343237.705,"btc":11102150.0,"cad":1305801576.55,"chf":1206964686.175,"cny":7979523764.12,"dkk":7342663362.165,"eur":983973562.5649999,"gbp":832402569.9049999,"hkd":10028082490.185,"idr":12551739913210.54,"inr":69661652529.78,"jpy":126168837145.505,"krw":1428024801733.35,"mxn":15686278804.89,"myr":3926476296.415,"nzd":1512224961.715,"php":53208370589.99,"pln":4095503199.52,"rub":40226996296.585,"sek":8421140645.96,"sgd":1593535998.1,"twd":38114060643.53,"usd":1298951550.0,"xag":54498233.92,"xau":897053.72,"xdr":853604345.7599999,"zar":11647105694.04},"total_volume":{"aud":0.0,"brl":0.0,"btc":0.0,"cad":0.0,"chf":0.0,"cny":0.0,"dkk":0.0,"eur":0.0,"gbp":0.0,"hkd":0.0,"idr":0.0,"inr":0.0,"jpy":0.0,"krw":0.0,"mxn":0.0,"myr":0.0,"nzd":0.0,"php":0.0,"pln":0.0,"rub":0.0,"sek":0.0,"sgd":0.0,"twd":0.0,"usd":0.0,"xag":0.0,"xau":0.0,"xdr":0.0,"zar":0.0}},"community_data":{"facebook_likes":null,"twitter_followers":null,"reddit_average_posts_48h":0.0,"reddit_average_comments_48h":0.0,"reddit_subscribers":null,"reddit_accounts_active_48h":null},"developer_data":{"forks":null,"stars":null,"subscribers":null,"total_issues":null,"closed_issues":null,"pull_requests_merged":null,"pull_request_contributors":null,"code_additions_deletions_4_weeks":{"additions":null,"deletions":null},"commit_count_4_weeks":null},"public_interest_stats":{"alexa_rank":null,"bing_matches":null}} \ No newline at end of file diff --git a/fiat/mock_data/04-04-2013.json b/fiat/mock_data/04-04-2013.json new file mode 100644 index 00000000..94ba1bd5 --- /dev/null +++ b/fiat/mock_data/04-04-2013.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"}} diff --git a/fiat/mock_data/05-05-2013.json b/fiat/mock_data/05-05-2013.json new file mode 100644 index 00000000..1cd65273 --- /dev/null +++ b/fiat/mock_data/05-05-2013.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"},"market_data":{"current_price":{"aud":112.4208,"brl":233.1081,"btc":1.0,"cad":117.0104,"chf":108.4737,"cny":715.1351,"dkk":659.3659,"eur":88.4403,"gbp":74.4895,"hkd":899.8427,"idr":1128375.8759,"inr":6235.1253,"jpy":11470.4956,"krw":127344.157,"mxn":1401.3929,"myr":352.0832,"nzd":135.8774,"php":4740.2255,"pln":366.461,"rub":3602.9548,"sek":754.5925,"sgd":143.0844,"twd":3426.0044,"usd":116.79,"xag":4.9089,"xau":0.0807,"xdr":76.8136,"zar":1033.9804},"market_cap":{"aud":1249804517.76,"brl":2591509369.32,"btc":11117200.0,"cad":1300828018.88,"chf":1205923817.64,"cny":7950299933.72,"dkk":7330302583.48,"eur":983208503.1599998,"gbp":828114669.4000001,"hkd":10003731264.44,"idr":12544380287555.48,"inr":69317134985.16,"jpy":127519793684.32,"krw":1415710462200.4,"mxn":15579565147.88,"myr":3914179351.04,"nzd":1510576231.28,"php":52698034928.6,"pln":4074020229.2,"rub":40054769102.56,"sek":8388955741.0,"sgd":1590697891.68,"twd":38087576115.68,"usd":1298377788.0,"xag":54573223.08,"xau":897158.0399999999,"xdr":853952153.9199998,"zar":11494966902.88},"total_volume":{"aud":0.0,"brl":0.0,"btc":0.0,"cad":0.0,"chf":0.0,"cny":0.0,"dkk":0.0,"eur":0.0,"gbp":0.0,"hkd":0.0,"idr":0.0,"inr":0.0,"jpy":0.0,"krw":0.0,"mxn":0.0,"myr":0.0,"nzd":0.0,"php":0.0,"pln":0.0,"rub":0.0,"sek":0.0,"sgd":0.0,"twd":0.0,"usd":0.0,"xag":0.0,"xau":0.0,"xdr":0.0,"zar":0.0}},"community_data":{"facebook_likes":null,"twitter_followers":null,"reddit_average_posts_48h":0.0,"reddit_average_comments_48h":0.0,"reddit_subscribers":null,"reddit_accounts_active_48h":null},"developer_data":{"forks":null,"stars":null,"subscribers":null,"total_issues":null,"closed_issues":null,"pull_requests_merged":null,"pull_request_contributors":null,"code_additions_deletions_4_weeks":{"additions":null,"deletions":null},"commit_count_4_weeks":null},"public_interest_stats":{"alexa_rank":null,"bing_matches":null}} \ No newline at end of file diff --git a/fiat/mock_data/05-06-2013.json b/fiat/mock_data/05-06-2013.json new file mode 100644 index 00000000..d45b810a --- /dev/null +++ b/fiat/mock_data/05-06-2013.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"},"market_data":{"current_price":{"aud":126.3133,"brl":259.5964,"btc":1.0,"cad":126.0278,"chf":115.6713,"cny":748.4143,"dkk":695.3988,"eur":93.2043,"gbp":79.6307,"hkd":946.0816,"idr":1196029.7068,"inr":6892.8316,"jpy":12203.5904,"krw":137012.0733,"mxn":1551.7041,"myr":377.299,"nzd":152.0791,"php":5112.2715,"pln":396.1512,"rub":3891.7674,"sek":800.3999,"sgd":152.8465,"twd":3646.9125,"uah":987.805115156,"usd":121.309,"xag":5.3382,"xau":0.0868,"xdr":81.033,"zar":1200.5467},"market_cap":{"aud":1420386743.3035636,"brl":2919148539.142982,"btc":11244950.003709536,"cad":1417176310.0775046,"chf":1300717985.3640869,"cny":8415881385.561269,"dkk":7819724738.639607,"eur":1048077693.6307446,"gbp":895443240.2603929,"hkd":10638640291.429523,"idr":13449294255917.375,"inr":77509546725.9892,"jpy":137228763913.74965,"krw":1540693914163.0862,"mxn":17448835025.0511,"myr":4242708391.449604,"nzd":1710121876.1091428,"php":57487237422.88915,"pln":4454700437.909536,"rub":43762729839.06665,"sek":9000456858.474112,"sgd":1718751250.7419894,"twd":41009348730.40335,"uah":11107819133.33776,"usd":1364113640.0,"xag":60027792.10980224,"xau":976061.6603219877,"xdr":911212033.6505947,"zar":13500087618.61847},"total_volume":{"aud":0.0,"brl":0.0,"btc":0.0,"cad":0.0,"chf":0.0,"cny":0.0,"dkk":0.0,"eur":0.0,"gbp":0.0,"hkd":0.0,"idr":0.0,"inr":0.0,"jpy":0.0,"krw":0.0,"mxn":0.0,"myr":0.0,"nzd":0.0,"php":0.0,"pln":0.0,"rub":0.0,"sek":0.0,"sgd":0.0,"twd":0.0,"uah":0.0,"usd":0.0,"xag":0.0,"xau":0.0,"xdr":0.0,"zar":0.0}},"community_data":{"facebook_likes":null,"twitter_followers":null,"reddit_average_posts_48h":0.0,"reddit_average_comments_48h":0.0,"reddit_subscribers":null,"reddit_accounts_active_48h":null},"developer_data":{"forks":null,"stars":null,"subscribers":null,"total_issues":null,"closed_issues":null,"pull_requests_merged":null,"pull_request_contributors":null,"code_additions_deletions_4_weeks":{"additions":null,"deletions":null},"commit_count_4_weeks":null},"public_interest_stats":{"alexa_rank":null,"bing_matches":null}} \ No newline at end of file diff --git a/fiat/mock_data/07-10-2013.json b/fiat/mock_data/07-10-2013.json new file mode 100644 index 00000000..328a45dd --- /dev/null +++ b/fiat/mock_data/07-10-2013.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"},"market_data":{"current_price":{"aud":130.8239,"bdt":9961.3816372,"bhd":48.39643563999999,"bmd":128.38,"brl":272.2555,"btc":1.0,"cad":127.0763,"chf":111.3766,"cny":754.702,"dkk":677.221,"eur":90.7575,"gbp":76.6108,"hkd":955.6959,"idr":1401976.6268,"inr":7601.0098,"jpy":11938.215,"krw":132238.2292,"ltc":59.84440454817475,"mmk":124564.42058759999,"mxn":1619.1347,"myr":393.0957,"nzd":148.4503,"php":5315.0149,"pln":381.3587,"rub":3973.0086,"sek":791.16,"sgd":153.7814,"twd":3623.6843,"uah":1051.00353918,"usd":128.38,"vef":807.6801751200001,"xag":5.5156,"xau":0.0932,"xdr":80.1381,"zar":1233.5253},"market_cap":{"aud":1544578916.545,"bdt":117609550368.68365,"bhd":571394937.205442,"bmd":1515724889.0,"brl":3214398173.525,"btc":11806550.0,"cad":1500332689.765,"chf":1314973396.73,"cny":8910426898.1,"dkk":7995643597.55,"eur":1071532961.6249999,"gbp":904509240.74,"hkd":11283471428.145,"idr":16552507143145.54,"inr":89741702254.19,"jpy":140949132308.25,"krw":1561277264961.26,"ltc":706555954.5182526,"mmk":1470676059888.5288,"mxn":19116394792.285,"myr":4641104036.835,"nzd":1752685889.465,"php":62751989167.595,"pln":4502530559.485,"rub":46907524686.33,"sek":9340870098.0,"sgd":1815627788.17,"twd":42783209872.165,"uah":12408725835.50563,"usd":1515724889.0,"vef":9535916371.563036,"xag":65120207.18,"xau":1100370.4600000002,"xdr":946154484.5549998,"zar":14563678130.715},"total_volume":{"aud":0.0,"bdt":0.0,"bhd":0.0,"bmd":0.0,"brl":0.0,"btc":0.0,"cad":0.0,"chf":0.0,"cny":0.0,"dkk":0.0,"eur":0.0,"gbp":0.0,"hkd":0.0,"idr":0.0,"inr":0.0,"jpy":0.0,"krw":0.0,"ltc":0.0,"mmk":0.0,"mxn":0.0,"myr":0.0,"nzd":0.0,"php":0.0,"pln":0.0,"rub":0.0,"sek":0.0,"sgd":0.0,"twd":0.0,"uah":0.0,"usd":0.0,"vef":0.0,"xag":0.0,"xau":0.0,"xdr":0.0,"zar":0.0}},"community_data":{"facebook_likes":null,"twitter_followers":null,"reddit_average_posts_48h":0.0,"reddit_average_comments_48h":0.0,"reddit_subscribers":null,"reddit_accounts_active_48h":null},"developer_data":{"forks":null,"stars":null,"subscribers":null,"total_issues":null,"closed_issues":null,"pull_requests_merged":null,"pull_request_contributors":null,"code_additions_deletions_4_weeks":{"additions":null,"deletions":null},"commit_count_4_weeks":null},"public_interest_stats":{"alexa_rank":null,"bing_matches":null}} \ No newline at end of file diff --git a/fiat/mock_data/13-06-2014.json b/fiat/mock_data/13-06-2014.json new file mode 100644 index 00000000..c784ee48 --- /dev/null +++ b/fiat/mock_data/13-06-2014.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"},"market_data":{"current_price":{"aud":635.4009,"bdt":46187.0412867627,"bhd":224.2182020654,"bmd":594.6833,"brl":1330.3026,"btc":1.0,"cad":645.6162,"chf":537.5144,"cny":3818.09,"dkk":3291.0056,"eur":439.2353,"gbp":350.5075,"hkd":4609.773,"idr":7056654.8237,"inr":35623.7845,"jpy":60664.7779,"krw":605273.662,"ltc":58.68544600938967,"mmk":576316.9820261401,"mxn":7774.4393,"myr":1921.5065,"nzd":689.3958,"php":26182.8705,"pln":1816.6144,"rub":20449.5057,"sek":3957.6568,"sgd":743.7241,"twd":17936.3326,"uah":6989.384186896,"usd":594.6833,"vef":3749.9319498579002,"vnd":12616057.538675,"xag":30.3811,"xau":0.4678,"xdr":388.0442,"zar":6383.9883},"market_cap":{"aud":8191238932.305,"bdt":595417933396.2369,"bhd":2890497741.0160007,"bmd":7666330027.785,"brl":17149529452.77,"btc":12891450.0,"cad":8322928961.49,"chf":6929340011.88,"cny":49220716330.5,"dkk":42425834142.12,"eur":5662379908.185,"gbp":4518549910.875,"hkd":59426658140.85,"idr":90970512826987.36,"inr":459242236692.525,"jpy":782056951058.955,"krw":7802855149989.9,"ltc":756540492.9577465,"mmk":7429561557940.883,"mxn":100223795513.985,"myr":24771004969.425,"nzd":8887311485.91,"php":337535165907.225,"pln":23418793706.88,"rub":263623780256.265,"sek":51019934754.36,"sgd":9587682048.945,"twd":231225334896.27,"uah":90103296776.16043,"usd":7666330027.785,"vef":48342060234.99562,"vnd":162639274956951.8,"xag":391656431.595,"xau":6030620.31,"xdr":5002452402.09,"zar":82298865970.035},"total_volume":{"aud":40549103.56573137,"bdt":2947498375.4849124,"bhd":14308835.723827252,"bmd":37950646.151919045,"brl":84895343.87055749,"btc":63816.5661486022,"cad":41201008.933909185,"chf":34302323.26342622,"cny":243657393.04631656,"dkk":210020676.56782028,"eur":28030488.577251133,"gbp":22368185.059331186,"hkd":294179883.5845404,"idr":450331479344.50385,"inr":2273387600.0077996,"jpy":3871417811.7456107,"krw":38626486689.029686,"ltc":3745103.647218439,"mmk":36778570806.03395,"mxn":496138019.85674256,"myr":122623946.66221909,"nzd":43994872.673268534,"php":1670900887.223535,"pln":115930093.0241033,"rub":1305017233.2102678,"sek":252564066.9706653,"sgd":47461918.22395964,"twd":1144635155.8312302,"uah":446038498.30104274,"usd":37950646.151919045,"vef":239307780.33086348,"vnd":805113470451.4246,"xag":1938817.4778172984,"xau":29853.38964431611,"xdr":24763648.357881423,"zar":407404211.6388525}},"community_data":{"facebook_likes":22450,"twitter_followers":54747,"reddit_average_posts_48h":2.449,"reddit_average_comments_48h":266.163,"reddit_subscribers":122886,"reddit_accounts_active_48h":"957.0"},"developer_data":{"forks":3894,"stars":5469,"subscribers":757,"total_issues":4332,"closed_issues":3943,"pull_requests_merged":1950,"pull_request_contributors":201,"code_additions_deletions_4_weeks":{"additions":null,"deletions":null},"commit_count_4_weeks":null},"public_interest_stats":{"alexa_rank":null,"bing_matches":null}} diff --git a/fiat/mock_data/20-04-2013.json b/fiat/mock_data/20-04-2013.json new file mode 100644 index 00000000..94ba1bd5 --- /dev/null +++ b/fiat/mock_data/20-04-2013.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"}} diff --git a/fiat/mock_data/20-11-2019.json b/fiat/mock_data/20-11-2019.json new file mode 100644 index 00000000..2dcf9060 --- /dev/null +++ b/fiat/mock_data/20-11-2019.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"},"market_data":{"current_price":{"aed":29895.556252804836,"ars":485421.07790578756,"aud":11927.653049612183,"bch":33.63953944857689,"bdt":690035.4308521386,"bhd":3068.738317969027,"bmd":8138.831605359057,"bnb":440.3065882935356,"brl":34128.562570752125,"btc":1.0,"cad":10801.69453000047,"chf":8061.919646688408,"clp":6426418.993942025,"cny":57197.26687298181,"czk":187745.75358926164,"dkk":54903.744126591664,"eos":2611.7345692770737,"eth":46.298470465960534,"eur":7346.923290157612,"gbp":6295.369969082021,"hkd":63709.552982009845,"huf":2444677.126964913,"idr":114676137.3195094,"ils":28177.44890091363,"inr":584864.5454373802,"jpy":882985.9102812062,"krw":9504690.325370407,"kwd":2470.900442397376,"lkr":1459699.318199838,"ltc":147.7002152423325,"mmk":12336753.62587893,"mxn":157572.5004020829,"myr":33838.006282440794,"nok":74323.81022013885,"nzd":12659.561898218928,"php":414372.5288556039,"pkr":1268614.8449705977,"pln":31481.39945227751,"rub":519856.47442806256,"sar":30523.100863736137,"sek":78433.10629768472,"sgd":11082.158667121108,"thb":245711.32616578983,"try":46424.77447078138,"twd":247819.28355157803,"uah":196944.8714820098,"usd":8138.831605359057,"vef":2022399076.2122076,"vnd":188819978.95240378,"xag":474.4301555409632,"xau":5.522685574132437,"xdr":5913.04021558867,"xlm":124534.24474263463,"xrp":32009.044210117067,"zar":120220.54543519647},"market_cap":{"aed":539216184347.0786,"ars":8754702929895.195,"aud":215108532402.1839,"bch":606965693.7308347,"bdt":12445939086798.977,"bhd":55349810272.20994,"bmd":146797393103.30997,"bnb":7956861576.178785,"brl":615565508500.11,"btc":18056975.0,"cad":194793828360.1895,"chf":145401203097.50433,"clp":115881862115752.81,"cny":1031662719251.4414,"czk":3386836054983.015,"dkk":990235760930.7228,"eos":47156656992.1783,"eth":836285546.5188296,"eur":132513272767.39243,"gbp":113531342257.38261,"hkd":1149073035824.1863,"huf":44093634990629.44,"idr":2068029594287882.2,"ils":508227254662.9701,"inr":10549007465796.953,"jpy":15924140811667.746,"krw":171432931613907.47,"kwd":44566807761.80631,"lkr":26328110104320.3,"ltc":2665915975.9376416,"mmk":222513913476766.62,"mxn":2842848955360.077,"myr":610324841566.3215,"nok":1340509754601.4958,"nzd":228301948107.3441,"php":7486962257826.343,"pkr":22881583146556.33,"pln":567829491818.596,"rub":9376404569427.018,"sar":550534997342.308,"sek":1414830338781.8389,"sgd":199869788618.91415,"thb":4430345323857.896,"try":837402059023.0033,"twd":4471888986106.136,"uah":3552229007857.6865,"usd":146797393103.30997,"vef":36477338099366760,"vnd":3405959412743900,"xag":8552645126.132073,"xau":99600563.24666482,"xdr":106651535632.2029,"xlm":2242611242035.2026,"xrp":577654290936.0109,"zar":2168300254311.0623},"total_volume":{"aed":91806191763.19203,"ars":1490678420139.214,"aud":36628601050.19057,"bch":103303580.75045899,"bdt":2119028144266.9175,"bhd":9423781116.770079,"bmd":24993518393.55118,"bnb":1352136442.5417123,"brl":104805320679.67813,"btc":3074333.834646516,"cad":33170897741.553368,"chf":24757329644.7321,"clp":19734874625492.48,"cny":175646949214.35953,"czk":576547982950.5977,"dkk":168603775731.05692,"eos":8020369404.536936,"eth":142177861.5523058,"eur":22561649053.858624,"gbp":19332436490.375057,"hkd":195645512956.95944,"huf":7507353106907.763,"idr":352158674165137.06,"ils":86530060030.31366,"inr":1796059125304.906,"jpy":2711559307275.5625,"krw":29187930650356.914,"kwd":7587882223.171772,"lkr":4482587123987.1,"ltc":453572235.5970587,"mmk":37884907025485.92,"mxn":483889012339.82043,"myr":103913052073.02832,"nok":228240809969.9092,"nzd":38876218172.28588,"php":1272495601815.3118,"pkr":3895786274927.592,"pln":96676153828.6573,"rub":1596425996462.3315,"sar":93733316998.92708,"sek":240860037406.81345,"sgd":34032174385.395035,"thb":754554320301.3098,"try":142565728216.80225,"twd":761027641565.2402,"uah":604797531952.8716,"usd":24993518393.55118,"vef":6210580456920606,"vnd":579846819033510.8,"xag":1456926423.0950127,"xau":16959601.841128074,"xdr":18158340970.319584,"xlm":382431912530.61053,"xrp":98296496845.90811,"zar":369184983706.85724}},"community_data":{"facebook_likes":null,"twitter_followers":68549,"reddit_average_posts_48h":6.429,"reddit_average_comments_48h":227.357,"reddit_subscribers":1190720,"reddit_accounts_active_48h":"3557.33333333333"},"developer_data":{"forks":24603,"stars":41204,"subscribers":3495,"total_issues":0,"closed_issues":0,"pull_requests_merged":6973,"pull_request_contributors":623,"code_additions_deletions_4_weeks":{"additions":3441,"deletions":-1615},"commit_count_4_weeks":375},"public_interest_stats":{"alexa_rank":12740,"bing_matches":135000000}} diff --git a/fiat/mock_data/21-11-2019.json b/fiat/mock_data/21-11-2019.json new file mode 100644 index 00000000..14dd021d --- /dev/null +++ b/fiat/mock_data/21-11-2019.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"},"market_data":{"current_price":{"aed":29737.733661544353,"ars":482992.0576847001,"aud":11909.083120440264,"bch":33.38939623106484,"bdt":686435.825992477,"bhd":3052.4408925589955,"bmd":8095.865638011639,"bnb":447.42498131132714,"brl":33973.49056335204,"btc":1.0,"cad":10770.529152304092,"chf":8017.651480082802,"clp":6417583.259568359,"cny":56962.51062904988,"czk":186532.80032847245,"dkk":54611.02597516125,"eos":2625.1996127062725,"eth":46.30756277128346,"eur":7307.587392569718,"gbp":6263.7712441296035,"hkd":63361.07803605232,"huf":2437260.3503234047,"idr":113658578.86366026,"ils":28110.059875022114,"inr":581104.954710677,"jpy":878241.5283779115,"krw":9474186.762883117,"kwd":2457.953382894158,"lkr":1451476.6944985148,"ltc":147.17764652439055,"mmk":12276458.59050864,"mxn":157745.51319696513,"myr":33723.328315137434,"nok":73919.30120786528,"nzd":12630.060434833318,"php":412223.4002195826,"pkr":1261966.2105007921,"pln":31386.456698725444,"rub":516701.6230282524,"sar":30360.961494224146,"sek":78027.18391192055,"sgd":11029.362072616914,"thb":244420.8060296636,"try":46147.243723230094,"twd":246632.45889225203,"uah":195582.93374510246,"usd":8095.865638011639,"vef":2011722564.2894442,"vnd":187451397.8769113,"xag":471.370523519991,"xau":5.491344703606915,"xdr":5886.050536922535,"xlm":126226.86768614998,"xrp":32338.763084294602,"zar":119758.9020368509},"market_cap":{"aed":536925789662.0078,"ars":8721126622799.587,"aud":214993230987.64774,"bch":603145132.8718755,"bdt":12393852945152.863,"bhd":55112950276.81427,"bmd":146173851045.95633,"bnb":8069562575.040651,"brl":613403948529.2512,"btc":18058775.0,"cad":194516467063.8744,"chf":144785199461.0198,"clp":115857415095711.94,"cny":1028479215959.3489,"czk":3368167110571.1367,"dkk":986119641838.593,"eos":47362660343.0749,"eth":834881213.8173289,"eur":131953912642.35466,"gbp":113092515946.4908,"hkd":1143920014822.8933,"huf":44007835435061.79,"idr":2051934847845294.5,"ils":507537536909.2167,"inr":10491950179817.375,"jpy":15862731500313.05,"krw":171059949186530.47,"kwd":44379258220.658554,"lkr":26206949031136.875,"ltc":2660325666.65285,"mmk":221656004387641.53,"mxn":2848592157028.2905,"myr":608887176531.9319,"nok":1334790467520.1284,"nzd":228006504250.86572,"php":7443249821227.297,"pkr":22785267089002.48,"pln":566719821025.2997,"rub":9329253695306.072,"sar":548178398889.3755,"sek":1408776800748.5935,"sgd":199092731818.5716,"thb":4413592261082.244,"try":833269884841.5155,"twd":4453040344437.87,"uah":3531322270240.796,"usd":146173851045.95633,"vef":36322395603696830,"vnd":3384688469578368,"xag":8512844964.182701,"xau":99193575.31978594,"xdr":106274821359.85637,"xlm":2281320453520.52,"xrp":583446527953.1724,"zar":2162554421914.3018},"total_volume":{"aed":86108153638.4696,"ars":1398544851555.271,"aud":34483769701.4642,"bch":96681855.22416703,"bdt":1987633699334.181,"bhd":8838603921.209757,"bmd":23442272034.865948,"bnb":1295557337.0497513,"brl":98373150367.11145,"btc":2896005.038733083,"cad":31186989216.112743,"chf":23215796244.73709,"clp":18582661731791.32,"cny":164939826037.3168,"czk":540121692261.5999,"dkk":158130900913.4299,"eos":7601490219.642489,"eth":134087512.35435177,"eur":21159744891.37511,"gbp":18137285873.37578,"hkd":183467425740.0729,"huf":7057295996096.396,"idr":329108145311632.75,"ils":81395084845.85982,"inr":1682639144253.6147,"jpy":2543023530910.265,"krw":27433318848801.867,"kwd":7117214443.4175005,"lkr":4202875028575.5767,"ltc":426165475.2614542,"mmk":35547536825741.08,"mxn":456765637917.7518,"myr":97648784161.23398,"nok":214039664814.34357,"nzd":36571421237.528984,"php":1193628145421.9192,"pkr":3654131198332.759,"pln":90882172338.37012,"rub":1496153783854.0442,"sar":87912763181.98567,"sek":225934390856.19467,"sgd":31936462095.3393,"thb":707741368511.1285,"try":133623294825.93925,"twd":714145398712.4277,"uah":566327128343.4884,"usd":23442272034.865948,"vef":5825114906715977,"vnd":542781570103440.4,"xag":1364893704.471942,"xau":15900658.698529225,"xdr":17043563229.317081,"xlm":365500701557.407,"xrp":93639657003.90553,"zar":346772153302.9578}},"community_data":{"facebook_likes":null,"twitter_followers":68537,"reddit_average_posts_48h":6.5,"reddit_average_comments_48h":248.25,"reddit_subscribers":1191683,"reddit_accounts_active_48h":"3672.76923076923"},"developer_data":{"forks":24612,"stars":41218,"subscribers":3495,"total_issues":0,"closed_issues":0,"pull_requests_merged":6978,"pull_request_contributors":623,"code_additions_deletions_4_weeks":{"additions":3491,"deletions":-1642},"commit_count_4_weeks":388},"public_interest_stats":{"alexa_rank":12740,"bing_matches":135000000}} diff --git a/fiat/mock_data/22-11-2019.json b/fiat/mock_data/22-11-2019.json new file mode 100644 index 00000000..19ad0808 --- /dev/null +++ b/fiat/mock_data/22-11-2019.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"},"market_data":{"current_price":{"aed":28035.679772629657,"ars":455975.92278356064,"aud":11242.023660019182,"bch":33.7483287902988,"bdt":647296.9519021783,"bhd":2877.7787238679057,"bmd":7632.494765498649,"bnb":454.38160859138037,"brl":32004.577050688924,"btc":1.0,"cad":10134.64789197728,"chf":7577.578965660877,"clp":6088438.929707239,"cny":53650.33220564306,"czk":176064.87038406474,"dkk":51541.93185162167,"eos":2702.457481762241,"eth":47.35816140456382,"eur":6897.386297149177,"gbp":5908.00889818188,"hkd":59688.78043936735,"huf":2307617.389787238,"idr":107442628.81392431,"ils":26414.69053433299,"inr":547635.3233044049,"jpy":828835.8421129878,"krw":8981385.56540522,"kwd":2317.3933256902314,"lkr":1371578.7340592865,"ltc":150.51551045178746,"mmk":11576409.193776581,"mxn":147997.88975040163,"myr":31823.686924746547,"nok":69791.7992730364,"nzd":11915.530263116301,"php":388189.40886026394,"pkr":1185305.8751410455,"pln":29644.4875492805,"rub":486228.079036091,"sar":28621.16844609101,"sek":73412.31132752451,"sgd":10402.327115898055,"thb":230471.15540126187,"try":43490.718423287806,"twd":232501.04791412465,"uah":184490.35082571974,"usd":7632.494765498649,"vef":1896580628.6955354,"vnd":177318106.98911732,"xag":446.02452345840845,"xau":5.21078050135358,"xdr":5544.7555748075,"xlm":125542.05293968241,"xrp":31338.87101002938,"zar":112082.42238187103},"market_cap":{"aed":505952466029.5935,"ars":8228063686842.731,"aud":202906746120.7657,"bch":610302833.79301,"bdt":11681596156197.934,"bhd":51934508235.10716,"bmd":137741605692.4732,"bnb":8208811080.573547,"brl":577564326829.1097,"btc":18060475.0,"cad":182927050731.8614,"chf":136752620963.6013,"clp":109834974835741.86,"cny":968213294733.5328,"czk":3177872948714.952,"dkk":930200055102.5531,"eos":48834956976.25193,"eth":855479661.9732217,"eur":124481359054.06459,"gbp":106626741157.78348,"hkd":1077223378894.6129,"huf":41647543632679.81,"idr":1939126324938642.2,"ils":476698903812.6255,"inr":9882973293887.5,"jpy":14957705316159.902,"krw":162084679666504.06,"kwd":41821381803.56006,"lkr":24752517095323.91,"ltc":2718657573.4072695,"mmk":208916381798492.3,"mxn":2670451606202.2573,"myr":574313624934.7668,"nok":1259581556794.9636,"nzd":215062993789.54813,"php":7005804182301.389,"pkr":21390900288155.363,"pln":535015393864.2815,"rub":8774828990639.0205,"sar":516518624602.2619,"sek":1324936091931.084,"sgd":187713296046.46274,"thb":4159796491912.691,"try":784893129459.0262,"twd":4195747188740.0435,"uah":3329448357091.939,"usd":137741605692.4732,"vef":34227086837012150,"vnd":3200663218868120.5,"xag":8055064362.341162,"xau":94058232.86316237,"xdr":100064731062.59404,"xlm":2269248253100.4473,"xrp":566093764804.5127,"zar":2022682035850.9631},"total_volume":{"aed":96938279018.82988,"ars":1576616710817.6833,"aud":38871268152.918,"bch":116690764.74068807,"bdt":2238142718151.254,"bhd":9950424571.517105,"bmd":26390689050.100677,"bnb":1571104089.9267807,"brl":110661437324.88211,"btc":3458209.3682739506,"cad":35042322250.70604,"chf":26200808042.38517,"clp":21051845239481.676,"cny":185505431470.96753,"czk":608775163260.6022,"dkk":178215267527.76758,"eos":9344220633.428228,"eth":163749147.56075427,"eur":23848922615.61833,"gbp":20427976766.120914,"hkd":206384425112.9548,"huf":7978991778122.924,"idr":371501729758266.6,"ils":91333424478.36926,"inr":1893545161079.935,"jpy":2865845264861.2,"krw":31054715525924.953,"kwd":8012793790.76967,"lkr":4742473986606.729,"ltc":520433771.07914567,"mmk":40027464772158.91,"mxn":511728656025.9774,"myr":110035977994.39453,"nok":241317384348.23724,"nzd":41200035336.076935,"php":1342232952203.5798,"pkr":4098402912964.3467,"pln":102501014019.56622,"rub":1681218845936.662,"sar":98962708775.86293,"sek":253835939652.59772,"sgd":35967870106.38203,"thb":796894434137.848,"try":150376785276.3785,"twd":803913143453.4763,"uah":637907739340.2526,"usd":26390689050.100677,"vef":6557760099174900,"vnd":613108448584249.1,"xag":1542208002.620382,"xau":18017187.321394224,"xdr":19171964702.159466,"xlm":434083662502.9747,"xrp":108359641954.22104,"zar":387544629631.8232}},"community_data":{"facebook_likes":null,"twitter_followers":68552,"reddit_average_posts_48h":8.0,"reddit_average_comments_48h":363.727,"reddit_subscribers":1192543,"reddit_accounts_active_48h":"4102.33333333333"},"developer_data":{"forks":24627,"stars":41240,"subscribers":3497,"total_issues":0,"closed_issues":0,"pull_requests_merged":6982,"pull_request_contributors":623,"code_additions_deletions_4_weeks":{"additions":3706,"deletions":-1803},"commit_count_4_weeks":370},"public_interest_stats":{"alexa_rank":12740,"bing_matches":135000000}} \ No newline at end of file diff --git a/fiat/mock_data/23-09-2011.json b/fiat/mock_data/23-09-2011.json new file mode 100644 index 00000000..94ba1bd5 --- /dev/null +++ b/fiat/mock_data/23-09-2011.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"}} diff --git a/fiat/mock_data/27-04-2013.json b/fiat/mock_data/27-04-2013.json new file mode 100644 index 00000000..94ba1bd5 --- /dev/null +++ b/fiat/mock_data/27-04-2013.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"}} diff --git a/fiat/mock_data/28-04-2013.json b/fiat/mock_data/28-04-2013.json new file mode 100644 index 00000000..b9f2a4ad --- /dev/null +++ b/fiat/mock_data/28-04-2013.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"},"market_data":{"current_price":{"aud":130.7952,"brl":268.8555,"btc":1.0,"cad":136.8008,"chf":126.7471,"cny":830.3415,"dkk":769.4261,"eur":103.1862,"gbp":86.889,"hkd":1043.747,"idr":1306348.9692,"inr":7304.2353,"jpy":13203.1967,"krw":149390.4586,"mxn":1633.6086,"myr":407.992,"nzd":158.5211,"php":5543.837,"pln":429.2283,"rub":4203.4233,"sek":884.1254,"sgd":166.2931,"usd":135.3,"xag":5.716,"xau":0.0938,"zar":1223.2239},"market_cap":{"aud":1450558006.5599997,"brl":2981688151.6499996,"btc":11090299.999999998,"cad":1517161912.2399998,"chf":1405663363.1299999,"cny":9208736337.449999,"dkk":8533166276.829999,"eur":1144365913.86,"gbp":963625076.6999998,"hkd":11575467354.099998,"idr":14487801973118.756,"inr":81006160747.59,"jpy":146427412362.00998,"krw":1656785003011.5798,"mxn":18117209456.579998,"myr":4524753677.599999,"nzd":1758046555.3299997,"php":61482815481.09999,"pln":4760270615.489999,"rub":46617225423.99,"sek":9805215923.619999,"sgd":1844240366.9299998,"usd":1500517590,"xag":63392154.79999999,"xau":1040270.1399999998,"zar":13565920018.169998},"total_volume":{"aud":0.0,"brl":0.0,"btc":0.0,"cad":0.0,"chf":0.0,"cny":0.0,"dkk":0.0,"eur":0.0,"gbp":0.0,"hkd":0.0,"idr":0.0,"inr":0.0,"jpy":0.0,"krw":0.0,"mxn":0.0,"myr":0.0,"nzd":0.0,"php":0.0,"pln":0.0,"rub":0.0,"sek":0.0,"sgd":0.0,"usd":0,"xag":0.0,"xau":0.0,"zar":0.0}},"community_data":{"facebook_likes":null,"twitter_followers":null,"reddit_average_posts_48h":0.0,"reddit_average_comments_48h":0.0,"reddit_subscribers":null,"reddit_accounts_active_48h":null},"developer_data":{"forks":null,"stars":null,"subscribers":null,"total_issues":null,"closed_issues":null,"pull_requests_merged":null,"pull_request_contributors":null,"code_additions_deletions_4_weeks":{"additions":null,"deletions":null},"commit_count_4_weeks":null},"public_interest_stats":{"alexa_rank":null,"bing_matches":null}} diff --git a/fiat/mock_data/29-04-2013.json b/fiat/mock_data/29-04-2013.json new file mode 100644 index 00000000..d7f64132 --- /dev/null +++ b/fiat/mock_data/29-04-2013.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","localization":{"en":"Bitcoin","de":"Bitcoin","es":"Bitcoin","fr":"Bitcoin","it":"Bitcoin","pl":"Bitcoin","ro":"Bitcoin","hu":"Bitcoin","nl":"Bitcoin","pt":"Bitcoin","sv":"Bitcoin","vi":"Bitcoin","tr":"Bitcoin","ru":"биткоина","ja":"ビットコイン","zh":"比特币","zh-tw":"比特幣","ko":"비트코인","ar":"بيتكوين","th":"บิตคอยน์","id":"Bitcoin"},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579"},"market_data":{"current_price":{"aud":140.0534,"brl":287.7259,"btc":1.0,"cad":146.3907,"chf":135.5619,"cny":889.0842,"dkk":822.6608,"eur":110.3745,"gbp":93.0697,"hkd":1117.9148,"idr":1394387.3129,"inr":7822.3338,"jpy":14108.4087,"krw":159839.6429,"mxn":1748.706,"myr":436.5932,"nzd":169.7084,"php":5937.7695,"pln":459.2644,"rub":4501.5503,"sek":944.2334,"sgd":178.0707,"twd":4262.3287,"usd":141.96,"xag":6.1223,"xau":0.1005,"xdr":95.6015,"zar":1309.9167},"market_cap":{"aud":1553878467.66,"brl":3192290087.91,"btc":11094900.0,"cad":1624190177.43,"chf":1504045724.31,"cny":9864300290.58,"dkk":9127339309.92,"eur":1224594040.05,"gbp":1032599014.53,"hkd":12403152914.52,"idr":15470587797894.21,"inr":86788011277.62,"jpy":156531383685.63,"krw":1773404854011.21,"mxn":19401718199.4,"myr":4843957894.68,"nzd":1882897727.16,"php":65878958825.55,"pln":5095492591.56,"rub":49944250423.47,"sek":10476175149.66,"sgd":1975676609.43,"twd":47290110693.63,"usd":1575032004.0,"xag":67926306.27,"xau":1115037.45,"xdr":1060689082.35,"zar":14533394794.83},"total_volume":{"aud":0.0,"brl":0.0,"btc":0.0,"cad":0.0,"chf":0.0,"cny":0.0,"dkk":0.0,"eur":0.0,"gbp":0.0,"hkd":0.0,"idr":0.0,"inr":0.0,"jpy":0.0,"krw":0.0,"mxn":0.0,"myr":0.0,"nzd":0.0,"php":0.0,"pln":0.0,"rub":0.0,"sek":0.0,"sgd":0.0,"twd":0.0,"usd":0.0,"xag":0.0,"xau":0.0,"xdr":0.0,"zar":0.0}},"community_data":{"facebook_likes":null,"twitter_followers":null,"reddit_average_posts_48h":0.0,"reddit_average_comments_48h":0.0,"reddit_subscribers":null,"reddit_accounts_active_48h":null},"developer_data":{"forks":null,"stars":null,"subscribers":null,"total_issues":null,"closed_issues":null,"pull_requests_merged":null,"pull_request_contributors":null,"code_additions_deletions_4_weeks":{"additions":null,"deletions":null},"commit_count_4_weeks":null},"public_interest_stats":{"alexa_rank":null,"bing_matches":null}} diff --git a/fiat/mock_data/current.json b/fiat/mock_data/current.json new file mode 100644 index 00000000..7d66dd3e --- /dev/null +++ b/fiat/mock_data/current.json @@ -0,0 +1 @@ +{"id":"bitcoin","symbol":"btc","name":"Bitcoin","asset_platform_id":null,"block_time_in_minutes":10,"categories":["Cryptocurrency"],"description":{"en":"Bitcoin is the first successful internet money based on peer-to-peer technology; whereby no central bank or authority is involved in the transaction and production of the Bitcoin currency. It was created by an anonymous individual/group under the name, Satoshi Nakamoto. The source code is available publicly as an open source project, anybody can look at it and be part of the developmental process.\r\n\r\nBitcoin is changing the way we see money as we speak. The idea was to produce a means of exchange, independent of any central authority, that could be transferred electronically in a secure, verifiable and immutable way. It is a decentralized peer-to-peer internet currency making mobile payment easy, very low transaction fees, protects your identity, and it works anywhere all the time with no central authority or banks.\r\n\r\nBitcoin is design to have only 21 million BTC ever created, thus making it a deflationary currency. Bitcoin uses the \u003ca href=\"https://www.coingecko.com/en?hashing_algorithm=SHA-256\"\u003eSHA-256\u003c/a\u003e hashing algorithm with an average transaction confirmation time of 10 minutes. Miners today are mining Bitcoin using ASIC chip dedicated to only mining Bitcoin, and the hash rate has shot up to peta hashes.\r\n\r\nBeing the first successful online cryptography currency, Bitcoin has inspired other alternative currencies such as \u003ca href=\"https://www.coingecko.com/en/coins/litecoin\"\u003eLitecoin\u003c/a\u003e, \u003ca href=\"https://www.coingecko.com/en/coins/peercoin\"\u003ePeercoin\u003c/a\u003e, \u003ca href=\"https://www.coingecko.com/en/coins/primecoin\"\u003ePrimecoin\u003c/a\u003e, and so on.\r\n\r\nThe cryptocurrency then took off with the innovation of the turing-complete smart contract by \u003ca href=\"https://www.coingecko.com/en/coins/ethereum\"\u003eEthereum\u003c/a\u003e which led to the development of other amazing projects such as \u003ca href=\"https://www.coingecko.com/en/coins/eos\"\u003eEOS\u003c/a\u003e, \u003ca href=\"https://www.coingecko.com/en/coins/tron\"\u003eTron\u003c/a\u003e, and even crypto-collectibles such as \u003ca href=\"https://www.coingecko.com/buzz/ethereum-still-king-dapps-cryptokitties-need-1-billion-on-eos\"\u003eCryptoKitties\u003c/a\u003e."},"links":{"homepage":["http://www.bitcoin.org","",""],"blockchain_site":["https://blockchair.com/bitcoin/","https://btc.com/","","",""],"official_forum_url":["https://bitcointalk.org/","",""],"chat_url":["","",""],"announcement_url":["",""],"twitter_screen_name":"btc","facebook_username":"bitcoins","bitcointalk_thread_identifier":null,"telegram_channel_identifier":"","subreddit_url":"https://www.reddit.com/r/Bitcoin/","repos_url":{"github":["https://github.com/bitcoin/bitcoin","https://github.com/bitcoin/bips"],"bitbucket":[]}},"image":{"thumb":"https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579","small":"https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579","large":"https://assets.coingecko.com/coins/images/1/large/bitcoin.png?1547033579"},"country_origin":"","genesis_date":"2009-01-03","sentiment_votes_up_percentage":38.69,"sentiment_votes_down_percentage":61.31,"market_cap_rank":1,"coingecko_rank":1,"coingecko_score":87.571,"developer_score":91.999,"community_score":80.5,"liquidity_score":100.084,"public_interest_score":44.29,"market_data":{"current_price":{"aed":26233,"ars":427146,"aud":10530.78,"bch":34.754373,"bdt":605978,"bhd":2693.12,"bmd":7142.59,"bnb":469.136,"brl":29879,"btc":1.0,"cad":9489.19,"chf":7113.95,"clp":5688362,"cny":50277,"czk":165074,"dkk":48362,"eos":2760,"eth":48.466911,"eur":6471.78,"gbp":5562.63,"hkd":55896,"huf":2162211,"idr":100796288,"ils":24797,"inr":512355,"jpy":776361,"krw":8423047,"kwd":2169.53,"lkr":1286409,"ltc":152.386,"mmk":10823325,"mxn":138495,"myr":29800,"nok":65379,"nzd":11149.85,"php":363629,"pkr":1110673,"pln":27816,"rub":455640,"sar":26785,"sek":68693,"sgd":9743.25,"thb":215688,"try":40799,"twd":218342,"uah":172189,"usd":7142.59,"vef":1774846373,"vnd":166109525,"xag":418.79,"xau":4.88,"xdr":5191.74,"xlm":125849,"xrp":30745,"zar":104995},"roi":null,"ath":{"aed":72229,"ars":654921,"aud":25717,"bch":42.242547,"bdt":1631248,"bhd":7416.37,"bmd":19665.39,"bnb":143062,"brl":64777,"btc":1.003301,"cad":25303,"chf":19484.57,"clp":12582805,"cny":130006,"czk":429834,"dkk":124584,"eos":3287,"eth":624.203,"eur":16727.68,"gbp":14759.86,"hkd":153608,"huf":5263109,"idr":266681922,"ils":69096,"inr":1259942,"jpy":2214028,"krw":21418073,"kwd":5939.64,"lkr":3024387,"ltc":318.98,"mmk":26871485,"mxn":376059,"myr":80224,"nok":164805,"nzd":28131,"php":991988,"pkr":2181203,"pln":70407,"rub":1157051,"sar":73750,"sek":167278,"sgd":26517,"thb":639578,"try":80284,"twd":589706,"uah":541437,"usd":19665.39,"vef":3454441855,"vnd":446720468,"xag":1225.45,"xau":15.67,"xdr":13907.05,"xlm":189028,"xrp":42151,"zar":257660},"ath_change_percentage":{"aed":-63.62124,"ars":-34.67198,"aud":-58.99662,"bch":-17.39149,"bdt":-62.79247,"bhd":-63.62871,"bmd":-63.62129,"bnb":-99.67103,"brl":-53.8445,"btc":-0.32896,"cad":-62.45264,"chf":-63.4543,"clp":-54.7089,"cny":-61.26497,"czk":-61.55431,"dkk":-61.13905,"eos":-16.10009,"eth":-92.21431,"eur":-61.26899,"gbp":-62.27369,"hkd":-63.55334,"huf":-58.87639,"idr":-62.15657,"ils":-64.07374,"inr":-59.26992,"jpy":-64.90022,"krw":-60.60644,"kwd":-63.4152,"lkr":-57.39745,"ltc":-52.11769,"mmk":-59.65748,"mxn":-63.10475,"myr":-62.79525,"nok":-60.29271,"nzd":-60.32548,"php":-63.31142,"pkr":-48.99833,"pln":-60.4529,"rub":-60.55354,"sar":-63.62382,"sek":-58.87165,"sgd":-63.20581,"thb":-66.23726,"try":-49.0973,"twd":-62.92736,"uah":-68.14693,"usd":-63.62129,"vef":-48.53915,"vnd":-62.77453,"xag":-65.86833,"xau":-68.86202,"xdr":-62.60861,"xlm":-33.15539,"xrp":-27.01399,"zar":-59.19278},"ath_date":{"aed":"2017-12-16T00:00:00.000Z","ars":"2019-08-13T13:41:31.186Z","aud":"2017-12-16T00:00:00.000Z","bch":"2018-12-15T16:19:57.060Z","bdt":"2017-12-16T00:00:00.000Z","bhd":"2017-12-16T00:00:00.000Z","bmd":"2017-12-16T00:00:00.000Z","bnb":"2017-10-19T00:00:00.000Z","brl":"2017-12-16T00:00:00.000Z","btc":"2019-10-15T16:00:56.136Z","cad":"2017-12-16T00:00:00.000Z","chf":"2017-12-16T00:00:00.000Z","clp":"2017-12-16T00:00:00.000Z","cny":"2017-12-16T00:00:00.000Z","czk":"2017-12-16T00:00:00.000Z","dkk":"2017-12-16T00:00:00.000Z","eos":"2019-09-05T20:22:13.572Z","eth":"2015-10-20T00:00:00.000Z","eur":"2017-12-16T00:00:00.000Z","gbp":"2017-12-16T00:00:00.000Z","hkd":"2017-12-16T00:00:00.000Z","huf":"2017-12-16T00:00:00.000Z","idr":"2017-12-16T00:00:00.000Z","ils":"2017-12-16T00:00:00.000Z","inr":"2017-12-16T00:00:00.000Z","jpy":"2017-12-16T00:00:00.000Z","krw":"2017-12-16T00:00:00.000Z","kwd":"2017-12-16T00:00:00.000Z","lkr":"2017-12-16T00:00:00.000Z","ltc":"2017-03-05T00:00:00.000Z","mmk":"2017-12-16T00:00:00.000Z","mxn":"2017-12-16T00:00:00.000Z","myr":"2017-12-16T00:00:00.000Z","nok":"2017-12-16T00:00:00.000Z","nzd":"2017-12-16T00:00:00.000Z","php":"2017-12-16T00:00:00.000Z","pkr":"2019-06-26T19:55:29.614Z","pln":"2017-12-16T00:00:00.000Z","rub":"2017-12-16T00:00:00.000Z","sar":"2017-12-16T00:00:00.000Z","sek":"2017-12-16T00:00:00.000Z","sgd":"2017-12-16T00:00:00.000Z","thb":"2017-12-16T00:00:00.000Z","try":"2019-06-26T19:55:29.614Z","twd":"2017-12-16T00:00:00.000Z","uah":"2017-12-16T00:00:00.000Z","usd":"2017-12-16T00:00:00.000Z","vef":"2019-06-26T19:55:29.614Z","vnd":"2017-12-16T00:00:00.000Z","xag":"2017-12-16T00:00:00.000Z","xau":"2017-12-16T00:00:00.000Z","xdr":"2017-12-16T00:00:00.000Z","xlm":"2019-09-12T22:33:57.455Z","xrp":"2019-09-06T12:53:39.935Z","zar":"2017-12-16T00:00:00.000Z"},"market_cap":{"aed":474793759318,"ars":7730972325788,"aud":190537867813,"bch":630551829,"bdt":10967221735647,"bhd":48741175024,"bmd":129269449023,"bnb":8504056047,"brl":540242881359,"btc":18061500,"cad":171672542961,"chf":128668733894,"clp":102976043092026,"cny":909940578620,"czk":2986033783826,"dkk":874823757177,"eos":49831754917,"eth":878149440,"eur":117068610616,"gbp":100617263456,"hkd":1011622634528,"huf":39109243612989,"idr":1823601645999521,"ils":448552061166,"inr":9272819588645,"jpy":14042144114110,"krw":152458449136441,"kwd":39265078063,"lkr":23281924163795,"ltc":2759842022,"mmk":195884749415125,"mxn":2507103402139,"myr":539325068270,"nok":1182462552968,"nzd":201673138152,"php":6576324680166,"pkr":20101399323136,"pln":503125744460,"rub":8247196943518,"sar":484759011874,"sek":1243159730063,"sgd":176297674578,"thb":3901906020384,"try":738440868912,"twd":3950345221975,"uah":3116344627531,"usd":129269449023,"vef":32121860601613224,"vnd":3004844249179323,"xag":7557859850,"xau":88142374,"xdr":93962084412,"xlm":2283168044885,"xrp":555894381400,"zar":1899894292486},"market_cap_rank":1,"total_volume":{"aed":138687050476,"ars":2258211477718,"aud":55673594578,"bch":183737598,"bdt":3203651532313,"bhd":14237857483,"bmd":37761091954,"bnb":2480204906,"brl":157962199864,"btc":5303296,"cad":50166932300,"chf":37609632215,"clp":30072933632557,"cny":265804102377,"czk":872704148379,"dkk":255678541091,"eos":14590103457,"eth":256232321,"eur":34214645720,"gbp":29408225131,"hkd":295508865363,"huf":11431065850716,"idr":532884529661782,"ils":131094616522,"inr":2708689636557,"jpy":4104423009447,"krw":44530522909173,"kwd":11469780637,"lkr":6800917663597,"ltc":805626595,"mmk":57220186912138,"mxn":732187572998,"myr":157543051743,"nok":345640000147,"nzd":58946461701,"php":1922417191403,"pkr":5871849798923,"pln":147054530842,"rub":2408855577961,"sar":141607455567,"sek":363160165026,"sgd":51510094341,"thb":1140290574296,"try":215692490077,"twd":1154318782196,"uah":910320086696,"usd":37761091954,"vef":9383164708217112,"vnd":878179122153079,"xag":2214048370,"xau":25773833,"xdr":27447404909,"xlm":665333514482,"xrp":162538792944,"zar":555079555485},"high_24h":{"aed":28288,"ars":460171,"aud":11341.92,"bch":35.462898,"bdt":653121,"bhd":2902.95,"bmd":7701.17,"bnb":479.999,"brl":32313,"btc":1.0,"cad":10224.26,"chf":7651.19,"clp":6150290,"cny":54135,"czk":177709,"dkk":52023,"eos":2804,"eth":49.32148,"eur":6961.83,"gbp":5962.03,"hkd":60252,"huf":2329092,"idr":108526800,"ils":26652,"inr":552563,"jpy":836775,"krw":9070591,"kwd":2338.72,"lkr":1386522,"ltc":155.932,"mmk":11702598,"mxn":149346,"myr":32102,"nok":70413,"nzd":12023.03,"php":391391,"pkr":1198463,"pln":29923,"rub":490607,"sar":28878,"sek":74080,"sgd":10492.07,"thb":232716,"try":43892,"twd":235078,"uah":186500,"usd":7701.17,"vef":1913645346,"vnd":178999196,"xag":450.44,"xau":5.26,"xdr":5594.65,"xlm":128230,"xrp":31649,"zar":113045},"low_24h":{"aed":25389,"ars":413252,"aud":10177.28,"bch":33.498779,"bdt":586479,"bhd":2606.33,"bmd":6912.76,"bnb":441.91,"brl":28872,"btc":1.0,"cad":9172.15,"chf":6875.83,"clp":5495871,"cny":48647,"czk":159479,"dkk":46725,"eos":2688,"eth":47.221694,"eur":6252.27,"gbp":5375.94,"hkd":54096,"huf":2089289,"idr":97454723,"ils":23978,"inr":495700,"jpy":750674,"krw":8145203,"kwd":2099.06,"lkr":1245015,"ltc":149.451,"mmk":10475054,"mxn":133891,"myr":28843,"nok":63132,"nzd":10775.68,"php":351479,"pkr":1073206,"pln":26857,"rub":440210,"sar":25927,"sek":66395,"sgd":9421.43,"thb":208711,"try":39550,"twd":211136,"uah":166648,"usd":6912.76,"vef":1717735697,"vnd":160482399,"xag":404.21,"xau":4.71,"xdr":5021.89,"xlm":124993,"xrp":30492,"zar":101450},"price_change_24h":-480.5673621733,"price_change_percentage_24h":-6.30404,"price_change_percentage_7d":-17.44599,"price_change_percentage_14d":-22.54054,"price_change_percentage_30d":-11.22023,"price_change_percentage_60d":-28.86786,"price_change_percentage_200d":24.23364,"price_change_percentage_1y":54.82116,"market_cap_change_24h":-9006537915.032,"market_cap_change_percentage_24h":-6.51345,"price_change_24h_in_currency":{"aed":-1765.3849375,"ars":-28250.7846463,"aud":-687.42268421,"bch":1.209693,"bdt":-40527.63640467,"bhd":-180.62666736,"bmd":-480.56736217,"bnb":13.500179,"brl":-2234.43033708,"btc":0.0,"cad":-631.36102032,"chf":-447.60591608,"clp":-374343.553352,"cny":-3307.29242038,"czk":-10750.48677935,"dkk":-3087.31721729,"eos":64.38,"eth":1.189393,"eur":-412.89174814,"gbp":-335.73696182,"hkd":-3726.49580785,"huf":-136103.17232295,"idr":-6721091.02167486,"ils":-1585.24792676,"inr":-34656.74698489,"jpy":-51434.05221209,"krw":-545602.5158788,"kwd":-145.59687908,"lkr":-83492.89071827,"ltc":0.41120093,"mmk":-738928.15288522,"mxn":-9373.69014498,"myr":-1954.66222593,"nok":-4225.97032316,"nzd":-741.18047963,"php":-24202.68533704,"pkr":-74347.06674375,"pln":-1774.3736239,"rub":-30122.73186387,"sar":-1801.65200366,"sek":-4761.52882112,"sgd":-641.55584025,"thb":-14530.99082294,"try":-2654.49777029,"twd":-14416.02940028,"uah":-12075.69031495,"usd":-480.56736217,"vef":-119415050.76443768,"vnd":-10911575.70808423,"xag":-25.13762803,"xau":-0.3155855,"xdr":-347.48043979,"xlm":-1338.794227113,"xrp":-784.6183422333,"zar":-7137.11714324},"price_change_percentage_1h_in_currency":{"aed":0.84905,"ars":0.86161,"aud":0.8867,"bch":-1.38159,"bdt":0.85317,"bhd":0.85317,"bmd":0.85317,"bnb":-1.51553,"brl":0.91348,"btc":0.0,"cad":0.915,"chf":0.88072,"clp":0.78989,"cny":0.87467,"czk":0.97735,"dkk":0.94907,"eos":-0.63738,"eth":-1.42231,"eur":0.94977,"gbp":0.91433,"hkd":0.86091,"huf":0.9903,"idr":0.88666,"ils":0.8754,"inr":0.90239,"jpy":0.87893,"krw":0.96319,"kwd":0.86978,"lkr":0.85317,"ltc":-1.45742,"mmk":0.85317,"mxn":0.85889,"myr":0.84592,"nok":1.07686,"nzd":0.85963,"php":1.01808,"pkr":0.85317,"pln":0.98571,"rub":0.95446,"sar":0.85328,"sek":0.92645,"sgd":0.9031,"thb":0.87489,"try":0.72911,"twd":0.89938,"uah":0.85317,"usd":0.85317,"vef":0.85317,"vnd":0.95458,"xag":1.08837,"xau":1.10648,"xdr":0.85317,"xlm":-1.37016,"xrp":0.15375,"zar":1.04254},"price_change_percentage_24h_in_currency":{"aed":-6.30532,"ars":-6.20356,"aud":-6.12774,"bch":3.60621,"bdt":-6.26872,"bhd":-6.2854,"bmd":-6.30404,"bnb":2.96293,"brl":-6.95795,"btc":0.0,"cad":-6.23841,"chf":-5.9195,"clp":-6.17453,"cny":-6.17208,"czk":-6.11433,"dkk":-6.00068,"eos":2.38856,"eth":2.51577,"eur":-5.99726,"gbp":-5.69203,"hkd":-6.25013,"huf":-5.92187,"idr":-6.25117,"ils":-6.0088,"inr":-6.33565,"jpy":-6.21338,"krw":-6.08344,"kwd":-6.28893,"lkr":-6.09481,"ltc":0.27057,"mmk":-6.39087,"mxn":-6.3392,"myr":-6.15559,"nok":-6.0714,"nzd":-6.2331,"php":-6.24051,"pkr":-6.27391,"pln":-5.99652,"rub":-6.20112,"sar":-6.30234,"sek":-6.48232,"sgd":-6.17783,"thb":-6.3118,"try":-6.10886,"twd":-6.19357,"uah":-6.55345,"usd":-6.30404,"vef":-6.30404,"vnd":-6.164,"xag":-5.66252,"xau":-6.07975,"xdr":-6.2731,"xlm":-1.05261,"xrp":-2.48855,"zar":-6.36494},"price_change_percentage_7d_in_currency":{"aed":-17.4561,"ars":-17.17189,"aud":-17.39343,"bch":11.54617,"bdt":-17.34556,"bhd":-17.42365,"bmd":-17.44599,"bnb":15.37505,"brl":-17.6939,"btc":0.0,"cad":-17.22355,"chf":-16.83939,"clp":-18.16528,"cny":-17.23081,"czk":-17.84696,"dkk":-17.5639,"eos":8.28045,"eth":3.50175,"eur":-17.56998,"gbp":-17.18365,"hkd":-17.46919,"huf":-17.62502,"idr":-17.26247,"ils":-17.71536,"inr":-17.71385,"jpy":-17.2919,"krw":-16.68652,"kwd":-17.43375,"lkr":-17.57364,"ltc":3.79137,"mmk":-17.47497,"mxn":-17.18117,"myr":-17.25559,"nok":-17.57382,"nzd":-17.76103,"php":-17.19551,"pkr":-17.66361,"pln":-17.28903,"rub":-17.622,"sar":-17.43643,"sek":-17.99201,"sgd":-17.30006,"thb":-17.44962,"try":-17.99541,"twd":-17.11248,"uah":-17.8811,"usd":-17.44599,"vef":-17.44599,"vnd":-17.01601,"xag":-17.68437,"xau":-17.19364,"xdr":-17.64438,"xlm":7.48208,"xrp":-4.57226,"zar":-18.1527},"price_change_percentage_14d_in_currency":{"aed":-22.55003,"ars":-22.26953,"aud":-21.20934,"bch":10.18823,"bdt":-22.4719,"bhd":-22.52698,"bmd":-22.54054,"bnb":3.78256,"brl":-20.98795,"btc":0.0,"cad":-21.89472,"chf":-22.44172,"clp":-16.95103,"cny":-21.8595,"czk":-22.52847,"dkk":-22.42537,"eos":3.89877,"eth":-1.80404,"eur":-22.42496,"gbp":-22.65386,"hkd":-22.54945,"huf":-22.08321,"idr":-21.79502,"ils":-23.09687,"inr":-21.7934,"jpy":-22.93533,"krw":-20.89782,"kwd":-22.52728,"lkr":-22.87826,"ltc":1.53118,"mmk":-22.72098,"mxn":-21.51393,"myr":-21.70354,"nok":-22.15384,"nzd":-22.92904,"php":-21.95551,"pkr":-22.80961,"pln":-21.72901,"rub":-22.2441,"sar":-22.54442,"sek":-22.62979,"sgd":-22.15219,"thb":-23.10963,"try":-23.08949,"twd":-21.78058,"uah":-23.91091,"usd":-22.54054,"vef":-22.54054,"vnd":-22.53941,"xag":-22.26463,"xau":-22.33687,"xdr":-22.55567,"xlm":1.69618,"xrp":-2.72537,"zar":-22.70147},"price_change_percentage_30d_in_currency":{"aed":-11.22443,"ars":-9.51444,"aud":-10.193,"bch":-1.85067,"bdt":-11.07576,"bhd":-11.21198,"bmd":-11.22023,"bnb":6.12033,"brl":-8.97239,"btc":0.0,"cad":-9.91713,"chf":-10.62881,"clp":-2.46349,"cny":-11.69817,"czk":-10.69528,"dkk":-10.45514,"eos":-0.60245,"eth":3.31477,"eur":-10.48102,"gbp":-10.92388,"hkd":-11.40589,"huf":-9.11838,"idr":-11.12997,"ils":-12.76593,"inr":-10.10704,"jpy":-11.04788,"krw":-10.68933,"kwd":-11.09524,"lkr":-11.91065,"ltc":1.16327,"mmk":-12.33699,"mxn":-10.07551,"myr":-11.44098,"nok":-11.18008,"nzd":-11.13426,"php":-11.70222,"pkr":-11.68762,"pln":-9.93606,"rub":-11.10484,"sar":-11.23433,"sek":-11.4895,"sgd":-11.12778,"thb":-11.50173,"try":-12.60444,"twd":-10.99896,"uah":-14.02182,"usd":-11.22023,"vef":-11.22023,"vnd":-11.12455,"xag":-8.57976,"xau":-9.77676,"xdr":-11.09094,"xlm":-2.0601,"xrp":11.18539,"zar":-10.5719},"price_change_percentage_60d_in_currency":{"aed":-28.87271,"ars":-24.94049,"aud":-28.98967,"bch":6.58558,"bdt":-28.55284,"bhd":-28.85919,"bmd":-28.86786,"bnb":-4.73332,"brl":-28.29542,"btc":0.0,"cad":-28.7742,"chf":-28.5492,"clp":-21.05412,"cny":-29.39446,"czk":-29.96813,"dkk":-28.93541,"eos":4.89171,"eth":2.15282,"eur":-28.98347,"gbp":-30.91919,"hkd":-29.00032,"huf":-28.93845,"idr":-28.57896,"ils":-29.88395,"inr":-28.33674,"jpy":-28.19392,"krw":-29.75392,"kwd":-28.86693,"lkr":-29.26237,"ltc":10.0907,"mmk":-29.68127,"mxn":-29.02416,"myr":-28.91758,"nok":-28.16281,"nzd":-30.41575,"php":-30.43231,"pkr":-29.27026,"pln":-30.19557,"rub":-29.14377,"sar":-28.88798,"sek":-29.4752,"sgd":-29.51848,"thb":-29.55026,"try":-29.23667,"twd":-29.86135,"uah":-29.77018,"usd":-28.86786,"vef":-28.86786,"vnd":-28.86503,"xag":-24.99432,"xau":-26.49988,"xdr":-29.12285,"xlm":-13.83671,"xrp":-14.54016,"zar":-29.92383},"price_change_percentage_200d_in_currency":{"aed":24.22488,"ars":66.85932,"aud":27.95174,"bch":76.94309,"bdt":24.91123,"bhd":24.2366,"bmd":24.23364,"bnb":86.35415,"brl":31.96581,"btc":0.0,"cad":22.59678,"chf":21.70062,"clp":46.20803,"cny":29.85077,"czk":25.05176,"dkk":26.08954,"eos":133.30477,"eth":36.87714,"eur":25.97742,"gbp":27.31032,"hkd":23.93812,"huf":30.22984,"idr":23.22097,"ils":20.37488,"inr":28.82094,"jpy":21.90092,"krw":25.82773,"kwd":24.06617,"lkr":26.37467,"ltc":100.2474,"mmk":23.76562,"mxn":26.42772,"myr":25.30265,"nok":30.11168,"nzd":28.35377,"php":22.01045,"pkr":36.51723,"pln":26.39489,"rub":21.26104,"sar":24.22499,"sek":24.9225,"sgd":24.29049,"thb":17.38252,"try":18.62205,"twd":22.88347,"uah":13.11625,"usd":24.23364,"vef":24.23364,"vnd":24.69572,"xag":8.68024,"xau":8.77097,"xdr":25.56291,"xlm":115.80188,"xrp":60.72697,"zar":26.48666},"price_change_percentage_1y_in_currency":{"aed":54.80299,"ars":155.34218,"aud":65.7028,"bch":78.89456,"bdt":57.19653,"bhd":54.81048,"bmd":54.82116,"bnb":-36.65258,"brl":70.52838,"btc":0.0,"cad":55.45654,"chf":55.10131,"clp":84.99866,"cny":57.31982,"czk":56.67972,"dkk":59.98455,"eos":129.05448,"eth":44.12795,"eur":59.73458,"gbp":54.03378,"hkd":54.68674,"huf":65.94232,"idr":49.69388,"ils":44.12632,"inr":56.02681,"jpy":48.86278,"krw":60.38647,"kwd":54.63991,"lkr":56.01994,"ltc":15.13364,"mmk":46.86442,"mxn":48.17348,"myr":54.61929,"nok":66.2395,"nzd":64.92835,"php":51.02989,"pkr":79.1998,"pln":59.54312,"rub":50.32841,"sar":54.6784,"sek":64.78944,"sgd":53.74122,"thb":42.06052,"try":66.34431,"twd":53.34085,"uah":34.53434,"usd":54.82116,"vef":54.80868,"vnd":54.8204,"xag":31.52974,"xau":29.51256,"xdr":56.55346,"xlm":458.8577,"xrp":200.09987,"zar":63.33739},"market_cap_change_24h_in_currency":{"aed":-33066285509.427795,"ars":-530278117251.8633,"aud":-12960274920.457092,"bch":24024713,"bdt":-759694248769.8789,"bhd":-3385553428.380455,"bmd":-9006537915.03212,"bnb":255092140,"brl":-42175575626.08362,"btc":1663,"cad":-12057111575.367401,"chf":-8491780658.011612,"clp":-7119297708339.391,"cny":-62028988767.01172,"czk":-203550613409.21533,"dkk":-58915127066.18848,"eos":1113960094,"eth":23139205,"eur":-7879783597.133743,"gbp":-6416093889.904633,"hkd":-69937566307.74951,"huf":-2567138850248.992,"idr":-127617002526785.75,"ils":-30288002282.410645,"inr":-646961300054.1992,"jpy":-965736921441.2344,"krw":-10313128196732.844,"kwd":-2728924342.179413,"lkr":-1566622601424.3633,"ltc":12247122,"mmk":-13842142728744.844,"mxn":-177409423821.13818,"myr":-36663555321.51227,"nok":-81603974747.83325,"nzd":-14103220841.779144,"php":-455362689074.46094,"pkr":-1393602846440.6172,"pln":-33879559789.90509,"rub":-570801019531.376,"sar":-33765015342.341797,"sek":-90223858525.68628,"sgd":-12077223463.969208,"thb":-272646025286.54932,"try":-49865416412.02966,"twd":-271842901196.03174,"uah":-226020631120.77588,"usd":-9006537915.03212,"vef":-2.238013371260464e+15,"vnd":-207810852801656.0,"xag":-501296089.4158993,"xau":-5993152.57134043,"xdr":-6570642236.026748,"xlm":-26680177257.057617,"xrp":-14067201416.741577,"zar":-135321097818.74121},"market_cap_change_percentage_24h_in_currency":{"aed":-6.51091,"ars":-6.41886,"aud":-6.36874,"bch":3.96103,"bdt":-6.47821,"bhd":-6.49485,"bmd":-6.51345,"bnb":3.09241,"brl":-7.24146,"btc":0.00921,"cad":-6.56242,"chf":-6.19113,"clp":-6.46648,"cny":-6.38178,"czk":-6.38173,"dkk":-6.30959,"eos":2.28656,"eth":2.70631,"eur":-6.30643,"gbp":-5.99448,"hkd":-6.46636,"huf":-6.1597,"idr":-6.54037,"ils":-6.32529,"inr":-6.52193,"jpy":-6.43487,"krw":-6.33595,"kwd":-6.49837,"lkr":-6.30469,"ltc":0.44574,"mmk":-6.60008,"mxn":-6.60863,"myr":-6.36533,"nok":-6.45567,"nzd":-6.53604,"php":-6.47587,"pkr":-6.48338,"pln":-6.30898,"rub":-6.47314,"sar":-6.51176,"sek":-6.76653,"sgd":-6.41127,"thb":-6.53114,"try":-6.32564,"twd":-6.43844,"uah":-6.7623,"usd":-6.51345,"vef":-6.51345,"vnd":-6.46851,"xag":-6.22021,"xau":-6.36652,"xdr":-6.53582,"xlm":-1.15506,"xrp":-2.4681,"zar":-6.64898},"total_supply":21000000.0,"circulating_supply":18061500.0,"last_updated":"2019-11-22T15:50:18.919Z"},"public_interest_stats":{"alexa_rank":12740,"bing_matches":135000000},"status_updates":[],"last_updated":"2019-11-22T15:50:18.919Z"} diff --git a/server/public.go b/server/public.go index ba73c09b..d6838238 100644 --- a/server/public.go +++ b/server/public.go @@ -186,6 +186,8 @@ func (s *PublicServer) ConnectFullPublicInterface() { serveMux.HandleFunc(path+"api/v2/sendtx/", s.jsonHandler(s.apiSendTx, apiV2)) serveMux.HandleFunc(path+"api/v2/estimatefee/", s.jsonHandler(s.apiEstimateFee, apiV2)) serveMux.HandleFunc(path+"api/v2/feestats/", s.jsonHandler(s.apiFeeStats, apiV2)) + serveMux.HandleFunc(path+"api/v2/tickers/", s.jsonHandler(s.apiTickers, apiV2)) + serveMux.HandleFunc(path+"api/v2/tickers-list/", s.jsonHandler(s.apiTickersList, apiV2)) // socket.io interface serveMux.Handle(path+"socket.io/", s.socketio.GetHandler()) // websocket interface @@ -210,6 +212,11 @@ func (s *PublicServer) OnNewBlock(hash string, height uint32) { s.websocket.OnNewBlock(hash, height) } +// OnNewFiatRatesTicker notifies users subscribed to bitcoind/fiatrates about new ticker +func (s *PublicServer) OnNewFiatRatesTicker(ticker *db.CurrencyRatesTicker) { + s.websocket.OnNewFiatRatesTicker(ticker) +} + // OnNewTxAddr notifies users subscribed to bitcoind/addresstxid about new block func (s *PublicServer) OnNewTxAddr(tx *bchain.Tx, desc bchain.AddressDescriptor) { s.socketio.OnNewTxAddr(tx.Txid, desc) @@ -1088,6 +1095,43 @@ func (s *PublicServer) apiSendTx(r *http.Request, apiVersion int) (interface{}, return nil, api.NewAPIError("Missing tx blob", true) } +// apiTickersList returns a list of available FiatRates currencies +func (s *PublicServer) apiTickersList(r *http.Request, apiVersion int) (interface{}, error) { + s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-list"}).Inc() + date := strings.ToLower(r.URL.Query().Get("date")) + result, err := s.api.GetFiatRatesTickersList(date) + return result, err +} + +// apiTickers returns FiatRates ticker prices for the specified block or date. +func (s *PublicServer) apiTickers(r *http.Request, apiVersion int) (interface{}, error) { + var result *db.ResultTickerAsString + var err error + currency := strings.ToLower(r.URL.Query().Get("currency")) + + if block := r.URL.Query().Get("block"); block != "" { + // Get tickers for specified block height or block hash + s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-block"}).Inc() + result, err = s.api.GetFiatRatesForBlockID(block, currency) + } else if date := r.URL.Query().Get("date"); date != "" { + // Get tickers for specified date + s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-date"}).Inc() + resultTickers, err := s.api.GetFiatRatesForDates([]string{date}, currency) + if err != nil { + return nil, err + } + result = &resultTickers.Tickers[0] + } else { + // No parameters - get the latest available ticker + s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-last"}).Inc() + result, err = s.api.GetCurrentFiatRates(currency) + } + if err != nil { + return nil, err + } + return result, nil +} + type resultEstimateFeeAsString struct { Result string `json:"result"` } diff --git a/server/public_test.go b/server/public_test.go index 60afb45c..d3ae15d8 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -58,6 +58,9 @@ func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *c if err := d.ConnectBlock(block2); err != nil { t.Fatal(err) } + if err := InitTestFiatRates(d); err != nil { + t.Fatal(err) + } is.FinishedSync(block2.Height) return d, is, tmp } @@ -146,6 +149,41 @@ func newPostRequest(u string, body string) *http.Request { return r } +// InitTestFiatRates initializes test data for /api/v2/tickers endpoint +func InitTestFiatRates(d *db.RocksDB) error { + convertedDate, err := db.FiatRatesConvertDate("20191121140000") + if err != nil { + return err + } + ticker := &db.CurrencyRatesTicker{ + Timestamp: convertedDate, + Rates: map[string]json.Number{ + "usd": "7814.5", + "eur": "7100.0", + }, + } + err = d.FiatRatesStoreTicker(ticker) + if err != nil { + return err + } + convertedDate, err = db.FiatRatesConvertDate("20191121143015") + if err != nil { + return err + } + ticker = &db.CurrencyRatesTicker{ + Timestamp: convertedDate, + Rates: map[string]json.Number{ + "usd": "7914.5", + "eur": "7134.1", + }, + } + err = d.FiatRatesStoreTicker(ticker) + if err != nil { + return err + } + return nil +} + func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { tests := []struct { name string @@ -460,6 +498,105 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { `{"txCount":3,"totalFeesSat":"1284","averageFeePerKb":1398,"decilesFeePerKb":[155,155,155,155,1679,1679,1679,2361,2361,2361,2361]}`, }, }, + { + name: "apiFiatRates missing currency", + r: newGetRequest(ts.URL + "/api/v2/tickers"), + status: http.StatusBadRequest, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"error":"Missing or empty \"currency\" parameter"}`, + }, + }, + { + name: "apiFiatRates get last rate", + r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"data_timestamp":"20191121143015","rates":{"usd":7914.5}}`, + }, + }, + { + name: "apiFiatRates get rate by exact date", + r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&date=20191121140000"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"data_timestamp":"20191121140000","rates":{"usd":7814.5}}`, + }, + }, + { + name: "apiFiatRates incorrect date", + r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&date=yesterday"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"error":"Date \"yesterday\" does not match any of available formats. Possible formats are: YYYYMMDDhhmmss, YYYYMMDDhhmm, YYYYMMDDhh, YYYYMMDD"}`, + }, + }, + { + name: "apiFiatRates future date", + r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&date=20200101000000"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"error":"No tickers available for 2020-01-01 00:00:00 +0000 UTC (usd)"}`, + }, + }, + { + name: "apiFiatRates get EUR rate (exact date)", + r: newGetRequest(ts.URL + "/api/v2/tickers?date=20191121140000¤cy=eur"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"data_timestamp":"20191121140000","rates":{"eur":7100.0,"usd":7814.5}}`, + }, + }, + { + name: "apiFiatRates get closest rate", + r: newGetRequest(ts.URL + "/api/v2/tickers?date=20191121130000¤cy=usd"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"data_timestamp":"20191121140000","rates":{"usd":7814.5}}`, + }, + }, + { + name: "apiFiatRates get rate by block height", + r: newGetRequest(ts.URL + "/api/v2/tickers?block=225494¤cy=usd"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"data_timestamp":"20191121140000","rates":{"usd":7814.5}}`, + }, + }, + { + name: "apiFiatRates get rate for EUR", + r: newGetRequest(ts.URL + "/api/v2/tickers?date=20191121140000¤cy=eur"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"data_timestamp":"20191121140000","rates":{"eur":7100.0,"usd":7814.5}}`, + }, + }, + { + name: "apiFiatRates get exact rate for an incorrect currency", + r: newGetRequest(ts.URL + "/api/v2/tickers?date=20191121140000¤cy=does_not_exist"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"error":"Currency \"does_not_exist\" is not available for timestamp 20191121140000. Available currencies are: eur, usd."}`, + }, + }, + { + name: "apiTickerList", + r: newGetRequest(ts.URL + "/api/v2/tickers-list?date=20191121140000"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"data_timestamp":"20191121140000","available_currencies":["eur","usd"]}`, + }, + }, { name: "apiAddress v1", r: newGetRequest(ts.URL + "/api/v1/address/mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"), @@ -965,6 +1102,154 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { }, want: `{"id":"16","data":{}}`, }, + { + name: "websocket getCurrentFiatRates no currency", + req: websocketReq{ + Method: "getCurrentFiatRates", + Params: map[string]interface{}{ + "": "", + }, + }, + want: `{"id":"17","data":{"error":{"message":"Missing or empty \"currency\" parameter"}}}`, + }, + { + name: "websocket getCurrentFiatRates usd", + req: websocketReq{ + Method: "getCurrentFiatRates", + Params: map[string]interface{}{ + "currency": "usd", + }, + }, + want: `{"id":"18","data":{"data_timestamp":"20191121143015","rates":{"usd":7914.5}}}`, + }, + { + name: "websocket getCurrentFiatRates eur", + req: websocketReq{ + Method: "getCurrentFiatRates", + Params: map[string]interface{}{ + "currency": "eur", + }, + }, + want: `{"id":"19","data":{"data_timestamp":"20191121143015","rates":{"eur":7134.1,"usd":7914.5}}}`, + }, + { + name: "websocket getCurrentFiatRates incorrect currency", + req: websocketReq{ + Method: "getCurrentFiatRates", + Params: map[string]interface{}{ + "currency": "does-not-exist", + }, + }, + want: `{"id":"20","data":{"error":{"message":"Currency \"does-not-exist\" is not available for timestamp 20191121143015. Available currencies are: eur, usd."}}}`, + }, + { + name: "websocket getFiatRatesForDates missing date", + req: websocketReq{ + Method: "getFiatRatesForDates", + Params: map[string]interface{}{ + "currency": "usd", + }, + }, + want: `{"id":"21","data":{"error":{"message":"No dates provided"}}}`, + }, + { + name: "websocket getFiatRatesForDates incorrect date", + req: websocketReq{ + Method: "getFiatRatesForDates", + Params: map[string]interface{}{ + "currency": "usd", + "dates": []string{"yesterday"}, + }, + }, + want: `{"id":"22","data":{"tickers":[{"error":"Date \"yesterday\" does not match any of available formats. Possible formats are: YYYYMMDDhhmmss, YYYYMMDDhhmm, YYYYMMDDhh, YYYYMMDD"}]}}`, + }, + { + name: "websocket getFiatRatesForDates incorrect (future) date", + req: websocketReq{ + Method: "getFiatRatesForDates", + Params: map[string]interface{}{ + "currency": "usd", + "dates": []string{"20200101000000"}, + }, + }, + want: `{"id":"23","data":{"tickers":[{"error":"No tickers available for 2020-01-01 00:00:00 +0000 UTC (usd)"}]}}`, + }, + { + name: "websocket getFiatRatesForDates exact date", + req: websocketReq{ + Method: "getFiatRatesForDates", + Params: map[string]interface{}{ + "currency": "usd", + "dates": []string{"20191121140000"}, + }, + }, + want: `{"id":"24","data":{"tickers":[{"data_timestamp":"20191121140000","rates":{"usd":7814.5}}]}}`, + }, + { + name: "websocket getFiatRatesForDates closest date, eur", + req: websocketReq{ + Method: "getFiatRatesForDates", + Params: map[string]interface{}{ + "currency": "eur", + "dates": []string{"20191121130000"}, + }, + }, + want: `{"id":"25","data":{"tickers":[{"data_timestamp":"20191121140000","rates":{"eur":7100.0,"usd":7814.5}}]}}`, + }, + { + name: "websocket getFiatRatesForDates multiple dates usd", + req: websocketReq{ + Method: "getFiatRatesForDates", + Params: map[string]interface{}{ + "currency": "usd", + "dates": []string{"20191121140000", "20191121143015"}, + }, + }, + want: `{"id":"26","data":{"tickers":[{"data_timestamp":"20191121140000","rates":{"usd":7814.5}},{"data_timestamp":"20191121143015","rates":{"usd":7914.5}}]}}`, + }, + { + name: "websocket getFiatRatesForDates multiple dates eur", + req: websocketReq{ + Method: "getFiatRatesForDates", + Params: map[string]interface{}{ + "currency": "eur", + "dates": []string{"20191121140000", "20191121143015"}, + }, + }, + want: `{"id":"27","data":{"tickers":[{"data_timestamp":"20191121140000","rates":{"eur":7100.0,"usd":7814.5}},{"data_timestamp":"20191121143015","rates":{"eur":7134.1,"usd":7914.5}}]}}`, + }, + { + name: "websocket getFiatRatesForDates multiple dates with an error", + req: websocketReq{ + Method: "getFiatRatesForDates", + Params: map[string]interface{}{ + "currency": "usd", + "dates": []string{"20191121140000", "20191121143015", "not-a-real-date"}, + }, + }, + want: `{"id":"28","data":{"tickers":[{"data_timestamp":"20191121140000","rates":{"usd":7814.5}},{"data_timestamp":"20191121143015","rates":{"usd":7914.5}},{"error":"Date \"not-a-real-date\" does not match any of available formats. Possible formats are: YYYYMMDDhhmmss, YYYYMMDDhhmm, YYYYMMDDhh, YYYYMMDD"}]}}`, + }, + { + name: "websocket getFiatRatesForDates multiple errors", + req: websocketReq{ + Method: "getFiatRatesForDates", + Params: map[string]interface{}{ + "currency": "usd", + "dates": []string{"20200101000000", "not-a-real-date"}, + }, + }, + want: `{"id":"29","data":{"tickers":[{"error":"No tickers available for 2020-01-01 00:00:00 +0000 UTC (usd)"},{"error":"Date \"not-a-real-date\" does not match any of available formats. Possible formats are: YYYYMMDDhhmmss, YYYYMMDDhhmm, YYYYMMDDhh, YYYYMMDD"}]}}`, + }, + { + name: "websocket getTickersList", + req: websocketReq{ + Method: "getFiatRatesTickersList", + Params: map[string]interface{}{ + "date": "20191121140000", + }, + }, + want: `{"id":"30","data":{"data_timestamp":"20191121140000","available_currencies":["eur","usd"]}}`, + }, } // send all requests at once diff --git a/server/websocket.go b/server/websocket.go index 1e4ea3e6..fa47835b 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -53,21 +53,23 @@ type websocketChannel struct { // WebsocketServer is a handle to websocket server type WebsocketServer struct { - socket *websocket.Conn - upgrader *websocket.Upgrader - db *db.RocksDB - txCache *db.TxCache - chain bchain.BlockChain - chainParser bchain.BlockChainParser - mempool bchain.Mempool - metrics *common.Metrics - is *common.InternalState - api *api.Worker - block0hash string - newBlockSubscriptions map[*websocketChannel]string - newBlockSubscriptionsLock sync.Mutex - addressSubscriptions map[string]map[*websocketChannel]string - addressSubscriptionsLock sync.Mutex + socket *websocket.Conn + upgrader *websocket.Upgrader + db *db.RocksDB + txCache *db.TxCache + chain bchain.BlockChain + chainParser bchain.BlockChainParser + mempool bchain.Mempool + metrics *common.Metrics + is *common.InternalState + api *api.Worker + block0hash string + newBlockSubscriptions map[*websocketChannel]string + newBlockSubscriptionsLock sync.Mutex + addressSubscriptions map[string]map[*websocketChannel]string + addressSubscriptionsLock sync.Mutex + fiatRatesSubscriptions map[string]map[*websocketChannel]string + fiatRatesSubscriptionsLock sync.Mutex } // NewWebsocketServer creates new websocket interface to blockbook and returns its handle @@ -86,17 +88,18 @@ func NewWebsocketServer(db *db.RocksDB, chain bchain.BlockChain, mempool bchain. WriteBufferSize: 1024 * 32, CheckOrigin: checkOrigin, }, - db: db, - txCache: txCache, - chain: chain, - chainParser: chain.GetChainParser(), - mempool: mempool, - metrics: metrics, - is: is, - api: api, - block0hash: b0, - newBlockSubscriptions: make(map[*websocketChannel]string), - addressSubscriptions: make(map[string]map[*websocketChannel]string), + db: db, + txCache: txCache, + chain: chain, + chainParser: chain.GetChainParser(), + mempool: mempool, + metrics: metrics, + is: is, + api: api, + block0hash: b0, + newBlockSubscriptions: make(map[*websocketChannel]string), + addressSubscriptions: make(map[string]map[*websocketChannel]string), + fiatRatesSubscriptions: make(map[string]map[*websocketChannel]string), } return s, nil } @@ -214,6 +217,7 @@ func (s *WebsocketServer) onConnect(c *websocketChannel) { func (s *WebsocketServer) onDisconnect(c *websocketChannel) { s.unsubscribeNewBlock(c) s.unsubscribeAddresses(c) + s.unsubscribeFiatRates(c) glog.Info("Client disconnected ", c.id, ", ", c.ip) s.metrics.WebsocketClients.Dec() } @@ -298,10 +302,54 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs "unsubscribeAddresses": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { return s.unsubscribeAddresses(c) }, + "subscribeFiatRates": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { + r := struct { + Currency string `json:"currency"` + }{} + err = json.Unmarshal(req.Params, &r) + if err != nil { + return nil, err + } + return s.subscribeFiatRates(c, r.Currency, req) + }, + "unsubscribeFiatRates": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { + return s.unsubscribeFiatRates(c) + }, "ping": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { r := struct{}{} return r, nil }, + "getCurrentFiatRates": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { + r := struct { + Currency string `json:"currency"` + }{} + err = json.Unmarshal(req.Params, &r) + if err == nil { + rv, err = s.getCurrentFiatRates(r.Currency) + } + return + }, + "getFiatRatesForDates": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { + r := struct { + Dates []string `json:"dates"` + Currency string `json:"currency"` + }{} + err = json.Unmarshal(req.Params, &r) + if err == nil { + rv, err = s.getFiatRatesForDates(r.Dates, r.Currency) + } + return + }, + "getFiatRatesTickersList": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { + r := struct { + Date string `json:"date"` + }{} + err = json.Unmarshal(req.Params, &r) + if err == nil { + rv, err = s.getFiatRatesTickersList(r.Date) + } + return + }, } func sendResponse(c *websocketChannel, req *websocketReq, data interface{}) { @@ -613,6 +661,36 @@ func (s *WebsocketServer) unsubscribeAddresses(c *websocketChannel) (res interfa return &subscriptionResponse{false}, nil } +// subscribeFiatRates subscribes all FiatRates subscriptions by this channel +func (s *WebsocketServer) subscribeFiatRates(c *websocketChannel, currency string, req *websocketReq) (res interface{}, err error) { + // unsubscribe all previous subscriptions + s.unsubscribeFiatRates(c) + s.fiatRatesSubscriptionsLock.Lock() + defer s.fiatRatesSubscriptionsLock.Unlock() + + as, ok := s.fiatRatesSubscriptions[currency] + if !ok { + as = make(map[*websocketChannel]string) + s.fiatRatesSubscriptions[currency] = as + } + as[c] = req.ID + return &subscriptionResponse{true}, nil +} + +// unsubscribeFiatRates unsubscribes all FiatRates subscriptions by this channel +func (s *WebsocketServer) unsubscribeFiatRates(c *websocketChannel) (res interface{}, err error) { + s.fiatRatesSubscriptionsLock.Lock() + defer s.fiatRatesSubscriptionsLock.Unlock() + for _, sa := range s.fiatRatesSubscriptions { + for sc := range sa { + if sc == c { + delete(sa, c) + } + } + } + return &subscriptionResponse{false}, nil +} + // OnNewBlock is a callback that broadcasts info about new block to subscribed clients func (s *WebsocketServer) OnNewBlock(hash string, height uint32) { s.newBlockSubscriptionsLock.Lock() @@ -640,8 +718,9 @@ func (s *WebsocketServer) OnNewTxAddr(tx *bchain.Tx, addrDesc bchain.AddressDesc // check if there is any subscription but release the lock immediately, GetTransactionFromBchainTx may take some time s.addressSubscriptionsLock.Lock() as, ok := s.addressSubscriptions[string(addrDesc)] + lenAs := len(as) s.addressSubscriptionsLock.Unlock() - if ok && len(as) > 0 { + if ok && lenAs > 0 { addr, _, err := s.chainParser.GetAddressesFromAddrDesc(addrDesc) if err != nil { glog.Error("GetAddressesFromAddrDesc error ", err, " for ", addrDesc) @@ -678,3 +757,51 @@ func (s *WebsocketServer) OnNewTxAddr(tx *bchain.Tx, addrDesc bchain.AddressDesc } } } + +func (s *WebsocketServer) broadcastTicker(coin string, rate json.Number) { + s.fiatRatesSubscriptionsLock.Lock() + defer s.fiatRatesSubscriptionsLock.Unlock() + as, ok := s.fiatRatesSubscriptions[coin] + if ok && len(as) > 0 { + data := struct { + Rate interface{} `json:"rate"` + }{ + Rate: rate, + } + // get the list of subscriptions again, this time keep the lock + as, ok = s.fiatRatesSubscriptions[coin] + if ok { + for c, id := range as { + if c.IsAlive() { + c.out <- &websocketRes{ + ID: id, + Data: &data, + } + } + } + glog.Info("broadcasting new rate ", rate, " for coin ", coin, " to ", len(as), " channels") + } + } +} + +// OnNewFiatRatesTicker is a callback that broadcasts info about fiat rates affecting subscribed currency +func (s *WebsocketServer) OnNewFiatRatesTicker(ticker *db.CurrencyRatesTicker) { + for currency, rate := range ticker.Rates { + s.broadcastTicker(currency, rate) + } +} + +func (s *WebsocketServer) getCurrentFiatRates(currency string) (interface{}, error) { + ret, err := s.api.GetCurrentFiatRates(currency) + return ret, err +} + +func (s *WebsocketServer) getFiatRatesForDates(dates []string, currency string) (interface{}, error) { + ret, err := s.api.GetFiatRatesForDates(dates, currency) + return ret, err +} + +func (s *WebsocketServer) getFiatRatesTickersList(date string) (interface{}, error) { + ret, err := s.api.GetFiatRatesTickersList(date) + return ret, err +} diff --git a/static/test-websocket.html b/static/test-websocket.html index 523ae86c..ae9c2504 100644 --- a/static/test-websocket.html +++ b/static/test-websocket.html @@ -1,6 +1,5 @@ - @@ -279,6 +278,70 @@ }); } + function getFiatRatesForDates() { + const method = 'getFiatRatesForDates'; + var dates = document.getElementById('getFiatRatesForDatesList').value.split(","); + var currency = document.getElementById('getFiatRatesForDatesCurrency').value; + dates = dates.map(s => s.trim()); + const params = { + dates, + "currency": currency + }; + send(method, params, function (result) { + document.getElementById('getFiatRatesForDatesResult').innerText = JSON.stringify(result).replace(/,/g, ", "); + }); + } + + function getCurrentFiatRates() { + const method = 'getCurrentFiatRates'; + var currency = document.getElementById('getCurrentFiatRatesCurrency').value; + const params = { + "currency": currency + }; + send(method, params, function (result) { + document.getElementById('getCurrentFiatRatesResult').innerText = JSON.stringify(result).replace(/,/g, ", "); + }); + } + + function getFiatRatesTickersList() { + const method = 'getFiatRatesTickersList'; + var date = document.getElementById('getFiatRatesTickersListDate').value; + const params = { + date, + }; + send(method, params, function (result) { + document.getElementById('getFiatRatesTickersListResult').innerText = JSON.stringify(result).replace(/,/g, ", "); + }); + } + + function subscribeNewFiatRatesTicker() { + const method = 'subscribeFiatRates'; + var currency = document.getElementById('subscribeFiatRatesCurrency').value; + const params = { + "currency": currency + }; + if (subscribeNewFiatRatesTickerId) { + delete subscriptions[subscribeNewFiatRatesTickerId]; + subscribeNewFiatRatesTickerId = ""; + } + subscribeNewFiatRatesTickerId = subscribe(method, params, function (result) { + document.getElementById('subscribeNewFiatRatesTickerResult').innerText += JSON.stringify(result).replace(/,/g, ", ") + "\n"; + }); + document.getElementById('subscribeNewFiatRatesTickerId').innerText = subscribeNewFiatRatesTickerId; + document.getElementById('unsubscribeNewFiatRatesTickerButton').setAttribute("style", "display: inherit;"); + } + + function unsubscribeNewFiatRatesTicker() { + const method = 'unsubscribeFiatRates'; + const params = { + }; + unsubscribe(method, subscribeNewFiatRatesTickerId, params, function (result) { + subscribeNewFiatRatesTickerId = ""; + document.getElementById('subscribeNewFiatRatesTickerResult').innerText += JSON.stringify(result).replace(/,/g, ", ") + "\n"; + document.getElementById('subscribeNewFiatRatesTickerId').innerText = ""; + document.getElementById('unsubscribeNewFiatRatesTickerButton').setAttribute("style", "display: none;"); + }); + } @@ -460,6 +523,59 @@
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+