From 4781eb55c7610e1107b091f8169b46a3ab3a2c35 Mon Sep 17 00:00:00 2001 From: gruve-p Date: Tue, 31 May 2022 18:03:59 +0200 Subject: [PATCH 001/530] Bump GRS to 23.0 --- configs/coins/groestlcoin.json | 6 +++--- configs/coins/groestlcoin_regtest.json | 6 +++--- configs/coins/groestlcoin_signet.json | 6 +++--- configs/coins/groestlcoin_testnet.json | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/groestlcoin.json b/configs/coins/groestlcoin.json index cfad624fdc..d50638a30a 100644 --- a/configs/coins/groestlcoin.json +++ b/configs/coins/groestlcoin.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "22.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v22.0/groestlcoin-22.0-x86_64-linux-gnu.tar.gz", + "version": "23.0", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v23.0/groestlcoin-23.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "b30c5353dd3d9cfd7e8b31f29eac125925751165f690bacff57effd76560dddd", + "verification_source": "46ab078422d0d2aaf5b89ac9603cb61a6ebf6c26a73b9440365a4df5f9bce7de", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/groestlcoin-qt" diff --git a/configs/coins/groestlcoin_regtest.json b/configs/coins/groestlcoin_regtest.json index cf5f434299..bbfcfceb2e 100644 --- a/configs/coins/groestlcoin_regtest.json +++ b/configs/coins/groestlcoin_regtest.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin-regtest", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "22.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v22.0/groestlcoin-22.0-x86_64-linux-gnu.tar.gz", + "version": "23.0", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v23.0/groestlcoin-23.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "b30c5353dd3d9cfd7e8b31f29eac125925751165f690bacff57effd76560dddd", + "verification_source": "46ab078422d0d2aaf5b89ac9603cb61a6ebf6c26a73b9440365a4df5f9bce7de", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/groestlcoin-qt" diff --git a/configs/coins/groestlcoin_signet.json b/configs/coins/groestlcoin_signet.json index 36ef6266c3..08d401b4e8 100644 --- a/configs/coins/groestlcoin_signet.json +++ b/configs/coins/groestlcoin_signet.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin-signet", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "22.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v22.0/groestlcoin-22.0-x86_64-linux-gnu.tar.gz", + "version": "23.0", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v23.0/groestlcoin-23.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "b30c5353dd3d9cfd7e8b31f29eac125925751165f690bacff57effd76560dddd", + "verification_source": "46ab078422d0d2aaf5b89ac9603cb61a6ebf6c26a73b9440365a4df5f9bce7de", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/groestlcoin-qt" diff --git a/configs/coins/groestlcoin_testnet.json b/configs/coins/groestlcoin_testnet.json index c0daa35bc5..8f13d5df31 100644 --- a/configs/coins/groestlcoin_testnet.json +++ b/configs/coins/groestlcoin_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin-testnet", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "22.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v22.0/groestlcoin-22.0-x86_64-linux-gnu.tar.gz", + "version": "23.0", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v23.0/groestlcoin-23.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "b30c5353dd3d9cfd7e8b31f29eac125925751165f690bacff57effd76560dddd", + "verification_source": "46ab078422d0d2aaf5b89ac9603cb61a6ebf6c26a73b9440365a4df5f9bce7de", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/groestlcoin-qt" From e5a428684699580cf4ba9f9887821e915b290b7d Mon Sep 17 00:00:00 2001 From: JoHnY Date: Thu, 9 Jun 2022 08:32:54 +0000 Subject: [PATCH 002/530] =?UTF-8?q?etc=201.12.6=20=E2=86=92=201.12.7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum-classic.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/ethereum-classic.json b/configs/coins/ethereum-classic.json index 537cd2fbc3..4236df8a47 100644 --- a/configs/coins/ethereum-classic.json +++ b/configs/coins/ethereum-classic.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-classic", "package_revision": "satoshilabs-1", "system_user": "ethereum-classic", - "version": "1.12.6", - "binary_url": "https://github.com/etclabscore/core-geth/releases/download/v1.12.6/core-geth-linux-v1.12.6.zip", + "version": "1.12.7", + "binary_url": "https://github.com/etclabscore/core-geth/releases/download/v1.12.7/core-geth-linux-v1.12.7.zip", "verification_type": "sha256", - "verification_source": "e46af4307abb876cfa423f9766dafc91eadb7f18a64c7fcde89220610797986f", + "verification_source": "91e8834b01e89aaea7b89a70cb005b527ab7815f17ce123229733aa49ff95ec3", "extract_command": "unzip -d backend", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --classic --ipcdisable --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38337 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --http --http.port 8137 --http.addr 127.0.0.1 --http.corsdomain \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From 401740a8b51c6556311756126c30603168931eb3 Mon Sep 17 00:00:00 2001 From: CodeFace Date: Tue, 7 Jun 2022 11:55:44 +0800 Subject: [PATCH 003/530] bump Qtum 22.1 --- bchain/coins/qtum/qtumparser.go | 4 ++-- configs/coins/qtum.json | 6 +++--- configs/coins/qtum_testnet.json | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bchain/coins/qtum/qtumparser.go b/bchain/coins/qtum/qtumparser.go index d6f2496543..e43b7aba6f 100644 --- a/bchain/coins/qtum/qtumparser.go +++ b/bchain/coins/qtum/qtumparser.go @@ -40,13 +40,13 @@ func init() { // QtumParser handle type QtumParser struct { - *btc.BitcoinLikeParser + *btc.BitcoinParser } // NewQtumParser returns new DashParser instance func NewQtumParser(params *chaincfg.Params, c *btc.Configuration) *QtumParser { return &QtumParser{ - BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c), + BitcoinParser: btc.NewBitcoinParser(params, c), } } diff --git a/configs/coins/qtum.json b/configs/coins/qtum.json index ae3c5a580b..f8383ded24 100644 --- a/configs/coins/qtum.json +++ b/configs/coins/qtum.json @@ -22,10 +22,10 @@ "package_name": "backend-qtum", "package_revision": "satoshilabs-1", "system_user": "qtum", - "version": "0.20.2", - "binary_url": "https://github.com/qtumproject/qtum/releases/download/mainnet-fastlane-v0.20.2/qtum-0.20.2-x86_64-linux-gnu.tar.gz", + "version": "22.1", + "binary_url": "https://github.com/qtumproject/qtum/releases/download/v22.1/qtum-22.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "52d746f2fb827c43cd8e1784a29ad6d21b843141b85002a49a3822ceebe8651d", + "verification_source": "34f2c6ca10026cc1600cfb3fbc1e606b7f163a15d98781866be6fc34e7269ea0", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/qtum-qt" diff --git a/configs/coins/qtum_testnet.json b/configs/coins/qtum_testnet.json index 63eb053e79..a374c8a493 100644 --- a/configs/coins/qtum_testnet.json +++ b/configs/coins/qtum_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-qtum-testnet", "package_revision": "satoshilabs-1", "system_user": "qtum", - "version": "0.20.2", - "binary_url": "https://github.com/qtumproject/qtum/releases/download/mainnet-fastlane-v0.20.2/qtum-0.20.2-x86_64-linux-gnu.tar.gz", + "version": "22.1", + "binary_url": "https://github.com/qtumproject/qtum/releases/download/v22.1/qtum-22.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "52d746f2fb827c43cd8e1784a29ad6d21b843141b85002a49a3822ceebe8651d", + "verification_source": "34f2c6ca10026cc1600cfb3fbc1e606b7f163a15d98781866be6fc34e7269ea0", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/qtum-qt" From 8ece7ac936b87765749b8e5fe16269d4d503990f Mon Sep 17 00:00:00 2001 From: JoHnY Date: Wed, 15 Jun 2022 13:48:05 +0000 Subject: [PATCH 004/530] =?UTF-8?q?eth=20(+testnet)=201.10.17=20=E2=86=92?= =?UTF-8?q?=201.10.19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 6 +++--- configs/coins/ethereum_testnet_goerli.json | 6 +++--- configs/coins/ethereum_testnet_ropsten.json | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index c2f2eaec79..7601ac28ab 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.10.17-25c9b49f", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz", + "version": "1.10.19-23bee162", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.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 --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --http --http.port 8136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index aee66db2bd..99a6b86ceb 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -20,10 +20,10 @@ "package_name": "backend-ethereum-testnet-goerli", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.17-25c9b49f", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz", + "version": "1.10.19-23bee162", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48326 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" 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 b4d940c200..bf281f6120 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.10.17-25c9b49f", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz", + "version": "1.10.19-23bee162", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From 0590280d954f8c2b1939225275570fc526f1d928 Mon Sep 17 00:00:00 2001 From: Dmytro <2937451+vorotech@users.noreply.github.com> Date: Fri, 17 Jun 2022 17:23:24 +0300 Subject: [PATCH 005/530] =?UTF-8?q?firo=200.14.9.1=20=E2=86=92=200.14.10.0?= =?UTF-8?q?=20(#778)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * firo 0.14.9.1 → 0.14.10.0 https://github.com/firoorg/firo/releases/tag/v0.14.10.0 * Update firo.json * Update firo.json --- configs/coins/firo.json | 67 +++-------------------------------------- 1 file changed, 5 insertions(+), 62 deletions(-) diff --git a/configs/coins/firo.json b/configs/coins/firo.json index 281b0c0edd..7d24e05d0a 100644 --- a/configs/coins/firo.json +++ b/configs/coins/firo.json @@ -22,72 +22,15 @@ "package_name": "backend-firo", "package_revision": "satoshilabs-1", "system_user": "firo", - "version": "0.14.9.1", - "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.9.1/firo-0.14.9.1-linux64.tar.gz", + "version": "0.14.10.0", + "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.10.0/firo-0.14.10.0-linux64.tar.gz", "verification_type": "sha256", - "verification_source": "6384cc13ba193df3d44d2923b20fa562061b4e204ff8e0180147575fc3a1a588", + "verification_source": "26ac0f15e37ecc417daf9a5933b898a93edcc23a6633a66b79241bff945492eb", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ - "bin/tor", - "bin/tor-gencert", - "bin/torify", - "bin/tor-print-ed-signing-cert", - "bin/tor-resolve", "bin/firo-qt", "bin/firo-tx", - "etc/tor/torrc.sample", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/cmake/relic-config.cmake", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/chiabls/aggregationinfo.hpp", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/chiabls/bls.hpp", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/chiabls/chaincode.hpp", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/chiabls/extendedprivatekey.hpp", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/chiabls/extendedpublickey.hpp", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/chiabls/privatekey.hpp", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/chiabls/publickey.hpp", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/chiabls/signature.hpp", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/chiabls/test-utils.hpp", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/chiabls/util.hpp", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/low/relic_bn_low.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/low/relic_dv_low.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/low/relic_fb_low.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/low/relic_fp_low.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/low/relic_fpx_low.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_arch.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_bc.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_bench.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_bn.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_conf.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_core.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_cp.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_dv.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_eb.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_ec.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_ed.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_ep.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_epx.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_err.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_fb.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_fbx.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_fp.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_fpx.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_label.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_md.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_pc.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_pool.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_pp.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_rand.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_test.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_trace.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_types.h", - "home/ubuntu/build/firo/depends/x86_64-linux-gnu/include/relic_util.h", - "include/bitcoinconsensus.h", - "lib/libbitcoinconsensus.so", - "lib/libbitcoinconsensus.so.0", - "lib/libbitcoinconsensus.so.0.0.0", - "README.md", - "share/tor/geoip", - "share/tor/geoip6" + "README.md" ], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/firod -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", @@ -122,4 +65,4 @@ "package_maintainer": "Putta Khunchalee", "package_maintainer_email": "putta@zcoin.io" } -} \ No newline at end of file +} From dcc770b2f7d702c33bf53b05dd9a38e14bbdcd79 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Fri, 17 Jun 2022 14:23:08 +0000 Subject: [PATCH 006/530] =?UTF-8?q?ltc=20(+testnet)=200.21.2=20=E2=86=92?= =?UTF-8?q?=200.21.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/litecoin.json | 6 +++--- configs/coins/litecoin_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/litecoin.json b/configs/coins/litecoin.json index 0e580e42a8..87ed3cf4e7 100644 --- a/configs/coins/litecoin.json +++ b/configs/coins/litecoin.json @@ -22,10 +22,10 @@ "package_name": "backend-litecoin", "package_revision": "satoshilabs-1", "system_user": "litecoin", - "version": "0.21.2", - "binary_url": "https://download.litecoin.org/litecoin-0.21.2/linux/litecoin-0.21.2-x86_64-linux-gnu.tar.gz", + "version": "0.21.2.1", + "binary_url": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-x86_64-linux-gnu.tar.gz", "verification_type": "gpg", - "verification_source": "https://download.litecoin.org/litecoin-0.21.2/linux/litecoin-0.21.2-x86_64-linux-gnu.tar.gz.asc", + "verification_source": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-x86_64-linux-gnu.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/litecoin-qt" diff --git a/configs/coins/litecoin_testnet.json b/configs/coins/litecoin_testnet.json index 3293eb3069..a5956c96e9 100644 --- a/configs/coins/litecoin_testnet.json +++ b/configs/coins/litecoin_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-litecoin-testnet", "package_revision": "satoshilabs-1", "system_user": "litecoin", - "version": "0.21.2", - "binary_url": "https://download.litecoin.org/litecoin-0.21.2/linux/litecoin-0.21.2-x86_64-linux-gnu.tar.gz", + "version": "0.21.2.1", + "binary_url": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-x86_64-linux-gnu.tar.gz", "verification_type": "gpg", - "verification_source": "https://download.litecoin.org/litecoin-0.21.2/linux/litecoin-0.21.2-x86_64-linux-gnu.tar.gz.asc", + "verification_source": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-x86_64-linux-gnu.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/litecoin-qt" From 2002bd23524b93fb755b8b4f50aee3e7bc906279 Mon Sep 17 00:00:00 2001 From: wakiyamap Date: Wed, 15 Jun 2022 01:54:55 +0900 Subject: [PATCH 007/530] =?UTF-8?q?mona=20(+testnet)=200.20.2=20=E2=86=92?= =?UTF-8?q?=200.20.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/monacoin.json | 6 +++--- configs/coins/monacoin_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/monacoin.json b/configs/coins/monacoin.json index bda3dfded1..9b4fa96a61 100644 --- a/configs/coins/monacoin.json +++ b/configs/coins/monacoin.json @@ -22,10 +22,10 @@ "package_name": "backend-monacoin", "package_revision": "satoshilabs-1", "system_user": "monacoin", - "version": "0.20.2", - "binary_url": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.2/monacoin-0.20.2-x86_64-linux-gnu.tar.gz", + "version": "0.20.3", + "binary_url": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.3/monacoin-0.20.3-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.2/monacoin-0.20.2-signatures.asc", + "verification_source": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.3/monacoin-0.20.3-signatures.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/monacoin-qt" diff --git a/configs/coins/monacoin_testnet.json b/configs/coins/monacoin_testnet.json index d3fe2e355f..c867057e7e 100644 --- a/configs/coins/monacoin_testnet.json +++ b/configs/coins/monacoin_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-monacoin-testnet", "package_revision": "satoshilabs-1", "system_user": "monacoin", - "version": "0.20.2", - "binary_url": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.2/monacoin-0.20.2-x86_64-linux-gnu.tar.gz", + "version": "0.20.3", + "binary_url": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.3/monacoin-0.20.3-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.2/monacoin-0.20.2-signatures.asc", + "verification_source": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.3/monacoin-0.20.3-signatures.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/monacoin-qt" From 83359706482276d016aead87e85f41b787b248fb Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 26 Jul 2022 07:59:55 +0000 Subject: [PATCH 008/530] =?UTF-8?q?dogecoin=20(+testnet)=201.14.5=20?= =?UTF-8?q?=E2=86=92=201.14.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/dogecoin.json | 6 +++--- configs/coins/dogecoin_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/dogecoin.json b/configs/coins/dogecoin.json index 9b0c6891b3..8a5fb3cb41 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.5", - "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.5/dogecoin-1.14.5-x86_64-linux-gnu.tar.gz", + "version": "1.14.6", + "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.6/dogecoin-1.14.6-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "17a03f019168ec5283947ea6fbf1a073c1d185ea9edacc2b91f360e1c191428e", + "verification_source": "fe9c9cdab946155866a5bd5a5127d2971a9eed3e0b65fb553fe393ad1daaebb0", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dogecoin-qt" diff --git a/configs/coins/dogecoin_testnet.json b/configs/coins/dogecoin_testnet.json index 8ef2edc7aa..7dece87b32 100644 --- a/configs/coins/dogecoin_testnet.json +++ b/configs/coins/dogecoin_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-dogecoin-testnet", "package_revision": "satoshilabs-1", "system_user": "dogecoin", - "version": "1.14.5", - "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.5/dogecoin-1.14.5-x86_64-linux-gnu.tar.gz", + "version": "1.14.6", + "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.6/dogecoin-1.14.6-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "17a03f019168ec5283947ea6fbf1a073c1d185ea9edacc2b91f360e1c191428e", + "verification_source": "fe9c9cdab946155866a5bd5a5127d2971a9eed3e0b65fb553fe393ad1daaebb0", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dogecoin-qt" From 1801dc45a6f134aa6cebb19649227fe63017c7a2 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 26 Jul 2022 08:26:03 +0000 Subject: [PATCH 009/530] =?UTF-8?q?zcash=20(+testnet)=205.0.0=20=E2=86=92?= =?UTF-8?q?=205.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/zcash.json | 6 +++--- configs/coins/zcash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index 6d1c9d0538..6ba96a8b4d 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,10 +22,10 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "5.0.0", - "binary_url": "https://z.cash/downloads/zcash-5.0.0-linux64-debian-bullseye.tar.gz", + "version": "5.1.0", + "binary_url": "https://z.cash/downloads/zcash-5.1.0-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "f9b87ae99ea2c2f659e67481cb9ce9dd3f179619ae38334f383f2eb8db6f9e2c", + "verification_source": "f1be2acb2a704b0bb23ca13b99add10585e5b080ce8ead789c8379a14dabf12b", "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 4622ba5ba3..91a0a2d6d0 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -21,10 +21,10 @@ "backend": { "package_name": "backend-zcash-testnet", "package_revision": "satoshilabs-1", - "version": "5.0.0", - "binary_url": "https://z.cash/downloads/zcash-5.0.0-linux64-debian-bullseye.tar.gz", + "version": "5.1.0", + "binary_url": "https://z.cash/downloads/zcash-5.1.0-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "f9b87ae99ea2c2f659e67481cb9ce9dd3f179619ae38334f383f2eb8db6f9e2c", + "verification_source": "f1be2acb2a704b0bb23ca13b99add10585e5b080ce8ead789c8379a14dabf12b", "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 f4921047880577e20f355d8be0d7c1e72c837392 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Sat, 30 Jul 2022 09:12:47 +0000 Subject: [PATCH 010/530] =?UTF-8?q?eth=20(+testnet)=201.10.19=20=E2=86=92?= =?UTF-8?q?=201.10.21?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 6 +++--- configs/coins/ethereum_testnet_goerli.json | 6 +++--- configs/coins/ethereum_testnet_ropsten.json | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 7601ac28ab..5d54383f0b 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.10.19-23bee162", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz", + "version": "1.10.21-67109427", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.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 --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --http --http.port 8136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index 99a6b86ceb..0e70afbeb9 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -20,10 +20,10 @@ "package_name": "backend-ethereum-testnet-goerli", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.19-23bee162", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz", + "version": "1.10.21-67109427", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48326 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" 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 bf281f6120..6ca99844b5 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.10.19-23bee162", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz", + "version": "1.10.21-67109427", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From 0226542178e05060e06639b4ace847a788624d62 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Wed, 27 Jul 2022 08:11:13 +0000 Subject: [PATCH 011/530] =?UTF-8?q?zcash=20(+testnet)=205.1.0=20=E2=86=92?= =?UTF-8?q?=205.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/zcash.json | 6 +++--- configs/coins/zcash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index 6ba96a8b4d..946e907793 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,10 +22,10 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "5.1.0", - "binary_url": "https://z.cash/downloads/zcash-5.1.0-linux64-debian-bullseye.tar.gz", + "version": "5.2.0", + "binary_url": "https://z.cash/downloads/zcash-5.2.0-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "f1be2acb2a704b0bb23ca13b99add10585e5b080ce8ead789c8379a14dabf12b", + "verification_source": "ce7113843862f04470d1260e293c393e523b36f8e5cb7b942ed56fa63a8ae77f", "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 91a0a2d6d0..2bf6d5cfc7 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -21,10 +21,10 @@ "backend": { "package_name": "backend-zcash-testnet", "package_revision": "satoshilabs-1", - "version": "5.1.0", - "binary_url": "https://z.cash/downloads/zcash-5.1.0-linux64-debian-bullseye.tar.gz", + "version": "5.2.0", + "binary_url": "https://z.cash/downloads/zcash-5.2.0-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "f1be2acb2a704b0bb23ca13b99add10585e5b080ce8ead789c8379a14dabf12b", + "verification_source": "ce7113843862f04470d1260e293c393e523b36f8e5cb7b942ed56fa63a8ae77f", "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 309f05c1668164cf3bd41aec085ab5f4b818a161 Mon Sep 17 00:00:00 2001 From: vdovhanych Date: Fri, 19 Aug 2022 13:07:40 +0200 Subject: [PATCH 012/530] feat: edit Dockerfile for arm64 build compatibility --- build/docker/bin/Dockerfile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/build/docker/bin/Dockerfile b/build/docker/bin/Dockerfile index d11c783896..eab7d0ab0f 100644 --- a/build/docker/bin/Dockerfile +++ b/build/docker/bin/Dockerfile @@ -10,8 +10,8 @@ RUN apt-get update && \ libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev \ liblz4-dev graphviz && \ apt-get clean - -ENV GOLANG_VERSION=go1.17.1.linux-amd64 +ARG GOLANG_VERSION +ENV GOLANG_VERSION=go1.17.1 ENV ROCKSDB_VERSION=v6.22.1 ENV GOPATH=/go ENV PATH=$PATH:$GOPATH/bin @@ -28,8 +28,10 @@ RUN if [ -n "${TCMALLOC}" ]; then \ fi # install and configure go -RUN cd /opt && wget https://dl.google.com/go/$GOLANG_VERSION.tar.gz && \ - tar xf $GOLANG_VERSION.tar.gz +ARG TARGETPLATFORM +RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then ARCHITECTURE=amd64; elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCHITECTURE=arm64; else ARCHITECTURE=amd64; fi \ + && cd /opt && wget https://dl.google.com/go/$GOLANG_VERSION.linux-$ARCHITECTURE.tar.gz && \ + tar xf $GOLANG_VERSION.linux-$ARCHITECTURE.tar.gz RUN ln -s /opt/go/bin/go /usr/bin/go RUN mkdir -p $GOPATH RUN echo -n "GO version: " && go version From 819596cb1fcbf33d992c855757839c7aea15bc7d Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 20 Aug 2022 22:19:09 +0200 Subject: [PATCH 013/530] Enable arm64 and MacOS Docker build --- build/templates/backend/debian/control | 2 +- build/templates/blockbook/debian/control | 2 +- build/tools/image_status.sh | 6 +-- build/tools/templates.go | 66 ++++++++++++++++-------- configs/coins/bitcoin_regtest.json | 6 +++ 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/build/templates/backend/debian/control b/build/templates/backend/debian/control index 4093b52e82..cdc74d4e9a 100644 --- a/build/templates/backend/debian/control +++ b/build/templates/backend/debian/control @@ -7,7 +7,7 @@ Build-Depends: debhelper, wget, tar, gzip, make, dh-exec Standards-Version: 3.9.5 Package: {{.Backend.PackageName}} -Architecture: amd64 +Architecture: {{.Env.Architecture}} Depends: ${shlibs:Depends}, ${misc:Depends}, logrotate Description: Satoshilabs packaged {{.Coin.Name}} server {{end}} diff --git a/build/templates/blockbook/debian/control b/build/templates/blockbook/debian/control index c269337b8a..e596de0142 100644 --- a/build/templates/blockbook/debian/control +++ b/build/templates/blockbook/debian/control @@ -7,7 +7,7 @@ Build-Depends: debhelper, dh-exec Standards-Version: 3.9.5 Package: {{.Blockbook.PackageName}} -Architecture: amd64 +Architecture: {{.Env.Architecture}} Depends: ${shlibs:Depends}, ${misc:Depends}, coreutils, passwd, findutils, psmisc, {{.Backend.PackageName}} Description: Satoshilabs blockbook server ({{.Coin.Name}}) {{end}} diff --git a/build/tools/image_status.sh b/build/tools/image_status.sh index 5c4397b72c..c8dc4283b8 100755 --- a/build/tools/image_status.sh +++ b/build/tools/image_status.sh @@ -16,10 +16,10 @@ if [ -z "$IMG_CREATED_TIME" ]; then exit 0 fi -IMG_CREATED_TS=$(date -d $IMG_CREATED_TIME +%s) -GIT_COMMIT_TS=$(date -d $(git log --pretty="format:%cI" -1 $DIR) +%s) +IMG_CREATED_TS=$IMG_CREATED_TIME +GIT_COMMIT_TS=$(git log --pretty="format:%cI" -1 $DIR) -if [ $IMG_CREATED_TS -lt $GIT_COMMIT_TS ]; then +if [[ "$IMG_CREATED_TS" < "$GIT_COMMIT_TS" ]]; then echo "out-of-time" else echo "ok" diff --git a/build/tools/templates.go b/build/tools/templates.go index 5612c5223d..13f1d5c777 100644 --- a/build/tools/templates.go +++ b/build/tools/templates.go @@ -8,10 +8,36 @@ import ( "os" "os/exec" "path/filepath" + "reflect" + "runtime" "text/template" "time" ) +// Backend contains backend specific fields +type Backend struct { + PackageName string `json:"package_name"` + PackageRevision string `json:"package_revision"` + SystemUser string `json:"system_user"` + Version string `json:"version"` + BinaryURL string `json:"binary_url"` + VerificationType string `json:"verification_type"` + VerificationSource string `json:"verification_source"` + ExtractCommand string `json:"extract_command"` + ExcludeFiles []string `json:"exclude_files"` + ExecCommandTemplate string `json:"exec_command_template"` + LogrotateFilesTemplate string `json:"logrotate_files_template"` + PostinstScriptTemplate string `json:"postinst_script_template"` + ServiceType string `json:"service_type"` + ServiceAdditionalParamsTemplate string `json:"service_additional_params_template"` + ProtectMemory bool `json:"protect_memory"` + Mainnet bool `json:"mainnet"` + ServerConfigFile string `json:"server_config_file"` + ClientConfigFile string `json:"client_config_file"` + AdditionalParams interface{} `json:"additional_params,omitempty"` + Platforms map[string]Backend `json:"platforms,omitempty"` +} + // Config contains the structure of the config type Config struct { Coin struct { @@ -33,27 +59,7 @@ type Config struct { RPCTimeout int `json:"rpc_timeout"` MessageQueueBindingTemplate string `json:"message_queue_binding_template"` } `json:"ipc"` - Backend struct { - PackageName string `json:"package_name"` - PackageRevision string `json:"package_revision"` - SystemUser string `json:"system_user"` - Version string `json:"version"` - BinaryURL string `json:"binary_url"` - VerificationType string `json:"verification_type"` - VerificationSource string `json:"verification_source"` - ExtractCommand string `json:"extract_command"` - ExcludeFiles []string `json:"exclude_files"` - ExecCommandTemplate string `json:"exec_command_template"` - LogrotateFilesTemplate string `json:"logrotate_files_template"` - PostinstScriptTemplate string `json:"postinst_script_template"` - ServiceType string `json:"service_type"` - ServiceAdditionalParamsTemplate string `json:"service_additional_params_template"` - ProtectMemory bool `json:"protect_memory"` - Mainnet bool `json:"mainnet"` - ServerConfigFile string `json:"server_config_file"` - ClientConfigFile string `json:"client_config_file"` - AdditionalParams interface{} `json:"additional_params,omitempty"` - } `json:"backend"` + Backend Backend `json:"backend"` Blockbook struct { PackageName string `json:"package_name"` SystemUser string `json:"system_user"` @@ -87,6 +93,7 @@ type Config struct { BackendDataPath string `json:"backend_data_path"` BlockbookInstallPath string `json:"blockbook_install_path"` BlockbookDataPath string `json:"blockbook_data_path"` + Architecture string `json:"architecture"` } `json:"-"` } @@ -136,6 +143,16 @@ func (c *Config) ParseTemplate() *template.Template { return t } +func copyNonZeroBackendFields(toValue *Backend, fromValue *Backend) { + from := reflect.ValueOf(*fromValue) + to := reflect.ValueOf(toValue).Elem() + for i := 0; i < from.NumField(); i++ { + if from.Field(i).IsValid() && !from.Field(i).IsZero() { + to.Field(i).Set(from.Field(i)) + } + } +} + // LoadConfig loads the config files func LoadConfig(configsDir, coin string) (*Config, error) { config := new(Config) @@ -161,8 +178,15 @@ func LoadConfig(configsDir, coin string) (*Config, error) { } config.Meta.BuildDatetime = time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700") + config.Env.Architecture = runtime.GOARCH if !isEmpty(config, "backend") { + // set platform specific fields to config + platform, found := config.Backend.Platforms[runtime.GOARCH] + if found { + copyNonZeroBackendFields(&config.Backend, &platform) + } + switch config.Backend.ServiceType { case "forking": case "simple": diff --git a/configs/coins/bitcoin_regtest.json b/configs/coins/bitcoin_regtest.json index dd025911b8..45ebef2f60 100644 --- a/configs/coins/bitcoin_regtest.json +++ b/configs/coins/bitcoin_regtest.json @@ -41,6 +41,12 @@ "client_config_file": "bitcoin_client.conf", "additional_params": { "deprecatedrpc": "estimatefee" + }, + "platforms": { + "arm64": { + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-23.0/bitcoin-23.0-aarch64-linux-gnu.tar.gz", + "verification_source": "06f4c78271a77752ba5990d60d81b1751507f77efda1e5981b4e92fd4d9969fb" + } } }, "blockbook": { From 5e839e1cee458af62445eafc2b28bade59fe655b Mon Sep 17 00:00:00 2001 From: JoHnY Date: Thu, 18 Aug 2022 08:16:56 +0000 Subject: [PATCH 014/530] =?UTF-8?q?dash=20(+testnet)=200.17.0.3=20?= =?UTF-8?q?=E2=86=92=200.18.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/dash.json | 6 +++--- configs/coins/dash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/dash.json b/configs/coins/dash.json index f2ee61a5c9..2942eea28c 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.17.0.3", - "binary_url": "https://github.com/dashpay/dash/releases/download/v0.17.0.3/dashcore-0.17.0.3-x86_64-linux-gnu.tar.gz", + "version": "0.18.0.1", + "binary_url": "https://github.com/dashpay/dash/releases/download/v18.0.1/dashcore-18.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v0.17.0.3/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v18.0.1/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" diff --git a/configs/coins/dash_testnet.json b/configs/coins/dash_testnet.json index c4c5e17108..653f1a33d9 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.17.0.3", - "binary_url": "https://github.com/dashpay/dash/releases/download/v0.17.0.3/dashcore-0.17.0.3-x86_64-linux-gnu.tar.gz", + "version": "0.18.0.1", + "binary_url": "https://github.com/dashpay/dash/releases/download/v18.0.1/dashcore-18.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v0.17.0.3/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v18.0.1/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" From ab0e7cd33b351a448e41e28363967b523d159abb Mon Sep 17 00:00:00 2001 From: Dmytro <2937451+vorotech@users.noreply.github.com> Date: Fri, 5 Aug 2022 13:29:06 +0300 Subject: [PATCH 015/530] =?UTF-8?q?firo=200.14.10.0=20=E2=86=92=200.14.11.?= =?UTF-8?q?1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/firo.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/firo.json b/configs/coins/firo.json index 7d24e05d0a..1e29446998 100644 --- a/configs/coins/firo.json +++ b/configs/coins/firo.json @@ -22,10 +22,10 @@ "package_name": "backend-firo", "package_revision": "satoshilabs-1", "system_user": "firo", - "version": "0.14.10.0", - "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.10.0/firo-0.14.10.0-linux64.tar.gz", + "version": "0.14.11.1", + "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.11.1/firo-0.14.11.1-linux64.tar.gz", "verification_type": "sha256", - "verification_source": "26ac0f15e37ecc417daf9a5933b898a93edcc23a6633a66b79241bff945492eb", + "verification_source": "8669ae8ce3356deee2512a4da133eab347c704cf47c865caf9ea10b46ba8b477", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/firo-qt", From 6e0a045d35f97695488db4f32ecb58e0e4b4da35 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 9 Oct 2022 21:56:36 +0200 Subject: [PATCH 016/530] Bump btcd library to process transactions with large witness item size The bitcoin testnet transaction 44692bc2da73192cd0b89bc7a43c0ce43578f6b3567bc945e46e6952e8ec5ca5 has witness size 396669. Originally the max witness size in btcd library was set to 11000, which prohibited processing of the transaction. Now it is set to 500000. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index eb384ddb06..e75b1a3b73 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe - github.com/martinboehm/btcd v0.0.0-20211010165247-d1f65b0f30fa + github.com/martinboehm/btcd v0.0.0-20221009194001-987348babe73 github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde github.com/mr-tron/base58 v1.2.0 // indirect diff --git a/go.sum b/go.sum index dc27b61eab..7dcd721f35 100644 --- a/go.sum +++ b/go.sum @@ -403,8 +403,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe h1:khZWpHuxJNh2EGzBbaS6EQ2d6KxgK31WeG0TnlTMUD4= github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe/go.mod h1:0hw4tpGU+9slqN/DrevhjTMb0iR9esxzpCdx8I6/UzU= github.com/martinboehm/btcd v0.0.0-20190104121910-8e7c0427fee5/go.mod h1:rKQj/jGwFruYjpM6vN+syReFoR0DsLQaajhyH/5mwUE= -github.com/martinboehm/btcd v0.0.0-20211010165247-d1f65b0f30fa h1:n8hCPoGumR6jNmNTMAo/VqDOw1yxUf0UCXJVZwf+JLQ= -github.com/martinboehm/btcd v0.0.0-20211010165247-d1f65b0f30fa/go.mod h1:YGXD0z/xtFXFF5jFp1GaVnrKRlEADn4pD47Zu4xaLg0= +github.com/martinboehm/btcd v0.0.0-20221009194001-987348babe73 h1:IaA3JXJ1iTNurglw33ehZOOyhP8W1rEJX1Y2U42w8fw= +github.com/martinboehm/btcd v0.0.0-20221009194001-987348babe73/go.mod h1:YGXD0z/xtFXFF5jFp1GaVnrKRlEADn4pD47Zu4xaLg0= github.com/martinboehm/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:NIviPmxe43yBgIB4HGB4w4kv9/s5kaDa/pi+wZAAxQo= github.com/martinboehm/btcutil v0.0.0-20210922221517-e83b0c752949/go.mod h1:8iJaVY/VHW6lnojpTXf5X4gF2dx81Xtj2R6lJp2colA= github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 h1:ra2UymMEDhR0CVxqz/0minCNXO8YMeZwxdnnFDpWVJ0= From 08e69f732dbe4f5263224bf6d45652f1c0522db4 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 25 Oct 2022 07:27:56 +0000 Subject: [PATCH 017/530] =?UTF-8?q?zcash=20(+testnet)=205.2.0=20=E2=86=92?= =?UTF-8?q?=205.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/zcash.json | 6 +++--- configs/coins/zcash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index 946e907793..ed68a1c7a0 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,10 +22,10 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "5.2.0", - "binary_url": "https://z.cash/downloads/zcash-5.2.0-linux64-debian-bullseye.tar.gz", + "version": "5.3.0", + "binary_url": "https://z.cash/downloads/zcash-5.3.0-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "ce7113843862f04470d1260e293c393e523b36f8e5cb7b942ed56fa63a8ae77f", + "verification_source": "9e6683a2ee121adf27e0d47adcf6a35807266a5a107809c5999d3fe9acb763b7", "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 2bf6d5cfc7..d499da9300 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -21,10 +21,10 @@ "backend": { "package_name": "backend-zcash-testnet", "package_revision": "satoshilabs-1", - "version": "5.2.0", - "binary_url": "https://z.cash/downloads/zcash-5.2.0-linux64-debian-bullseye.tar.gz", + "version": "5.3.0", + "binary_url": "https://z.cash/downloads/zcash-5.3.0-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "ce7113843862f04470d1260e293c393e523b36f8e5cb7b942ed56fa63a8ae77f", + "verification_source": "9e6683a2ee121adf27e0d47adcf6a35807266a5a107809c5999d3fe9acb763b7", "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 fafe82edd5df99841a0f19aac3419c9487ddc55f Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 25 Oct 2022 07:34:54 +0000 Subject: [PATCH 018/530] =?UTF-8?q?dash=20(+testnet)=2018.0.1=20=E2=86=92?= =?UTF-8?q?=2018.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/dash.json | 6 +++--- configs/coins/dash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/dash.json b/configs/coins/dash.json index 2942eea28c..72b33a0531 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.18.0.1", - "binary_url": "https://github.com/dashpay/dash/releases/download/v18.0.1/dashcore-18.0.1-x86_64-linux-gnu.tar.gz", + "version": "18.1.0", + "binary_url": "https://github.com/dashpay/dash/releases/download/v18.1.0/dashcore-18.1.0-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v18.0.1/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v18.1.0/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" diff --git a/configs/coins/dash_testnet.json b/configs/coins/dash_testnet.json index 653f1a33d9..7cc9673ce2 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.18.0.1", - "binary_url": "https://github.com/dashpay/dash/releases/download/v18.0.1/dashcore-18.0.1-x86_64-linux-gnu.tar.gz", + "version": "18.1.0", + "binary_url": "https://github.com/dashpay/dash/releases/download/v18.1.0/dashcore-18.1.0-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v18.0.1/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v18.1.0/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" From 8edcee02a835bb99b1387ccc1620d83d4fb1e7fb Mon Sep 17 00:00:00 2001 From: Martin Kuvandzhiev Date: Wed, 26 Oct 2022 15:51:40 +0300 Subject: [PATCH 019/530] Updating the documentation for POST of sendtx The POST sendtx has a trailing '/' character at the end. Without it, blockbook returns aways "missing inputs". With that PR I'm adding the information. --- docs/api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api.md b/docs/api.md index 9c372cd3e2..9798429f89 100644 --- a/docs/api.md +++ b/docs/api.md @@ -17,7 +17,7 @@ GET /api/v1/utxo/
GET /api/v1/block/ GET /api/v1/estimatefee/ GET /api/v1/sendtx/ -POST /api/v1/sendtx (hex tx data in request body) +POST /api/v1/sendtx/ (hex tx data in request body) ``` ### Socket.io API @@ -579,7 +579,7 @@ Sends new transaction to backend. ``` GET /api/v2/sendtx/ -POST /api/v2/sendtx (hex tx data in request body) +POST /api/v2/sendtx/ (hex tx data in request body) NB: the '/' symbol at the end is mandatory. ``` Response: From b9bf68fb13ea34b1e1156710ca0230f95d309dbb Mon Sep 17 00:00:00 2001 From: Pierre K Date: Thu, 27 Oct 2022 19:16:07 +0200 Subject: [PATCH 020/530] Bump eCash node to Bitcoin ABC 0.26.1 (#805) --- configs/coins/ecash.json | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/configs/coins/ecash.json b/configs/coins/ecash.json index af40823e80..ec51d13d47 100644 --- a/configs/coins/ecash.json +++ b/configs/coins/ecash.json @@ -22,10 +22,10 @@ "package_name": "backend-ecash", "package_revision": "satoshilabs-1", "system_user": "ecash", - "version": "0.25.1", - "binary_url": "https://download.bitcoinabc.org/0.25.1/linux/bitcoin-abc-0.25.1-x86_64-linux-gnu.tar.gz", + "version": "0.26.1", + "binary_url": "https://download.bitcoinabc.org/0.26.1/linux/bitcoin-abc-0.26.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "295183578ce67444fd6a504d2dcb85d07345454881ba4db5f52d82dd3a659bed", + "verification_source": "64c799b339b2aa03f50ac605f7df0586341ff5a2d74321b424f4fe35d37da0be", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -38,7 +38,11 @@ "protect_memory": true, "mainnet": true, "server_config_file": "bcash.conf", - "client_config_file": "bitcoin_like_client.conf" + "client_config_file": "bitcoin_like_client.conf", + "additional_params": { + "listen": 1, + "avalanche": 1 + } }, "blockbook": { "package_name": "blockbook-ecash", @@ -49,7 +53,7 @@ "additional_params": "", "block_chain": { "parse": true, - "subversion": "/Bitcoin ABC:0.24.9(EB32.0)/", + "subversion": "/Bitcoin ABC:0.26.1(EB32.0)/", "address_format": "cashaddr", "mempool_workers": 8, "mempool_sub_workers": 2, From 23dc21153e216507c0fce11c2b719a688c244916 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 1 Nov 2022 11:58:06 +0100 Subject: [PATCH 021/530] Update btcd dependency --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e75b1a3b73..1939e37ccd 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe - github.com/martinboehm/btcd v0.0.0-20221009194001-987348babe73 + github.com/martinboehm/btcd v0.0.0-20221010203408-826a2173023c github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde github.com/mr-tron/base58 v1.2.0 // indirect diff --git a/go.sum b/go.sum index 7dcd721f35..7e2daeb29c 100644 --- a/go.sum +++ b/go.sum @@ -403,8 +403,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe h1:khZWpHuxJNh2EGzBbaS6EQ2d6KxgK31WeG0TnlTMUD4= github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe/go.mod h1:0hw4tpGU+9slqN/DrevhjTMb0iR9esxzpCdx8I6/UzU= github.com/martinboehm/btcd v0.0.0-20190104121910-8e7c0427fee5/go.mod h1:rKQj/jGwFruYjpM6vN+syReFoR0DsLQaajhyH/5mwUE= -github.com/martinboehm/btcd v0.0.0-20221009194001-987348babe73 h1:IaA3JXJ1iTNurglw33ehZOOyhP8W1rEJX1Y2U42w8fw= -github.com/martinboehm/btcd v0.0.0-20221009194001-987348babe73/go.mod h1:YGXD0z/xtFXFF5jFp1GaVnrKRlEADn4pD47Zu4xaLg0= +github.com/martinboehm/btcd v0.0.0-20221010203408-826a2173023c h1:2CwtozuaSPMFrUiSMKuwMPpbMg7JS5nb/q1CWX2tNj8= +github.com/martinboehm/btcd v0.0.0-20221010203408-826a2173023c/go.mod h1:YGXD0z/xtFXFF5jFp1GaVnrKRlEADn4pD47Zu4xaLg0= github.com/martinboehm/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:NIviPmxe43yBgIB4HGB4w4kv9/s5kaDa/pi+wZAAxQo= github.com/martinboehm/btcutil v0.0.0-20210922221517-e83b0c752949/go.mod h1:8iJaVY/VHW6lnojpTXf5X4gF2dx81Xtj2R6lJp2colA= github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 h1:ra2UymMEDhR0CVxqz/0minCNXO8YMeZwxdnnFDpWVJ0= From 95eb699ccbaeef0ec6d8fd0486de3445b8405e8a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 1 Nov 2022 12:41:56 +0100 Subject: [PATCH 022/530] Update btcd dependency to fix issue with maxWitnessItemsPerInput in BTC --- go.mod | 2 +- go.sum | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1939e37ccd..f02c5d7c42 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe - github.com/martinboehm/btcd v0.0.0-20221010203408-826a2173023c + github.com/martinboehm/btcd v0.0.0-20221101112928-408689e15809 github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde github.com/mr-tron/base58 v1.2.0 // indirect diff --git a/go.sum b/go.sum index 7e2daeb29c..184844b5eb 100644 --- a/go.sum +++ b/go.sum @@ -403,8 +403,9 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe h1:khZWpHuxJNh2EGzBbaS6EQ2d6KxgK31WeG0TnlTMUD4= github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe/go.mod h1:0hw4tpGU+9slqN/DrevhjTMb0iR9esxzpCdx8I6/UzU= github.com/martinboehm/btcd v0.0.0-20190104121910-8e7c0427fee5/go.mod h1:rKQj/jGwFruYjpM6vN+syReFoR0DsLQaajhyH/5mwUE= -github.com/martinboehm/btcd v0.0.0-20221010203408-826a2173023c h1:2CwtozuaSPMFrUiSMKuwMPpbMg7JS5nb/q1CWX2tNj8= -github.com/martinboehm/btcd v0.0.0-20221010203408-826a2173023c/go.mod h1:YGXD0z/xtFXFF5jFp1GaVnrKRlEADn4pD47Zu4xaLg0= +github.com/martinboehm/btcd v0.0.0-20211010165247-d1f65b0f30fa/go.mod h1:YGXD0z/xtFXFF5jFp1GaVnrKRlEADn4pD47Zu4xaLg0= +github.com/martinboehm/btcd v0.0.0-20221101112928-408689e15809 h1:a3l5GCQYYyB4zDmtsB8gu+aB15earQxMG1W/S/zKcXs= +github.com/martinboehm/btcd v0.0.0-20221101112928-408689e15809/go.mod h1:YGXD0z/xtFXFF5jFp1GaVnrKRlEADn4pD47Zu4xaLg0= github.com/martinboehm/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:NIviPmxe43yBgIB4HGB4w4kv9/s5kaDa/pi+wZAAxQo= github.com/martinboehm/btcutil v0.0.0-20210922221517-e83b0c752949/go.mod h1:8iJaVY/VHW6lnojpTXf5X4gF2dx81Xtj2R6lJp2colA= github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 h1:ra2UymMEDhR0CVxqz/0minCNXO8YMeZwxdnnFDpWVJ0= From a47c5f4b42da83ebf3155b4f70c4389fd57a4a71 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Fri, 25 Nov 2022 08:15:23 +0000 Subject: [PATCH 023/530] =?UTF-8?q?btc=20(+testnet)=2023.0=20=E2=86=92=202?= =?UTF-8?q?4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/bitcoin.json | 6 +++--- configs/coins/bitcoin_regtest.json | 10 +++++----- configs/coins/bitcoin_signet.json | 6 +++--- configs/coins/bitcoin_testnet.json | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index c059bb8616..ca3705072e 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": "23.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-23.0/bitcoin-23.0-x86_64-linux-gnu.tar.gz", + "version": "24.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0/bitcoin-24.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "2cca490c1f2842884a3c5b0606f179f9f937177da4eadd628e3f7fd7e25d26d0", + "verification_source": "fb86cf6af7a10bc5f3ae6cd6a5b0348854e1462102fe71e755d30b51b6e317d1", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" diff --git a/configs/coins/bitcoin_regtest.json b/configs/coins/bitcoin_regtest.json index 45ebef2f60..8852063d07 100644 --- a/configs/coins/bitcoin_regtest.json +++ b/configs/coins/bitcoin_regtest.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-regtest", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "23.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-23.0/bitcoin-23.0-x86_64-linux-gnu.tar.gz", + "version": "24.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0/bitcoin-24.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "2cca490c1f2842884a3c5b0606f179f9f937177da4eadd628e3f7fd7e25d26d0", + "verification_source": "fb86cf6af7a10bc5f3ae6cd6a5b0348854e1462102fe71e755d30b51b6e317d1", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" @@ -44,8 +44,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-23.0/bitcoin-23.0-aarch64-linux-gnu.tar.gz", - "verification_source": "06f4c78271a77752ba5990d60d81b1751507f77efda1e5981b4e92fd4d9969fb" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0/bitcoin-24.0-aarch64-linux-gnu.tar.gz", + "verification_source": "904e103f08f776d03935118568411724f9e070e0e888e52c9e5692308fa47d49" } } }, diff --git a/configs/coins/bitcoin_signet.json b/configs/coins/bitcoin_signet.json index 5eab9c235d..24e5506f44 100644 --- a/configs/coins/bitcoin_signet.json +++ b/configs/coins/bitcoin_signet.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-signet", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "23.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-23.0/bitcoin-23.0-x86_64-linux-gnu.tar.gz", + "version": "24.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0/bitcoin-24.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "2cca490c1f2842884a3c5b0606f179f9f937177da4eadd628e3f7fd7e25d26d0", + "verification_source": "fb86cf6af7a10bc5f3ae6cd6a5b0348854e1462102fe71e755d30b51b6e317d1", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" diff --git a/configs/coins/bitcoin_testnet.json b/configs/coins/bitcoin_testnet.json index 8d4f2c4651..f201acc8ae 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": "23.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-23.0/bitcoin-23.0-x86_64-linux-gnu.tar.gz", + "version": "24.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0/bitcoin-24.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "2cca490c1f2842884a3c5b0606f179f9f937177da4eadd628e3f7fd7e25d26d0", + "verification_source": "fb86cf6af7a10bc5f3ae6cd6a5b0348854e1462102fe71e755d30b51b6e317d1", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" From c62ed158978971610ca796fb2b8a00b719455a1a Mon Sep 17 00:00:00 2001 From: David Hill Date: Mon, 28 Nov 2022 11:47:47 -0500 Subject: [PATCH 024/530] decred: Bump to v1.7.5 --- configs/coins/decred.json | 8 ++++---- configs/coins/decred_testnet.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/configs/coins/decred.json b/configs/coins/decred.json index 0b9ded8e67..d8e4e35fbe 100644 --- a/configs/coins/decred.json +++ b/configs/coins/decred.json @@ -22,10 +22,10 @@ "package_name": "backend-decred", "package_revision": "decred-1", "system_user": "decred", - "version": "1.6.0-rc3", - "binary_url": "https://github.com/decred/decred-binaries/releases/download/v1.6.0-rc3/decred-linux-amd64-v1.6.0-rc3.tar.gz", + "version": "1.7.5", + "binary_url": "https://github.com/decred/decred-binaries/releases/download/v1.7.5/decred-linux-amd64-v1.7.5.tar.gz", "verification_type": "sha256", - "verification_source": "42e588b80cf03eb69fff9a8fe0fedc81d8142404769c19143a3a8498008b46dd", + "verification_source": "8be1894e6e61e9d0392f158b16055b8cec81d96ec3d0725d3494bc0a306c362b", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/dcrd --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --appdata={{.Env.BackendDataPath}}/{{.Coin.Alias}} -C={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf", @@ -52,7 +52,7 @@ "additional_params": "-resyncindexperiod=300111 -resyncmempoolperiod=60111", "block_chain": { "parse": true, - "subversion":"/Decred dcrd:1.6.0-rc3", + "subversion":"/Decred dcrd:1.7.5", "mempool_workers": 8, "mempool_sub_workers": 2, "block_addresses_to_keep": 30, diff --git a/configs/coins/decred_testnet.json b/configs/coins/decred_testnet.json index cc0b8b4ae7..f9894b5fe0 100644 --- a/configs/coins/decred_testnet.json +++ b/configs/coins/decred_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-decred-testnet", "package_revision": "decred-testnet-1", "system_user": "decred", - "version": "1.6.0-rc3", - "binary_url": "https://github.com/decred/decred-binaries/releases/download/v1.6.0-rc3/decred-linux-amd64-v1.6.0-rc3.tar.gz", + "version": "1.7.5", + "binary_url": "https://github.com/decred/decred-binaries/releases/download/v1.7.5/decred-linux-amd64-v1.7.5.tar.gz", "verification_type": "sha256", - "verification_source": "42e588b80cf03eb69fff9a8fe0fedc81d8142404769c19143a3a8498008b46dd", + "verification_source": "8be1894e6e61e9d0392f158b16055b8cec81d96ec3d0725d3494bc0a306c362b", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/dcrd --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --rpcuser={{.IPC.RPCUser}} --rpcpass={{.IPC.RPCPass}} -C={{.Env.BackendDataPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf --nofilelogging --appdata={{.Env.BackendDataPath}}/{{.Coin.Alias}} --notls --txindex --addrindex --testnet --rpclisten=[127.0.0.1]:18061", @@ -52,7 +52,7 @@ "additional_params": "-resyncindexperiod=300111 -resyncmempoolperiod=60111", "block_chain": { "parse": true, - "subversion":"/Decred dcrd:1.6.0-rc3", + "subversion":"/Decred dcrd:1.7.5", "mempool_workers": 8, "mempool_sub_workers": 2, "block_addresses_to_keep": 30, From a7b621acb8d8a7db66d84aa4c9c641f6fe2ddbca Mon Sep 17 00:00:00 2001 From: JoHnY Date: Mon, 5 Dec 2022 10:00:57 +0000 Subject: [PATCH 025/530] =?UTF-8?q?zec=20(+testnet)=205.3.0=20=E2=86=92=20?= =?UTF-8?q?5.3.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/zcash.json | 6 +++--- configs/coins/zcash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index ed68a1c7a0..67a45f16fc 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,10 +22,10 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "5.3.0", - "binary_url": "https://z.cash/downloads/zcash-5.3.0-linux64-debian-bullseye.tar.gz", + "version": "5.3.1", + "binary_url": "https://z.cash/downloads/zcash-5.3.1-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "9e6683a2ee121adf27e0d47adcf6a35807266a5a107809c5999d3fe9acb763b7", + "verification_source": "2b6d3ad3cb6d962fe3bffe19fb4dea42e487fcdff9f4b36908f495cb8060022d", "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 d499da9300..659a4d4958 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -21,10 +21,10 @@ "backend": { "package_name": "backend-zcash-testnet", "package_revision": "satoshilabs-1", - "version": "5.3.0", - "binary_url": "https://z.cash/downloads/zcash-5.3.0-linux64-debian-bullseye.tar.gz", + "version": "5.3.1", + "binary_url": "https://z.cash/downloads/zcash-5.3.1-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "9e6683a2ee121adf27e0d47adcf6a35807266a5a107809c5999d3fe9acb763b7", + "verification_source": "2b6d3ad3cb6d962fe3bffe19fb4dea42e487fcdff9f4b36908f495cb8060022d", "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 ce123f125e940c45145b332a2302bd23aa9cf100 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 6 Dec 2022 10:30:34 +0000 Subject: [PATCH 026/530] =?UTF-8?q?bch=20(+testnet)=2024.1.0=20=E2=86=92?= =?UTF-8?q?=2025.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/bcash.json | 6 +++--- configs/coins/bcash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/bcash.json b/configs/coins/bcash.json index 3994a47954..837c259be2 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": "24.1.0", - "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v24.1.0/bitcoin-cash-node-24.1.0-x86_64-linux-gnu.tar.gz", + "version": "25.0.0", + "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v25.0.0/bitcoin-cash-node-25.0.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "857b6b95c54d84756fdd86893cd238a9b100c471a0b235aca4246cca74112ca9", + "verification_source": "f2383a35772544cf4c349429238e19b0771f0e61862726663fceea9d1e3ba4c2", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" diff --git a/configs/coins/bcash_testnet.json b/configs/coins/bcash_testnet.json index dd50644ca7..e325fa07b5 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": "24.1.0", - "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v24.1.0/bitcoin-cash-node-24.1.0-x86_64-linux-gnu.tar.gz", + "version": "25.0.0", + "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v25.0.0/bitcoin-cash-node-25.0.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "857b6b95c54d84756fdd86893cd238a9b100c471a0b235aca4246cca74112ca9", + "verification_source": "f2383a35772544cf4c349429238e19b0771f0e61862726663fceea9d1e3ba4c2", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" From 82c4a092e4a7dcf6c4e1302c109ab83a89315733 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 6 Dec 2022 11:04:39 +0000 Subject: [PATCH 027/530] =?UTF-8?q?zec=20(+testnet)=205.3.1=20=E2=86=92=20?= =?UTF-8?q?5.3.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/zcash.json | 6 +++--- configs/coins/zcash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index 67a45f16fc..368018424e 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,10 +22,10 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "5.3.1", - "binary_url": "https://z.cash/downloads/zcash-5.3.1-linux64-debian-bullseye.tar.gz", + "version": "5.3.2", + "binary_url": "https://z.cash/downloads/zcash-5.3.2-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "2b6d3ad3cb6d962fe3bffe19fb4dea42e487fcdff9f4b36908f495cb8060022d", + "verification_source": "20b0aa39b72826fe5c2d967151ce8cccbd11c1cf1b6c2adf8ddad0c596e241fc", "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 659a4d4958..1a924e7b6e 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -21,10 +21,10 @@ "backend": { "package_name": "backend-zcash-testnet", "package_revision": "satoshilabs-1", - "version": "5.3.1", - "binary_url": "https://z.cash/downloads/zcash-5.3.1-linux64-debian-bullseye.tar.gz", + "version": "5.3.2", + "binary_url": "https://z.cash/downloads/zcash-5.3.2-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "2b6d3ad3cb6d962fe3bffe19fb4dea42e487fcdff9f4b36908f495cb8060022d", + "verification_source": "20b0aa39b72826fe5c2d967151ce8cccbd11c1cf1b6c2adf8ddad0c596e241fc", "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 c9d68173b9687aa6436c8c26c271a14e649fbf71 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 13 Dec 2022 08:51:17 +0000 Subject: [PATCH 028/530] =?UTF-8?q?btc=20(+testnet)=2024.0=20=E2=86=92=202?= =?UTF-8?q?4.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/bitcoin.json | 6 +++--- configs/coins/bitcoin_regtest.json | 6 +++--- configs/coins/bitcoin_signet.json | 6 +++--- configs/coins/bitcoin_testnet.json | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index ca3705072e..569211f16f 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": "24.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0/bitcoin-24.0-x86_64-linux-gnu.tar.gz", + "version": "24.0.1", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0.1/bitcoin-24.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "fb86cf6af7a10bc5f3ae6cd6a5b0348854e1462102fe71e755d30b51b6e317d1", + "verification_source": "49df6e444515d457ea0b885d66f521f2a26ca92ccf73d5296082e633544253bf", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" diff --git a/configs/coins/bitcoin_regtest.json b/configs/coins/bitcoin_regtest.json index 8852063d07..b613ac2106 100644 --- a/configs/coins/bitcoin_regtest.json +++ b/configs/coins/bitcoin_regtest.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-regtest", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "24.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0/bitcoin-24.0-x86_64-linux-gnu.tar.gz", + "version": "24.0.1", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0.1/bitcoin-24.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "fb86cf6af7a10bc5f3ae6cd6a5b0348854e1462102fe71e755d30b51b6e317d1", + "verification_source": "49df6e444515d457ea0b885d66f521f2a26ca92ccf73d5296082e633544253bf", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" diff --git a/configs/coins/bitcoin_signet.json b/configs/coins/bitcoin_signet.json index 24e5506f44..95f7c4bb4a 100644 --- a/configs/coins/bitcoin_signet.json +++ b/configs/coins/bitcoin_signet.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-signet", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "24.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0/bitcoin-24.0-x86_64-linux-gnu.tar.gz", + "version": "24.0.1", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0.1/bitcoin-24.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "fb86cf6af7a10bc5f3ae6cd6a5b0348854e1462102fe71e755d30b51b6e317d1", + "verification_source": "49df6e444515d457ea0b885d66f521f2a26ca92ccf73d5296082e633544253bf", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" diff --git a/configs/coins/bitcoin_testnet.json b/configs/coins/bitcoin_testnet.json index f201acc8ae..4bf0586874 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": "24.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0/bitcoin-24.0-x86_64-linux-gnu.tar.gz", + "version": "24.0.1", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-24.0.1/bitcoin-24.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "fb86cf6af7a10bc5f3ae6cd6a5b0348854e1462102fe71e755d30b51b6e317d1", + "verification_source": "49df6e444515d457ea0b885d66f521f2a26ca92ccf73d5296082e633544253bf", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" From 97a3f4859170d647c314859a4b39d826dcec4473 Mon Sep 17 00:00:00 2001 From: gruve-p Date: Fri, 9 Dec 2022 18:27:11 +0100 Subject: [PATCH 029/530] Bump GRS to 24.0.1 --- configs/coins/groestlcoin.json | 6 +++--- configs/coins/groestlcoin_regtest.json | 12 +++++++++--- configs/coins/groestlcoin_signet.json | 6 +++--- configs/coins/groestlcoin_testnet.json | 6 +++--- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/configs/coins/groestlcoin.json b/configs/coins/groestlcoin.json index d50638a30a..ffb7fe1d3d 100644 --- a/configs/coins/groestlcoin.json +++ b/configs/coins/groestlcoin.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "23.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v23.0/groestlcoin-23.0-x86_64-linux-gnu.tar.gz", + "version": "24.0.1", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v24.0.1/groestlcoin-24.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "46ab078422d0d2aaf5b89ac9603cb61a6ebf6c26a73b9440365a4df5f9bce7de", + "verification_source": "4b69743190e2697d7b7772bf6f63cde595d590ff6664abf15a7201dab2a6098b", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/groestlcoin-qt" diff --git a/configs/coins/groestlcoin_regtest.json b/configs/coins/groestlcoin_regtest.json index bbfcfceb2e..e34d9736c1 100644 --- a/configs/coins/groestlcoin_regtest.json +++ b/configs/coins/groestlcoin_regtest.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin-regtest", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "23.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v23.0/groestlcoin-23.0-x86_64-linux-gnu.tar.gz", + "version": "24.0.1", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v24.0.1/groestlcoin-24.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "46ab078422d0d2aaf5b89ac9603cb61a6ebf6c26a73b9440365a4df5f9bce7de", + "verification_source": "4b69743190e2697d7b7772bf6f63cde595d590ff6664abf15a7201dab2a6098b", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/groestlcoin-qt" @@ -42,6 +42,12 @@ "additional_params": { "deprecatedrpc": "estimatefee", "whitelist": "127.0.0.1" + }, + "platforms": { + "arm64": { + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v24.0.1/groestlcoin-24.0.1-aarch64-linux-gnu.tar.gz", + "verification_source": "ca316c369728348406778c30b2b567bb2ede1ebcc87fb0305c0bed3dacae762b" + } } }, "blockbook": { diff --git a/configs/coins/groestlcoin_signet.json b/configs/coins/groestlcoin_signet.json index 08d401b4e8..9919b6ea08 100644 --- a/configs/coins/groestlcoin_signet.json +++ b/configs/coins/groestlcoin_signet.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin-signet", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "23.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v23.0/groestlcoin-23.0-x86_64-linux-gnu.tar.gz", + "version": "24.0.1", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v24.0.1/groestlcoin-24.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "46ab078422d0d2aaf5b89ac9603cb61a6ebf6c26a73b9440365a4df5f9bce7de", + "verification_source": "4b69743190e2697d7b7772bf6f63cde595d590ff6664abf15a7201dab2a6098b", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/groestlcoin-qt" diff --git a/configs/coins/groestlcoin_testnet.json b/configs/coins/groestlcoin_testnet.json index 8f13d5df31..a4d2139d77 100644 --- a/configs/coins/groestlcoin_testnet.json +++ b/configs/coins/groestlcoin_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin-testnet", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "23.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v23.0/groestlcoin-23.0-x86_64-linux-gnu.tar.gz", + "version": "24.0.1", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v24.0.1/groestlcoin-24.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "46ab078422d0d2aaf5b89ac9603cb61a6ebf6c26a73b9440365a4df5f9bce7de", + "verification_source": "4b69743190e2697d7b7772bf6f63cde595d590ff6664abf15a7201dab2a6098b", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/groestlcoin-qt" From 18d76c9753eadd14a2f89c7f2908ae98ab4a30ec Mon Sep 17 00:00:00 2001 From: JoHnY Date: Wed, 4 Jan 2023 09:09:11 +0000 Subject: [PATCH 030/530] =?UTF-8?q?dash=20(+testnet)=2018.1.0=20=E2=86=92?= =?UTF-8?q?=2018.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/dash.json | 6 +++--- configs/coins/dash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/dash.json b/configs/coins/dash.json index 72b33a0531..14f35a11ea 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": "18.1.0", - "binary_url": "https://github.com/dashpay/dash/releases/download/v18.1.0/dashcore-18.1.0-x86_64-linux-gnu.tar.gz", + "version": "18.2.0", + "binary_url": "https://github.com/dashpay/dash/releases/download/v18.2.0/dashcore-18.2.0-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v18.1.0/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v18.2.0/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" diff --git a/configs/coins/dash_testnet.json b/configs/coins/dash_testnet.json index 7cc9673ce2..f84d8409f9 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": "18.1.0", - "binary_url": "https://github.com/dashpay/dash/releases/download/v18.1.0/dashcore-18.1.0-x86_64-linux-gnu.tar.gz", + "version": "18.2.0", + "binary_url": "https://github.com/dashpay/dash/releases/download/v18.2.0/dashcore-18.2.0-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v18.1.0/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v18.2.0/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" From 2ce0c19227040bafc6d29f70a491b006ad2c3a54 Mon Sep 17 00:00:00 2001 From: vertiond Date: Sat, 31 Dec 2022 12:43:27 -0600 Subject: [PATCH 031/530] vtc (+testnet) 0.18.0 -> 22.1 --- configs/coins/vertcoin.json | 6 +++--- configs/coins/vertcoin_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/vertcoin.json b/configs/coins/vertcoin.json index b592b76a29..eb81187d4d 100644 --- a/configs/coins/vertcoin.json +++ b/configs/coins/vertcoin.json @@ -22,10 +22,10 @@ "package_name": "backend-vertcoin", "package_revision": "satoshilabs-1", "system_user": "vertcoin", - "version": "0.18.0", - "binary_url": "https://github.com/vertcoin-project/vertcoin-core/releases/download/0.18.0/vertcoin-0.18.0-x86_64-linux-gnu.tar.gz", + "version": "22.1", + "binary_url": "https://github.com/vertcoin-project/vertcoin-core/releases/download/v22.1/vertcoin-22.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "6ded7ea883b6cf9cee95701b13eef2e601a85f91d15f255d4fc7b25db92808ec", + "verification_source": "aab3068e02d55128326801cdbcbfcb175be96291e024edf5ab12f3af6f4433c0", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/vertcoin-qt" diff --git a/configs/coins/vertcoin_testnet.json b/configs/coins/vertcoin_testnet.json index 51f7590eef..680f30b705 100644 --- a/configs/coins/vertcoin_testnet.json +++ b/configs/coins/vertcoin_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-vertcoin-testnet", "package_revision": "satoshilabs-1", "system_user": "vertcoin", - "version": "0.18.0", - "binary_url": "https://github.com/vertcoin-project/vertcoin-core/releases/download/0.18.0/vertcoin-0.18.0-x86_64-linux-gnu.tar.gz", + "version": "22.1", + "binary_url": "https://github.com/vertcoin-project/vertcoin-core/releases/download/v22.1/vertcoin-22.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "6ded7ea883b6cf9cee95701b13eef2e601a85f91d15f255d4fc7b25db92808ec", + "verification_source": "aab3068e02d55128326801cdbcbfcb175be96291e024edf5ab12f3af6f4433c0", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/vertcoin-qt" From f47afff5b7cc8a5edba83d1e9708709e19b6bd51 Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Fri, 6 Jan 2023 15:08:43 +0100 Subject: [PATCH 032/530] Fix: typos Fix: typos --- tests/rpc/rpc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rpc/rpc.go b/tests/rpc/rpc.go index 63670a4d25..9cab43b2ca 100644 --- a/tests/rpc/rpc.go +++ b/tests/rpc/rpc.go @@ -356,7 +356,7 @@ func testGetBestBlockHeight(t *testing.T, h *TestHandler) { return } } - t.Error("GetBestBlockHeigh() didn't get the the best heigh") + t.Error("GetBestBlockHeight() didn't get the best height") } func testGetBlockHeader(t *testing.T, h *TestHandler) { From feee426e3b3e878d16ccee038a377d480714ab2d Mon Sep 17 00:00:00 2001 From: JoHnY Date: Mon, 9 Jan 2023 08:25:42 +0000 Subject: [PATCH 033/530] =?UTF-8?q?dash=20(+testnet)=2018.2.0=20=E2=86=92?= =?UTF-8?q?=2018.1.1=20(bugfix)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/dash.json | 6 +++--- configs/coins/dash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/dash.json b/configs/coins/dash.json index 14f35a11ea..0a2798fd56 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": "18.2.0", - "binary_url": "https://github.com/dashpay/dash/releases/download/v18.2.0/dashcore-18.2.0-x86_64-linux-gnu.tar.gz", + "version": "18.1.1", + "binary_url": "https://github.com/dashpay/dash/releases/download/v18.1.1/dashcore-18.1.1-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v18.2.0/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v18.1.1/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" diff --git a/configs/coins/dash_testnet.json b/configs/coins/dash_testnet.json index f84d8409f9..07fe15249e 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": "18.2.0", - "binary_url": "https://github.com/dashpay/dash/releases/download/v18.2.0/dashcore-18.2.0-x86_64-linux-gnu.tar.gz", + "version": "18.1.1", + "binary_url": "https://github.com/dashpay/dash/releases/download/v18.1.1/dashcore-18.1.1-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v18.2.0/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v18.1.1/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" From 86ff5a9538dba6b869f53850676f9edfc3cb5fa8 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Thu, 12 Jan 2023 07:54:51 +0000 Subject: [PATCH 034/530] =?UTF-8?q?bch=20(+testnet)=2025.0.0=20=E2=86=92?= =?UTF-8?q?=2026.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/bcash.json | 6 +++--- configs/coins/bcash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/bcash.json b/configs/coins/bcash.json index 837c259be2..fc24971724 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": "25.0.0", - "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v25.0.0/bitcoin-cash-node-25.0.0-x86_64-linux-gnu.tar.gz", + "version": "26.0.0", + "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v26.0.0/bitcoin-cash-node-26.0.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "f2383a35772544cf4c349429238e19b0771f0e61862726663fceea9d1e3ba4c2", + "verification_source": "e32e05fd63161f6f1fe717fca789448d2ee48e2017d3d4c6686b4222fe69497e", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" diff --git a/configs/coins/bcash_testnet.json b/configs/coins/bcash_testnet.json index e325fa07b5..e13b5ebc05 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": "25.0.0", - "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v25.0.0/bitcoin-cash-node-25.0.0-x86_64-linux-gnu.tar.gz", + "version": "26.0.0", + "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v26.0.0/bitcoin-cash-node-26.0.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "f2383a35772544cf4c349429238e19b0771f0e61862726663fceea9d1e3ba4c2", + "verification_source": "e32e05fd63161f6f1fe717fca789448d2ee48e2017d3d4c6686b4222fe69497e", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/bitcoin-qt" From 0ebbf16f18551f1c73b59bec6cfcbbdc96ec47e8 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Wed, 18 Jan 2023 10:22:49 +0000 Subject: [PATCH 035/530] =?UTF-8?q?dash=20(+testnet)=2018.1.1=20=E2=86=92?= =?UTF-8?q?=2018.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/dash.json | 6 +++--- configs/coins/dash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/dash.json b/configs/coins/dash.json index 0a2798fd56..414211897e 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": "18.1.1", - "binary_url": "https://github.com/dashpay/dash/releases/download/v18.1.1/dashcore-18.1.1-x86_64-linux-gnu.tar.gz", + "version": "18.2.1", + "binary_url": "https://github.com/dashpay/dash/releases/download/v18.2.1/dashcore-18.2.1-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v18.1.1/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v18.2.1/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" diff --git a/configs/coins/dash_testnet.json b/configs/coins/dash_testnet.json index 07fe15249e..be6f74f0e4 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": "18.1.1", - "binary_url": "https://github.com/dashpay/dash/releases/download/v18.1.1/dashcore-18.1.1-x86_64-linux-gnu.tar.gz", + "version": "18.2.1", + "binary_url": "https://github.com/dashpay/dash/releases/download/v18.2.1/dashcore-18.2.1-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v18.1.1/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v18.2.1/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" From 6acf8cc38a79dc91b8067f593aa8e4dc25e01275 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 10 Nov 2021 00:46:47 +0100 Subject: [PATCH 036/530] Create ethereum profiles for backend archive mode --- configs/coins/ethereum.json | 2 +- configs/coins/ethereum_archive.json | 65 +++++++++++++++++++ configs/coins/ethereum_testnet_ropsten.json | 3 +- .../ethereum_testnet_ropsten_archive.json | 62 ++++++++++++++++++ 4 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 configs/coins/ethereum_archive.json create mode 100644 configs/coins/ethereum_testnet_ropsten_archive.json diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 5d54383f0b..7c8c702103 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -27,7 +27,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.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 --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --http --http.port 8136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ipcdisable --syncmode full --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug\" --http --http.port 8136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json new file mode 100644 index 0000000000..17d258ef16 --- /dev/null +++ b/configs/coins/ethereum_archive.json @@ -0,0 +1,65 @@ +{ + "coin": { + "name": "Ethereum Archive", + "shortcut": "ETH", + "label": "Ethereum", + "alias": "ethereum_archive" + }, + "ports": { + "backend_rpc": 8016, + "backend_message_queue": 0, + "backend_p2p": 38316, + "backend_http": 8116, + "blockbook_internal": 9016, + "blockbook_public": 9116 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-ethereum-archive", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "1.10.12-6c4dc6c3", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.12-6c4dc6c3.tar.gz", + "verification_type": "gpg", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.12-6c4dc6c3.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 --gcmode archive --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 8116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-ethereum-archive", + "system_user": "blockbook-ethereum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "additional_params": { + "mempoolTxTimeoutHours": 48, + "processInternalTransactions": true, + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\", \"periodSeconds\": 60}" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 6ca99844b5..052e0f1c5e 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -26,7 +26,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -50,6 +50,7 @@ "block_addresses_to_keep": 3000, "additional_params": { "mempoolTxTimeoutHours": 12, + "processInternalTransactions": true, "queryBackendOnMempoolResync": false } } diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json new file mode 100644 index 0000000000..02ea50d802 --- /dev/null +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -0,0 +1,62 @@ +{ + "coin": { + "name": "Ethereum Testnet Ropsten Archive", + "shortcut": "tROP", + "label": "Ethereum Ropsten", + "alias": "ethereum_testnet_ropsten_archive" + }, + "ports": { + "backend_rpc": 18016, + "backend_message_queue": 0, + "backend_p2p": 48316, + "blockbook_internal": 19016, + "blockbook_public": 19116 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-ethereum-testnet-ropsten-archive", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "1.10.12-6c4dc6c3", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.12-6c4dc6c3.tar.gz", + "verification_type": "gpg", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.12-6c4dc6c3.tar.gz.asc", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-ethereum-testnet-ropsten-archive", + "system_user": "blockbook-ethereum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 3000, + "additional_params": { + "mempoolTxTimeoutHours": 12, + "processInternalTransactions": true, + "queryBackendOnMempoolResync": false + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} From 5818ce8aa284515099ced3f745741ea5e7cbeadb Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 8 Dec 2021 09:49:42 +0100 Subject: [PATCH 037/530] Ethereum: process call trace to extract internal transactions --- bchain/coins/eth/ethparser.go | 30 ++++++++--- bchain/coins/eth/ethrpc.go | 93 +++++++++++++++++++++++++++++++++-- bchain/types.go | 25 ++++++++++ 3 files changed, 137 insertions(+), 11 deletions(-) diff --git a/bchain/coins/eth/ethparser.go b/bchain/coins/eth/ethparser.go index 884b6e64e4..8526825d00 100644 --- a/bchain/coins/eth/ethparser.go +++ b/bchain/coins/eth/ethparser.go @@ -77,8 +77,9 @@ type rpcReceipt struct { } type completeTransaction struct { - Tx *rpcTransaction `json:"tx"` - Receipt *rpcReceipt `json:"receipt,omitempty"` + Tx *rpcTransaction `json:"tx"` + InternalData *bchain.EthereumInternalData `json:"internalData,omitempty"` + Receipt *rpcReceipt `json:"receipt,omitempty"` } type rpcBlockTransactions struct { @@ -96,7 +97,7 @@ func ethNumber(n string) (int64, error) { return 0, errors.Errorf("Not a number: '%v'", n) } -func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, blocktime int64, confirmations uint32, fixEIP55 bool) (*bchain.Tx, error) { +func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, internalData *bchain.EthereumInternalData, blocktime int64, confirmations uint32, fixEIP55 bool) (*bchain.Tx, error) { txid := tx.Hash var ( fa, ta []string @@ -121,9 +122,24 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, bloc } } } + if internalData != nil { + // ignore empty internal data + if internalData.Type == bchain.CALL && len(internalData.Transfers) == 0 { + internalData = nil + } else { + if fixEIP55 { + for i := range internalData.Transfers { + it := &internalData.Transfers[i] + it.From = EIP55AddressFromAddress(it.From) + it.To = EIP55AddressFromAddress(it.To) + } + } + } + } ct := completeTransaction{ - Tx: tx, - Receipt: receipt, + Tx: tx, + InternalData: internalData, + Receipt: receipt, } vs, err := hexutil.DecodeBig(tx.Value) if err != nil { @@ -254,6 +270,7 @@ func hexEncodeBig(b []byte) string { } // PackTx packs transaction to byte array +// completeTransaction.InternalData are not packed, they are stored in a different table func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { var err error var n uint64 @@ -396,7 +413,8 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { Logs: logs, } } - tx, err := p.ethTxToTx(&rt, rr, int64(pt.BlockTime), 0, false) + // TODO handle internal transactions + tx, err := p.ethTxToTx(&rt, rr, nil, int64(pt.BlockTime), 0, false) if err != nil { return nil, 0, err } diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 551c650cd0..8bb585aa5a 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -42,6 +42,7 @@ type Configuration struct { BlockAddressesToKeep int `json:"block_addresses_to_keep"` MempoolTxTimeoutHours int `json:"mempoolTxTimeoutHours"` QueryBackendOnMempoolResync bool `json:"queryBackendOnMempoolResync"` + ProcessInternalTransactions bool `json:"processInternalTransactions"` } // EthereumRPC is an interface to JSON-RPC eth service. @@ -157,11 +158,9 @@ func (b *EthereumRPC) Initialize() error { case MainNet: b.Testnet = false b.Network = "livenet" - break case TestNet: b.Testnet = true b.Network = "testnet" - break case TestNetGoerli: b.Testnet = true b.Network = "goerli" @@ -511,6 +510,83 @@ func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]* return r, nil } +type rpcCallTrace struct { + // CREATE, CREATE2, SELFDESTRUCT, CALL, CALLCODE, DELEGATECALL, STATICCALL + Type string `json:"type"` + From string `json:"from"` + To string `json:"to"` + Value string `json:"value"` + Error string `json:"error"` + Calls []rpcCallTrace `json:"calls"` +} + +type rpcTraceResult struct { + Result rpcCallTrace `json:"result"` +} + +func (b *EthereumRPC) processCallTrace(call rpcCallTrace, d *bchain.EthereumInternalData) { + value, err := hexutil.DecodeBig(call.Value) + if call.Type == "CREATE" { + d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ + Type: bchain.CREATE, + Value: *value, + From: call.From, + To: call.To, + }) + + } else if call.Type == "SELFDESTRUCT" { + d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ + Type: bchain.SELFDESTRUCT, + Value: *value, + From: call.From, + To: call.To, + }) + } else if err == nil && value.BitLen() > 0 { + d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ + Value: *value, + From: call.From, + To: call.To, + }) + } + for i := range call.Calls { + b.processCallTrace(call.Calls[i], d) + } +} + +// getInternalDataForBlock fetches debug trace using callTracer, extracts internal transfers and creations and destructions of contracts +// by design, it never returns error so that missing internal transactions do not stop the rest of the blockchain import +func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []rpcTransaction) ([]bchain.EthereumInternalData, error) { + data := make([]bchain.EthereumInternalData, len(transactions)) + if b.ChainConfig.ProcessInternalTransactions { + ctx, cancel := context.WithTimeout(context.Background(), b.timeout) + defer cancel() + var trace []rpcTraceResult + err := b.rpc.CallContext(ctx, &trace, "debug_traceBlockByHash", blockHash, map[string]interface{}{"tracer": "callTracer"}) + if err != nil { + glog.Error("debug_traceBlockByHash block ", blockHash, ", error ", err) + return data, nil + } + if len(trace) != len(data) { + glog.Error("debug_traceBlockByHash block ", blockHash, ", error: trace length does not match block length ", len(trace), "!=", len(data)) + return data, nil + } + for i, result := range trace { + r := &result.Result + d := &data[i] + if r.Type == "CREATE" { + d.Type = bchain.CREATE + d.Contract = r.To + } else if r.Type == "SELFDESTRUCT" { + d.Type = bchain.SELFDESTRUCT + } + for j := range r.Calls { + b.processCallTrace(r.Calls[j], d) + } + } + } + return data, nil +} + // GetBlock returns block with given hash or height, hash has precedence if both passed func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { raw, err := b.getBlockRaw(hash, height, true) @@ -534,10 +610,16 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error if err != nil { return nil, err } + + internalData, err := b.getInternalDataForBlock(head.Hash, body.Transactions) + if err != nil { + return nil, err + } + btxs := make([]bchain.Tx, len(body.Transactions)) for i := range body.Transactions { tx := &body.Transactions[i] - btx, err := b.Parser.ethTxToTx(tx, &rpcReceipt{Logs: logs[tx.Hash]}, bbh.Time, uint32(bbh.Confirmations), true) + btx, err := b.Parser.ethTxToTx(tx, &rpcReceipt{Logs: logs[tx.Hash]}, &internalData[i], bbh.Time, uint32(bbh.Confirmations), true) if err != nil { return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash) } @@ -603,7 +685,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { var btx *bchain.Tx if tx.BlockNumber == "" { // mempool tx - btx, err = b.Parser.ethTxToTx(tx, nil, 0, 0, true) + btx, err = b.Parser.ethTxToTx(tx, nil, nil, 0, 0, true) if err != nil { return nil, errors.Annotatef(err, "txid %v", txid) } @@ -636,7 +718,8 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { if err != nil { return nil, errors.Annotatef(err, "txid %v", txid) } - btx, err = b.Parser.ethTxToTx(tx, &receipt, time, confirmations, true) + // TODO - handle internal tx + btx, err = b.Parser.ethTxToTx(tx, &receipt, nil, time, confirmations, true) if err != nil { return nil, errors.Annotatef(err, "txid %v", txid) } diff --git a/bchain/types.go b/bchain/types.go index 29d8f2b256..7c96f773ab 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -202,6 +202,31 @@ func AddressDescriptorFromString(s string) (AddressDescriptor, error) { // EthereumType specific +// EthereumInternalTransfer contains data about internal transfer +type EthereumInternalTransfer struct { + Type EthereumInternalTransactionType `json:"type"` + From string `json:"from"` + To string `json:"to"` + Value big.Int `json:"value"` +} + +// EthereumInternalTransactionType - type of ethereum transaction from internal data +type EthereumInternalTransactionType int + +// EthereumInternalTransactionType enumeration +const ( + CALL = EthereumInternalTransactionType(iota) + CREATE + SELFDESTRUCT +) + +// EthereumInternalTransaction contains internal transfers +type EthereumInternalData struct { + Type EthereumInternalTransactionType `json:"type"` + Contract string `json:"contract,omitempty"` + Transfers []EthereumInternalTransfer `json:"transfers,omitempty"` +} + // Erc20Contract contains info about ERC20 contract type Erc20Contract struct { Contract string `json:"contract"` From c374ef86fd2801d341d556b9e200cf33b0395996 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 10 Dec 2021 00:30:27 +0100 Subject: [PATCH 038/530] Update types in preparation for eth internal transactions, bump dbVersion to 6 --- api/types.go | 14 ++-- bchain/coins/eth/erc20.go | 4 +- bchain/coins/eth/erc20_test.go | 12 +-- bchain/coins/eth/ethparser.go | 62 ++++----------- bchain/coins/eth/ethparser_test.go | 36 ++++----- bchain/coins/eth/ethrpc.go | 18 ++--- bchain/types.go | 43 ----------- bchain/types_ethereumtype.go | 85 +++++++++++++++++++++ db/rocksdb.go | 6 +- db/rocksdb_ethereumtype.go | 8 +- db/rocksdb_ethereumtype_test.go | 24 +++--- docs/rocksdb.md | 4 +- tests/dbtestdata/dbtestdata_ethereumtype.go | 23 +++++- 13 files changed, 185 insertions(+), 154 deletions(-) create mode 100644 bchain/types_ethereumtype.go diff --git a/api/types.go b/api/types.go index eb0fad76aa..ce1ecd475a 100644 --- a/api/types.go +++ b/api/types.go @@ -173,12 +173,14 @@ type TokenTransfer struct { // EthereumSpecific contains ethereum specific transaction data type EthereumSpecific struct { - Status eth.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending - Nonce uint64 `json:"nonce"` - GasLimit *big.Int `json:"gasLimit"` - GasUsed *big.Int `json:"gasUsed"` - GasPrice *Amount `json:"gasPrice"` - Data string `json:"data,omitempty"` + TxType string `json:"txType,omitempty"` + Status eth.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending + Nonce uint64 `json:"nonce"` + GasLimit *big.Int `json:"gasLimit"` + GasUsed *big.Int `json:"gasUsed"` + GasPrice *Amount `json:"gasPrice"` + Data string `json:"data,omitempty"` + InternalTransfers []bchain.EthereumInternalTransfer `json:"internalTransfers,omitempty"` } // Tx holds information about a transaction diff --git a/bchain/coins/eth/erc20.go b/bchain/coins/eth/erc20.go index d660511a70..6971f70728 100644 --- a/bchain/coins/eth/erc20.go +++ b/bchain/coins/eth/erc20.go @@ -56,7 +56,7 @@ func addressFromPaddedHex(s string) (string, error) { return a.String(), nil } -func erc20GetTransfersFromLog(logs []*rpcLog) ([]bchain.Erc20Transfer, error) { +func erc20GetTransfersFromLog(logs []*bchain.RpcLog) ([]bchain.Erc20Transfer, error) { var r []bchain.Erc20Transfer for _, l := range logs { if len(l.Topics) == 3 && l.Topics[0] == erc20TransferEventSignature { @@ -84,7 +84,7 @@ func erc20GetTransfersFromLog(logs []*rpcLog) ([]bchain.Erc20Transfer, error) { return r, nil } -func erc20GetTransfersFromTx(tx *rpcTransaction) ([]bchain.Erc20Transfer, error) { +func erc20GetTransfersFromTx(tx *bchain.RpcTransaction) ([]bchain.Erc20Transfer, error) { var r []bchain.Erc20Transfer if len(tx.Payload) == 128+len(erc20TransferMethodSignature) && strings.HasPrefix(tx.Payload, erc20TransferMethodSignature) { to, err := addressFromPaddedHex(tx.Payload[len(erc20TransferMethodSignature) : 64+len(erc20TransferMethodSignature)]) diff --git a/bchain/coins/eth/erc20_test.go b/bchain/coins/eth/erc20_test.go index f0a584f969..574144d498 100644 --- a/bchain/coins/eth/erc20_test.go +++ b/bchain/coins/eth/erc20_test.go @@ -15,13 +15,13 @@ import ( func TestErc20_erc20GetTransfersFromLog(t *testing.T) { tests := []struct { name string - args []*rpcLog + args []*bchain.RpcLog want []bchain.Erc20Transfer wantErr bool }{ { name: "1", - args: []*rpcLog{ + args: []*bchain.RpcLog{ { Address: "0x76a45e8976499ab9ae223cc584019341d5a84e96", Topics: []string{ @@ -43,7 +43,7 @@ func TestErc20_erc20GetTransfersFromLog(t *testing.T) { }, { name: "2", - args: []*rpcLog{ + args: []*bchain.RpcLog{ { // Transfer Address: "0x0d0f936ee4c93e25944694d6c121de94d9760f11", Topics: []string{ @@ -167,17 +167,17 @@ func TestErc20_erc20GetTransfersFromTx(t *testing.T) { bn, _ := new(big.Int).SetString("21e19e0c9bab2400000", 16) tests := []struct { name string - args *rpcTransaction + args *bchain.RpcTransaction want []bchain.Erc20Transfer }{ { name: "0", - args: (b.Txs[0].CoinSpecificData.(completeTransaction)).Tx, + args: (b.Txs[0].CoinSpecificData.(bchain.EthereumSpecificData)).Tx, want: []bchain.Erc20Transfer{}, }, { name: "1", - args: (b.Txs[1].CoinSpecificData.(completeTransaction)).Tx, + args: (b.Txs[1].CoinSpecificData.(bchain.EthereumSpecificData)).Tx, want: []bchain.Erc20Transfer{ { Contract: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2", diff --git a/bchain/coins/eth/ethparser.go b/bchain/coins/eth/ethparser.go index 8526825d00..8b8bdf1d94 100644 --- a/bchain/coins/eth/ethparser.go +++ b/bchain/coins/eth/ethparser.go @@ -41,49 +41,13 @@ type rpcHeader struct { Nonce string `json:"nonce"` } -type rpcTransaction struct { - AccountNonce string `json:"nonce"` - GasPrice string `json:"gasPrice"` - GasLimit string `json:"gas"` - To string `json:"to"` // nil means contract creation - Value string `json:"value"` - Payload string `json:"input"` - Hash string `json:"hash"` - BlockNumber string `json:"blockNumber"` - BlockHash string `json:"blockHash,omitempty"` - From string `json:"from"` - TransactionIndex string `json:"transactionIndex"` - // Signature values - ignored - // V string `json:"v"` - // R string `json:"r"` - // S string `json:"s"` -} - -type rpcLog struct { - Address string `json:"address"` - Topics []string `json:"topics"` - Data string `json:"data"` -} - type rpcLogWithTxHash struct { - rpcLog + bchain.RpcLog Hash string `json:"transactionHash"` } -type rpcReceipt struct { - GasUsed string `json:"gasUsed"` - Status string `json:"status"` - Logs []*rpcLog `json:"logs"` -} - -type completeTransaction struct { - Tx *rpcTransaction `json:"tx"` - InternalData *bchain.EthereumInternalData `json:"internalData,omitempty"` - Receipt *rpcReceipt `json:"receipt,omitempty"` -} - type rpcBlockTransactions struct { - Transactions []rpcTransaction `json:"transactions"` + Transactions []bchain.RpcTransaction `json:"transactions"` } type rpcBlockTxids struct { @@ -97,7 +61,7 @@ func ethNumber(n string) (int64, error) { return 0, errors.Errorf("Not a number: '%v'", n) } -func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, internalData *bchain.EthereumInternalData, blocktime int64, confirmations uint32, fixEIP55 bool) (*bchain.Tx, error) { +func (p *EthereumParser) ethTxToTx(tx *bchain.RpcTransaction, receipt *bchain.RpcReceipt, internalData *bchain.EthereumInternalData, blocktime int64, confirmations uint32, fixEIP55 bool) (*bchain.Tx, error) { txid := tx.Hash var ( fa, ta []string @@ -136,7 +100,7 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, inte } } } - ct := completeTransaction{ + ct := bchain.EthereumSpecificData{ Tx: tx, InternalData: internalData, Receipt: receipt, @@ -274,7 +238,7 @@ func hexEncodeBig(b []byte) string { func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { var err error var n uint64 - r, ok := tx.CoinSpecificData.(completeTransaction) + r, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData) if !ok { return nil, errors.New("Missing CoinSpecificData") } @@ -373,7 +337,7 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { if err != nil { return nil, 0, err } - rt := rpcTransaction{ + rt := bchain.RpcTransaction{ AccountNonce: hexutil.EncodeUint64(pt.Tx.AccountNonce), BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)), From: EIP55Address(pt.Tx.From), @@ -388,15 +352,15 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)), Value: hexEncodeBig(pt.Tx.Value), } - var rr *rpcReceipt + var rr *bchain.RpcReceipt if pt.Receipt != nil { - logs := make([]*rpcLog, len(pt.Receipt.Log)) + logs := make([]*bchain.RpcLog, len(pt.Receipt.Log)) for i, l := range pt.Receipt.Log { topics := make([]string, len(l.Topics)) for j, t := range l.Topics { topics[j] = hexutil.Encode(t) } - logs[i] = &rpcLog{ + logs[i] = &bchain.RpcLog{ Address: EIP55Address(l.Address), Data: hexutil.Encode(l.Data), Topics: topics, @@ -407,7 +371,7 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { if len(pt.Receipt.Status) != 1 || pt.Receipt.Status[0] != 'U' { status = hexEncodeBig(pt.Receipt.Status) } - rr = &rpcReceipt{ + rr = &bchain.RpcReceipt{ GasUsed: hexEncodeBig(pt.Receipt.GasUsed), Status: status, Logs: logs, @@ -460,7 +424,7 @@ func (p *EthereumParser) GetChainType() bchain.ChainType { // GetHeightFromTx returns ethereum specific data from bchain.Tx func GetHeightFromTx(tx *bchain.Tx) (uint32, error) { var bn string - csd, ok := tx.CoinSpecificData.(completeTransaction) + csd, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData) if !ok { return 0, errors.New("Missing CoinSpecificData") } @@ -476,7 +440,7 @@ func GetHeightFromTx(tx *bchain.Tx) (uint32, error) { func (p *EthereumParser) EthereumTypeGetErc20FromTx(tx *bchain.Tx) ([]bchain.Erc20Transfer, error) { var r []bchain.Erc20Transfer var err error - csd, ok := tx.CoinSpecificData.(completeTransaction) + csd, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData) if ok { if csd.Receipt != nil { r, err = erc20GetTransfersFromLog(csd.Receipt.Logs) @@ -519,7 +483,7 @@ func GetEthereumTxData(tx *bchain.Tx) *EthereumTxData { // GetEthereumTxDataFromSpecificData returns EthereumTxData from coinSpecificData func GetEthereumTxDataFromSpecificData(coinSpecificData interface{}) *EthereumTxData { etd := EthereumTxData{Status: TxStatusPending} - csd, ok := coinSpecificData.(completeTransaction) + csd, ok := coinSpecificData.(bchain.EthereumSpecificData) if ok { if csd.Tx != nil { etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce) diff --git a/bchain/coins/eth/ethparser_test.go b/bchain/coins/eth/ethparser_test.go index a9e29703ea..c990343cd7 100644 --- a/bchain/coins/eth/ethparser_test.go +++ b/bchain/coins/eth/ethparser_test.go @@ -89,8 +89,8 @@ func init() { }, }, }, - CoinSpecificData: completeTransaction{ - Tx: &rpcTransaction{ + CoinSpecificData: bchain.EthereumSpecificData{ + Tx: &bchain.RpcTransaction{ AccountNonce: "0xb26c", GasPrice: "0x430e23400", GasLimit: "0x5208", @@ -102,10 +102,10 @@ func init() { From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97", TransactionIndex: "0xa", }, - Receipt: &rpcReceipt{ + Receipt: &bchain.RpcReceipt{ GasUsed: "0x5208", Status: "0x1", - Logs: []*rpcLog{}, + Logs: []*bchain.RpcLog{}, }, }, } @@ -127,8 +127,8 @@ func init() { }, }, }, - CoinSpecificData: completeTransaction{ - Tx: &rpcTransaction{ + CoinSpecificData: bchain.EthereumSpecificData{ + Tx: &bchain.RpcTransaction{ AccountNonce: "0xd0", GasPrice: "0x9502f9000", GasLimit: "0x130d5", @@ -139,10 +139,10 @@ func init() { BlockNumber: "0x41eee8", From: "0x20cD153de35D469BA46127A0C8F18626b59a256A", TransactionIndex: "0x0"}, - Receipt: &rpcReceipt{ + Receipt: &bchain.RpcReceipt{ GasUsed: "0xcb39", Status: "0x1", - Logs: []*rpcLog{ + Logs: []*bchain.RpcLog{ { Address: "0x4af4114F73d1c1C903aC9E0361b379D1291808A2", Data: "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", @@ -174,8 +174,8 @@ func init() { }, }, }, - CoinSpecificData: completeTransaction{ - Tx: &rpcTransaction{ + CoinSpecificData: bchain.EthereumSpecificData{ + Tx: &bchain.RpcTransaction{ AccountNonce: "0xb26c", GasPrice: "0x430e23400", GasLimit: "0x5208", @@ -187,10 +187,10 @@ func init() { From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97", TransactionIndex: "0xa", }, - Receipt: &rpcReceipt{ + Receipt: &bchain.RpcReceipt{ GasUsed: "0x5208", Status: "0x0", - Logs: []*rpcLog{}, + Logs: []*bchain.RpcLog{}, }, }, } @@ -212,8 +212,8 @@ func init() { }, }, }, - CoinSpecificData: completeTransaction{ - Tx: &rpcTransaction{ + CoinSpecificData: bchain.EthereumSpecificData{ + Tx: &bchain.RpcTransaction{ AccountNonce: "0xb26c", GasPrice: "0x430e23400", GasLimit: "0x5208", @@ -225,10 +225,10 @@ func init() { From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97", TransactionIndex: "0xa", }, - Receipt: &rpcReceipt{ + Receipt: &bchain.RpcReceipt{ GasUsed: "0x5208", Status: "", - Logs: []*rpcLog{}, + Logs: []*bchain.RpcLog{}, }, }, } @@ -351,8 +351,8 @@ func TestEthereumParser_UnpackTx(t *testing.T) { return } // DeepEqual has problems with pointers in completeTransaction - gs := got.CoinSpecificData.(completeTransaction) - ws := tt.want.CoinSpecificData.(completeTransaction) + gs := got.CoinSpecificData.(bchain.EthereumSpecificData) + ws := tt.want.CoinSpecificData.(bchain.EthereumSpecificData) gc := *got wc := *tt.want gc.CoinSpecificData = nil diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 8bb585aa5a..da3e02d8d6 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -490,7 +490,7 @@ func (b *EthereumRPC) getBlockRaw(hash string, height uint32, fullTxs bool) (jso return raw, nil } -func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]*rpcLog, error) { +func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]*bchain.RpcLog, error) { ctx, cancel := context.WithTimeout(context.Background(), b.timeout) defer cancel() var logs []rpcLogWithTxHash @@ -502,10 +502,10 @@ func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]* if err != nil { return nil, errors.Annotatef(err, "blockNumber %v", blockNumber) } - r := make(map[string][]*rpcLog) + r := make(map[string][]*bchain.RpcLog) for i := range logs { l := &logs[i] - r[l.Hash] = append(r[l.Hash], &l.rpcLog) + r[l.Hash] = append(r[l.Hash], &l.RpcLog) } return r, nil } @@ -555,7 +555,7 @@ func (b *EthereumRPC) processCallTrace(call rpcCallTrace, d *bchain.EthereumInte // getInternalDataForBlock fetches debug trace using callTracer, extracts internal transfers and creations and destructions of contracts // by design, it never returns error so that missing internal transactions do not stop the rest of the blockchain import -func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []rpcTransaction) ([]bchain.EthereumInternalData, error) { +func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []bchain.RpcTransaction) ([]bchain.EthereumInternalData, error) { data := make([]bchain.EthereumInternalData, len(transactions)) if b.ChainConfig.ProcessInternalTransactions { ctx, cancel := context.WithTimeout(context.Background(), b.timeout) @@ -619,7 +619,7 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error btxs := make([]bchain.Tx, len(body.Transactions)) for i := range body.Transactions { tx := &body.Transactions[i] - btx, err := b.Parser.ethTxToTx(tx, &rpcReceipt{Logs: logs[tx.Hash]}, &internalData[i], bbh.Time, uint32(bbh.Confirmations), true) + btx, err := b.Parser.ethTxToTx(tx, &bchain.RpcReceipt{Logs: logs[tx.Hash]}, &internalData[i], bbh.Time, uint32(bbh.Confirmations), true) if err != nil { return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash) } @@ -671,7 +671,7 @@ func (b *EthereumRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { ctx, cancel := context.WithTimeout(context.Background(), b.timeout) defer cancel() - var tx *rpcTransaction + var tx *bchain.RpcTransaction hash := ethcommon.HexToHash(txid) err := b.rpc.CallContext(ctx, &tx, "eth_getTransactionByHash", hash) if err != nil { @@ -705,7 +705,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { if time, err = ethNumber(ht.Time); err != nil { return nil, errors.Annotatef(err, "txid %v", txid) } - var receipt rpcReceipt + var receipt bchain.RpcReceipt err = b.rpc.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash) if err != nil { return nil, errors.Annotatef(err, "txid %v", txid) @@ -733,13 +733,13 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { // GetTransactionSpecific returns json as returned by backend, with all coin specific data func (b *EthereumRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) { - csd, ok := tx.CoinSpecificData.(completeTransaction) + csd, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData) if !ok { ntx, err := b.GetTransaction(tx.Txid) if err != nil { return nil, err } - csd, ok = ntx.CoinSpecificData.(completeTransaction) + csd, ok = ntx.CoinSpecificData.(bchain.EthereumSpecificData) if !ok { return nil, errors.New("Cannot get CoinSpecificData") } diff --git a/bchain/types.go b/bchain/types.go index 7c96f773ab..555db84a84 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -200,49 +200,6 @@ func AddressDescriptorFromString(s string) (AddressDescriptor, error) { return nil, errors.New("invalid address descriptor") } -// EthereumType specific - -// EthereumInternalTransfer contains data about internal transfer -type EthereumInternalTransfer struct { - Type EthereumInternalTransactionType `json:"type"` - From string `json:"from"` - To string `json:"to"` - Value big.Int `json:"value"` -} - -// EthereumInternalTransactionType - type of ethereum transaction from internal data -type EthereumInternalTransactionType int - -// EthereumInternalTransactionType enumeration -const ( - CALL = EthereumInternalTransactionType(iota) - CREATE - SELFDESTRUCT -) - -// EthereumInternalTransaction contains internal transfers -type EthereumInternalData struct { - Type EthereumInternalTransactionType `json:"type"` - Contract string `json:"contract,omitempty"` - Transfers []EthereumInternalTransfer `json:"transfers,omitempty"` -} - -// Erc20Contract contains info about ERC20 contract -type Erc20Contract struct { - Contract string `json:"contract"` - Name string `json:"name"` - Symbol string `json:"symbol"` - Decimals int `json:"decimals"` -} - -// Erc20Transfer contains a single ERC20 token transfer -type Erc20Transfer struct { - Contract string - From string - To string - Tokens big.Int -} - // MempoolTxidEntry contains mempool txid with first seen time type MempoolTxidEntry struct { Txid string diff --git a/bchain/types_ethereumtype.go b/bchain/types_ethereumtype.go new file mode 100644 index 0000000000..94e4ecc27e --- /dev/null +++ b/bchain/types_ethereumtype.go @@ -0,0 +1,85 @@ +package bchain + +import "math/big" + +// EthereumType specific + +// EthereumInternalTransfer contains data about internal transfer +type EthereumInternalTransfer struct { + Type EthereumInternalTransactionType `json:"type"` + From string `json:"from"` + To string `json:"to"` + Value big.Int `json:"value"` +} + +// EthereumInternalTransactionType - type of ethereum transaction from internal data +type EthereumInternalTransactionType int + +// EthereumInternalTransactionType enumeration +const ( + CALL = EthereumInternalTransactionType(iota) + CREATE + SELFDESTRUCT +) + +// EthereumInternalTransaction contains internal transfers +type EthereumInternalData struct { + Type EthereumInternalTransactionType `json:"type"` + Contract string `json:"contract,omitempty"` + Transfers []EthereumInternalTransfer `json:"transfers,omitempty"` +} + +// Erc20Contract contains info about ERC20 contract +type Erc20Contract struct { + Contract string `json:"contract"` + Name string `json:"name"` + Symbol string `json:"symbol"` + Decimals int `json:"decimals"` +} + +// Erc20Transfer contains a single ERC20 token transfer +type Erc20Transfer struct { + Contract string + From string + To string + Tokens big.Int +} + +// RpcTransaction is returned by eth_getTransactionByHash +type RpcTransaction struct { + AccountNonce string `json:"nonce"` + GasPrice string `json:"gasPrice"` + GasLimit string `json:"gas"` + To string `json:"to"` // nil means contract creation + Value string `json:"value"` + Payload string `json:"input"` + Hash string `json:"hash"` + BlockNumber string `json:"blockNumber"` + BlockHash string `json:"blockHash,omitempty"` + From string `json:"from"` + TransactionIndex string `json:"transactionIndex"` + // Signature values - ignored + // V string `json:"v"` + // R string `json:"r"` + // S string `json:"s"` +} + +// RpcLog is returned by eth_getLogs +type RpcLog struct { + Address string `json:"address"` + Topics []string `json:"topics"` + Data string `json:"data"` +} + +// RpcLog is returned by eth_getTransactionReceipt +type RpcReceipt struct { + GasUsed string `json:"gasUsed"` + Status string `json:"status"` + Logs []*RpcLog `json:"logs"` +} + +type EthereumSpecificData struct { + Tx *RpcTransaction `json:"tx"` + InternalData *EthereumInternalData `json:"internalData,omitempty"` + Receipt *RpcReceipt `json:"receipt,omitempty"` +} diff --git a/db/rocksdb.go b/db/rocksdb.go index a03a2a1e62..59b90c9a70 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -22,7 +22,7 @@ import ( "github.com/trezor/blockbook/common" ) -const dbVersion = 5 +const dbVersion = 6 const packedHeightBytes = 4 const maxAddrDescLen = 1024 @@ -112,6 +112,8 @@ const ( cfTxAddresses // EthereumType cfAddressContracts = cfAddressBalance + cfInternalData + cfContracts ) // common columns @@ -120,7 +122,7 @@ var cfBaseNames = []string{"default", "height", "addresses", "blockTxs", "transa // type specific columns var cfNamesBitcoinType = []string{"addressBalance", "txAddresses"} -var cfNamesEthereumType = []string{"addressContracts"} +var cfNamesEthereumType = []string{"addressContracts", "internalData", "contracts"} func openDB(path string, c *gorocksdb.Cache, openFiles int) (*gorocksdb.DB, []*gorocksdb.ColumnFamilyHandle, error) { // opts with bloom filter diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index f1df45ed2c..e63b144ca8 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -22,6 +22,7 @@ type AddrContract struct { type AddrContracts struct { TotalTxs uint NonContractTxs uint + InternalTxs uint Contracts []AddrContract } @@ -30,7 +31,7 @@ func (d *RocksDB) storeAddressContracts(wb *gorocksdb.WriteBatch, acm map[string varBuf := make([]byte, vlq.MaxLen64) for addrDesc, acs := range acm { // address with 0 contracts is removed from db - happens on disconnect - if acs == nil || (acs.NonContractTxs == 0 && len(acs.Contracts) == 0) { + if acs == nil || (acs.NonContractTxs == 0 && acs.InternalTxs == 0 && len(acs.Contracts) == 0) { wb.DeleteCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc)) } else { buf = buf[:0] @@ -38,6 +39,8 @@ func (d *RocksDB) storeAddressContracts(wb *gorocksdb.WriteBatch, acm map[string buf = append(buf, varBuf[:l]...) l = packVaruint(acs.NonContractTxs, varBuf) buf = append(buf, varBuf[:l]...) + l = packVaruint(acs.InternalTxs, varBuf) + buf = append(buf, varBuf[:l]...) for _, ac := range acs.Contracts { buf = append(buf, ac.Contract...) l = packVaruint(ac.Txs, varBuf) @@ -64,6 +67,8 @@ func (d *RocksDB) GetAddrDescContracts(addrDesc bchain.AddressDescriptor) (*Addr buf = buf[l:] nct, l := unpackVaruint(buf) buf = buf[l:] + ict, l := unpackVaruint(buf) + buf = buf[l:] c := make([]AddrContract, 0, 4) for len(buf) > 0 { if len(buf) < eth.EthereumTypeAddressDescriptorLen { @@ -80,6 +85,7 @@ func (d *RocksDB) GetAddrDescContracts(addrDesc bchain.AddressDescriptor) (*Addr return &AddrContracts{ TotalTxs: tt, NonContractTxs: nct, + InternalTxs: ict, Contracts: c, }, nil } diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index 9819ec5d35..55af30ff9e 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -44,10 +44,10 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo } if err := checkColumn(d, cfAddressContracts, []keyPair{ - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "0101", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "0201" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "0101" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "0101", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "010100", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "020100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010100", nil}, }); err != nil { { t.Fatal(err) @@ -113,14 +113,14 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) { } if err := checkColumn(d, cfAddressContracts, []keyPair{ - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "0101", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "0402" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "0101" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "0101", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "0101", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), "0101" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser), "0100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser), "0101", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "010100", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "040200" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010100", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "010100", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser), "010000" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser), "010100", nil}, }); err != nil { { t.Fatal(err) diff --git a/docs/rocksdb.md b/docs/rocksdb.md index 4301b0f7cf..3860a70b3d 100644 --- a/docs/rocksdb.md +++ b/docs/rocksdb.md @@ -84,9 +84,9 @@ Column families used only by **Ethereum type** coins: - **addressContracts** (used only by Ethereum type coins) - Maps *addrDesc* to *total number of transactions*, *number of non contract transactions* and array of *contracts* with *number of transfers* of given address. + Maps *addrDesc* to *total number of transactions*, *number of non contract transactions*, *number of internal transactions* and array of *contracts* with *number of transfers* of given address. ``` - (addrDesc []byte) -> (total_txs vuint)+(non-contract_txs vuint)+[]((contractAddrDesc []byte)+(nr_transfers vuint)) + (addrDesc []byte) -> (total_txs vuint)+(non-contract_txs vuint)+(internal_txs vuint)+[]((contractAddrDesc []byte)+(nr_transfers vuint)) ``` - **blockTxs** diff --git a/tests/dbtestdata/dbtestdata_ethereumtype.go b/tests/dbtestdata/dbtestdata_ethereumtype.go index 085eb7647f..cebc9cb265 100644 --- a/tests/dbtestdata/dbtestdata_ethereumtype.go +++ b/tests/dbtestdata/dbtestdata_ethereumtype.go @@ -30,10 +30,15 @@ const ( EthTx4Packed = "08e9dd870210d4b5f0db051aa50b08f6be0712043b9aca001890a10f2ac40a4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f80000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c73843220c92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf23a14479cc461fecd078f766ecc58533d6f69580cf3ac42144bda106325c335df99eab7fe363cac8a0ba2a24d482422d40b0a03034d301201011a9e010a140d0f936ee4c93e25944694d6c121de94d9760f1112200000000000000000000000000000000000000000000000006a8313d60b1f606b1a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a21220000000000000000000000000000000000000000000000000000308fd0e798ac01a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1aa1030a14479cc461fecd078f766ecc58533d6f69580cf3ac1280020000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f606b000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac1a200d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb31a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1a2000000000000000000000000000000000000000000000000000000000000000001a205af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f1a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a2122000000000000000000000000000000000000000000000000000031855667df7a81a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a9e010a140d0f936ee4c93e25944694d6c121de94d9760f1112200000000000000000000000000000000000000000000000006a8313d60b1f80001a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1aa1030a14479cc461fecd078f766ecc58533d6f69580cf3ac1280020000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f481a200d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb31a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1a2000000000000000000000000000000000000000000000000000000000000000001a20b0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa" ) -func unpackTxs(packed []string, parser bchain.BlockChainParser) []bchain.Tx { +type packedAndInternal struct { + packed string + internal *bchain.EthereumInternalData +} + +func unpackTxs(packed []packedAndInternal, parser bchain.BlockChainParser) []bchain.Tx { r := make([]bchain.Tx, len(packed)) for i, p := range packed { - b, err := hex.DecodeString(p) + b, err := hex.DecodeString(p.packed) if err != nil { panic(err) } @@ -41,6 +46,8 @@ func unpackTxs(packed []string, parser bchain.BlockChainParser) []bchain.Tx { if err != nil { panic(err) } + c, _ := tx.CoinSpecificData.(bchain.EthereumSpecificData) + c.InternalData = p.internal r[i] = *tx } return r @@ -56,7 +63,11 @@ func GetTestEthereumTypeBlock1(parser bchain.BlockChainParser) *bchain.Block { Time: 1534858022, Confirmations: 2, }, - Txs: unpackTxs([]string{EthTx1Packed, EthTx2Packed}, parser), + Txs: unpackTxs([]packedAndInternal{{ + packed: EthTx1Packed, + }, { + packed: EthTx2Packed, + }}, parser), } } @@ -70,6 +81,10 @@ func GetTestEthereumTypeBlock2(parser bchain.BlockChainParser) *bchain.Block { Time: 1534859988, Confirmations: 1, }, - Txs: unpackTxs([]string{EthTx3Packed, EthTx4Packed}, parser), + Txs: unpackTxs([]packedAndInternal{{ + packed: EthTx3Packed, + }, { + packed: EthTx4Packed, + }}, parser), } } From e3bb706ea2f22df5589c2e7b87bc0cd1629607af Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 18 Dec 2021 02:41:04 +0100 Subject: [PATCH 039/530] Index ETH internal transactions --- api/worker.go | 4 +- bchain/coins/blockchain.go | 1 + ...ethereumtype.go => types_ethereum_type.go} | 0 db/rocksdb.go | 5 +- db/rocksdb_ethereumtype.go | 305 ++++++++++++++++-- db/rocksdb_ethereumtype_test.go | 124 +++++-- tests/dbtestdata/dbtestdata_ethereumtype.go | 50 ++- 7 files changed, 431 insertions(+), 58 deletions(-) rename bchain/{types_ethereumtype.go => types_ethereum_type.go} (100%) diff --git a/api/worker.go b/api/worker.go index 74b086ea24..ccd54a4307 100644 --- a/api/worker.go +++ b/api/worker.go @@ -701,9 +701,9 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto continue } // filter only transactions of this contract - filter.Vout = i + 1 + filter.Vout = i + db.ContractIndexOffset } - t, err := w.getEthereumToken(i+1, addrDesc, c.Contract, details, int(c.Txs)) + t, err := w.getEthereumToken(i+db.ContractIndexOffset, addrDesc, c.Contract, details, int(c.Txs)) if err != nil { return nil, nil, nil, 0, 0, 0, err } diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 1fe7baae17..c4008e8f13 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -70,6 +70,7 @@ func init() { BlockChainFactories["Ethereum"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Classic"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Ropsten"] = eth.NewEthereumRPC + BlockChainFactories["Ethereum Testnet Ropsten Archive"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Goerli"] = eth.NewEthereumRPC BlockChainFactories["Bcash"] = bch.NewBCashRPC BlockChainFactories["Bcash Testnet"] = bch.NewBCashRPC diff --git a/bchain/types_ethereumtype.go b/bchain/types_ethereum_type.go similarity index 100% rename from bchain/types_ethereumtype.go rename to bchain/types_ethereum_type.go diff --git a/db/rocksdb.go b/db/rocksdb.go index 59b90c9a70..62c771b065 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -110,8 +110,11 @@ const ( // BitcoinType cfAddressBalance cfTxAddresses + + __break__ + // EthereumType - cfAddressContracts = cfAddressBalance + cfAddressContracts = iota - __break__ + cfAddressBalance - 1 cfInternalData cfContracts ) diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index e63b144ca8..a46cac2764 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -3,6 +3,7 @@ package db import ( "bytes" "encoding/hex" + "math/big" vlq "github.com/bsm/go-vlq" "github.com/flier/gorocksdb" @@ -12,6 +13,9 @@ import ( "github.com/trezor/blockbook/bchain/coins/eth" ) +const InternalTxIndexOffset = 1 +const ContractIndexOffset = 2 + // AddrContract is Contract address with number of transactions done by given address type AddrContract struct { Contract bchain.AddressDescriptor @@ -108,6 +112,11 @@ func isZeroAddress(addrDesc bchain.AddressDescriptor) bool { return true } +const transferTo = int32(0) +const transferFrom = ^int32(0) +const internalTransferTo = int32(1) +const internalTransferFrom = ^int32(1) + func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.AddressDescriptor, btxID []byte, index int32, contract bchain.AddressDescriptor, addresses addressesMap, addressContracts map[string]*AddrContracts, addTxCount bool) error { var err error strAddrDesc := string(addrDesc) @@ -127,7 +136,11 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address } if contract == nil { if addTxCount { - ac.NonContractTxs++ + if index == internalTransferFrom || index == internalTransferTo { + ac.InternalTxs++ + } else { + ac.NonContractTxs++ + } } } else { // do not store contracts for 0x0000000000000000000000000000000000000000 address @@ -138,15 +151,21 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address i = len(ac.Contracts) ac.Contracts = append(ac.Contracts, AddrContract{Contract: contract}) } - // index 0 is for ETH transfers, contract indexes start with 1 + // index 0 is for ETH transfers, index 1 (InternalTxIndexOffset) is for internal transfers, contract indexes start with 2 (ContractIndexOffset) if index < 0 { - index = ^int32(i + 1) + index = ^int32(i + ContractIndexOffset) } else { - index = int32(i + 1) + index = int32(i + ContractIndexOffset) } if addTxCount { ac.Contracts[i].Txs++ } + } else { + if index < 0 { + index = transferFrom + } else { + index = transferTo + } } } counted := addToAddressesMap(addresses, strAddrDesc, btxID, index) @@ -160,10 +179,23 @@ type ethBlockTxContract struct { addr, contract bchain.AddressDescriptor } +type ethInternalTransfer struct { + internalType bchain.EthereumInternalTransactionType + from, to bchain.AddressDescriptor + value big.Int +} + +type ethInternalData struct { + internalType bchain.EthereumInternalTransactionType + contract bchain.AddressDescriptor + transfers []ethInternalTransfer +} + type ethBlockTx struct { - btxID []byte - from, to bchain.AddressDescriptor - contracts []ethBlockTxContract + btxID []byte + from, to bchain.AddressDescriptor + contracts []ethBlockTxContract + internalData *ethInternalData } func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses addressesMap, addressContracts map[string]*AddrContracts) ([]ethBlockTx, error) { @@ -184,12 +216,12 @@ func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses ad if err != bchain.ErrAddressMissing { glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, output", err, block.Height, tx.Txid) } - continue - } - if err = d.addToAddressesAndContractsEthereumType(to, btxID, 0, nil, addresses, addressContracts, true); err != nil { - return nil, err + } else { + if err = d.addToAddressesAndContractsEthereumType(to, btxID, transferTo, nil, addresses, addressContracts, true); err != nil { + return nil, err + } + blockTx.to = to } - blockTx.to = to } // there is only one input address in EthereumType transaction, store it in format txid ^0 if len(tx.Vin) == 1 && len(tx.Vin[0].Addresses) == 1 { @@ -198,12 +230,68 @@ func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses ad if err != bchain.ErrAddressMissing { glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, input", err, block.Height, tx.Txid) } - continue + } else { + if err = d.addToAddressesAndContractsEthereumType(from, btxID, transferFrom, nil, addresses, addressContracts, !bytes.Equal(from, to)); err != nil { + return nil, err + } + blockTx.from = from } - if err = d.addToAddressesAndContractsEthereumType(from, btxID, ^int32(0), nil, addresses, addressContracts, !bytes.Equal(from, to)); err != nil { - return nil, err + } + // process internal data + eid, _ := tx.CoinSpecificData.(bchain.EthereumSpecificData) + if eid.InternalData != nil { + blockTx.internalData = ðInternalData{ + internalType: eid.InternalData.Type, + } + // index contract creation + if eid.InternalData.Type == bchain.CREATE { + to, err = d.chainParser.GetAddrDescFromAddress(eid.InternalData.Contract) + if err != nil { + if err != bchain.ErrAddressMissing { + glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, create contract", err, block.Height, tx.Txid) + } + // set the internalType to CALL if incorrect contract so that it is not breaking the packing of data to DB + blockTx.internalData.internalType = bchain.CALL + } else { + blockTx.internalData.contract = to + if err = d.addToAddressesAndContractsEthereumType(to, btxID, internalTransferTo, nil, addresses, addressContracts, true); err != nil { + return nil, err + } + } + } + // index internal transfers + if len(eid.InternalData.Transfers) > 0 { + blockTx.internalData.transfers = make([]ethInternalTransfer, len(eid.InternalData.Transfers)) + for i := range eid.InternalData.Transfers { + iti := &eid.InternalData.Transfers[i] + ito := &blockTx.internalData.transfers[i] + to, err = d.chainParser.GetAddrDescFromAddress(iti.To) + if err != nil { + // do not log ErrAddressMissing, transactions can be without to address (for example eth contracts) + if err != bchain.ErrAddressMissing { + glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, internal transfer %d to", err, block.Height, tx.Txid, i) + } + } else { + if err = d.addToAddressesAndContractsEthereumType(to, btxID, internalTransferTo, nil, addresses, addressContracts, true); err != nil { + return nil, err + } + ito.to = to + } + from, err = d.chainParser.GetAddrDescFromAddress(iti.From) + if err != nil { + if err != bchain.ErrAddressMissing { + glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, internal transfer %d from", err, block.Height, tx.Txid, i) + } + } else { + if err = d.addToAddressesAndContractsEthereumType(from, btxID, internalTransferFrom, nil, addresses, addressContracts, !bytes.Equal(from, to)); err != nil { + return nil, err + } + ito.from = from + } + ito.internalType = iti.Type + ito.value = iti.Value + } } - blockTx.from = from } // store erc20 transfers erc20, err := d.chainParser.EthereumTypeGetErc20FromTx(&tx) @@ -249,14 +337,95 @@ func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses ad return blockTxs, nil } +var ethZeroAddress []byte = make([]byte, eth.EthereumTypeAddressDescriptorLen) + +func packEthInternalData(data *ethInternalData) []byte { + // allocate enough for type+contract+all transfers with bigint value + buf := make([]byte, 0, (2*len(data.transfers)+1)*(eth.EthereumTypeAddressDescriptorLen+16)) + appendAddress := func(a bchain.AddressDescriptor) { + if len(a) != eth.EthereumTypeAddressDescriptorLen { + buf = append(buf, ethZeroAddress...) + } else { + buf = append(buf, a...) + } + } + varBuf := make([]byte, maxPackedBigintBytes) + + // internalType is one bit (CALL|CREATE), it is joined with count of internal transfers*2 + l := packVaruint(uint(data.internalType)&1+uint(len(data.transfers))<<1, varBuf) + buf = append(buf, varBuf[:l]...) + if data.internalType == bchain.CREATE { + appendAddress(data.contract) + } + for i := range data.transfers { + t := &data.transfers[i] + buf = append(buf, byte(t.internalType)) + appendAddress(t.from) + appendAddress(t.to) + l = packBigint(&t.value, varBuf) + buf = append(buf, varBuf[:l]...) + } + return buf +} + +func (d *RocksDB) unpackEthInternalData(buf []byte) (*bchain.EthereumInternalData, error) { + id := bchain.EthereumInternalData{} + v, l := unpackVaruint(buf) + id.Type = bchain.EthereumInternalTransactionType(v & 1) + id.Transfers = make([]bchain.EthereumInternalTransfer, v>>1) + if id.Type == bchain.CREATE { + addresses, _, _ := d.chainParser.GetAddressesFromAddrDesc(buf[l : l+eth.EthereumTypeAddressDescriptorLen]) + l += eth.EthereumTypeAddressDescriptorLen + if len(addresses) > 0 { + id.Contract = addresses[0] + } + } + var ll int + for i := range id.Transfers { + t := &id.Transfers[i] + t.Type = bchain.EthereumInternalTransactionType(buf[l]) + l++ + addresses, _, _ := d.chainParser.GetAddressesFromAddrDesc(buf[l : l+eth.EthereumTypeAddressDescriptorLen]) + l += eth.EthereumTypeAddressDescriptorLen + if len(addresses) > 0 { + t.From = addresses[0] + } + addresses, _, _ = d.chainParser.GetAddressesFromAddrDesc(buf[l : l+eth.EthereumTypeAddressDescriptorLen]) + l += eth.EthereumTypeAddressDescriptorLen + if len(addresses) > 0 { + t.To = addresses[0] + } + t.Value, ll = unpackBigint(buf[l:]) + l += ll + } + return &id, nil +} + +func (d *RocksDB) GetEthereumInternalData(txid string) (*bchain.EthereumInternalData, error) { + btxID, err := d.chainParser.PackTxid(txid) + if err != nil { + return nil, err + } + + val, err := d.db.GetCF(d.ro, d.cfh[cfInternalData], btxID) + if err != nil { + return nil, err + } + defer val.Free() + buf := val.Data() + if len(buf) == 0 { + return nil, nil + } + return d.unpackEthInternalData(buf) +} + func (d *RocksDB) storeAndCleanupBlockTxsEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block, blockTxs []ethBlockTx) error { pl := d.chainParser.PackedTxidLen() buf := make([]byte, 0, (pl+2*eth.EthereumTypeAddressDescriptorLen)*len(blockTxs)) varBuf := make([]byte, vlq.MaxLen64) - zeroAddress := make([]byte, eth.EthereumTypeAddressDescriptorLen) appendAddress := func(a bchain.AddressDescriptor) { if len(a) != eth.EthereumTypeAddressDescriptorLen { - buf = append(buf, zeroAddress...) + buf = append(buf, ethZeroAddress...) } else { buf = append(buf, a...) } @@ -266,7 +435,29 @@ func (d *RocksDB) storeAndCleanupBlockTxsEthereumType(wb *gorocksdb.WriteBatch, buf = append(buf, blockTx.btxID...) appendAddress(blockTx.from) appendAddress(blockTx.to) - l := packVaruint(uint(len(blockTx.contracts)), varBuf) + // internal data - store the number of addresses, with odd number the CREATE tx type + var internalDataTransfers uint + if blockTx.internalData != nil { + wb.PutCF(d.cfh[cfInternalData], blockTx.btxID, packEthInternalData(blockTx.internalData)) + internalDataTransfers = uint(len(blockTx.internalData.transfers)) * 2 + if blockTx.internalData.internalType == bchain.CREATE { + internalDataTransfers++ + } + } + l := packVaruint(internalDataTransfers, varBuf) + buf = append(buf, varBuf[:l]...) + if internalDataTransfers > 0 { + if blockTx.internalData.internalType == bchain.CREATE { + appendAddress(blockTx.internalData.contract) + } + for j := range blockTx.internalData.transfers { + c := &blockTx.internalData.transfers[j] + appendAddress(c.from) + appendAddress(c.to) + } + } + // contracts - store the number of address pairs + l = packVaruint(uint(len(blockTx.contracts)), varBuf) buf = append(buf, varBuf[:l]...) for j := range blockTx.contracts { c := &blockTx.contracts[j] @@ -323,8 +514,33 @@ func (d *RocksDB) getBlockTxsEthereumType(height uint32) ([]ethBlockTx, error) { if err != nil { return nil, err } + // internal data + var internalData *ethInternalData cc, l := unpackVaruint(buf[i:]) i += l + if cc > 0 { + internalData = ðInternalData{} + // odd count of internal transfers means it is CREATE transaction with the contract added to the list + if cc&1 == 1 { + internalData.internalType = bchain.CREATE + internalData.contract, i, err = getAddress(i) + if err != nil { + return nil, err + } + } + internalData.transfers = make([]ethInternalTransfer, cc/2) + for j := range internalData.transfers { + t := &internalData.transfers[j] + t.from, i, err = getAddress(i) + t.to, i, err = getAddress(i) + if err != nil { + return nil, err + } + } + } + // contracts + cc, l = unpackVaruint(buf[i:]) + i += l contracts := make([]ethBlockTxContract, cc) for j := range contracts { contracts[j].addr, i, err = getAddress(i) @@ -337,10 +553,11 @@ func (d *RocksDB) getBlockTxsEthereumType(height uint32) ([]ethBlockTx, error) { } } bt = append(bt, ethBlockTx{ - btxID: txid, - from: from, - to: to, - contracts: contracts, + btxID: txid, + from: from, + to: to, + internalData: internalData, + contracts: contracts, }) } return bt, nil @@ -349,7 +566,7 @@ func (d *RocksDB) getBlockTxsEthereumType(height uint32) ([]ethBlockTx, error) { func (d *RocksDB) disconnectBlockTxsEthereumType(wb *gorocksdb.WriteBatch, height uint32, blockTxs []ethBlockTx, contracts map[string]*AddrContracts) error { glog.Info("Disconnecting block ", height, " containing ", len(blockTxs), " transactions") addresses := make(map[string]map[string]struct{}) - disconnectAddress := func(btxID []byte, addrDesc, contract bchain.AddressDescriptor) error { + disconnectAddress := func(btxID []byte, internal bool, addrDesc, contract bchain.AddressDescriptor) error { var err error // do not process empty address if len(addrDesc) == 0 { @@ -382,10 +599,18 @@ func (d *RocksDB) disconnectBlockTxsEthereumType(wb *gorocksdb.WriteBatch, heigh c.TotalTxs-- } if contract == nil { - if c.NonContractTxs > 0 { - c.NonContractTxs-- + if internal { + if c.InternalTxs > 0 { + c.InternalTxs-- + } else { + glog.Warning("AddressContracts ", addrDesc, ", InternalTxs would be negative, tx ", hex.EncodeToString(btxID)) + } } else { - glog.Warning("AddressContracts ", addrDesc, ", EthTxs would be negative, tx ", hex.EncodeToString(btxID)) + if c.NonContractTxs > 0 { + c.NonContractTxs-- + } else { + glog.Warning("AddressContracts ", addrDesc, ", EthTxs would be negative, tx ", hex.EncodeToString(btxID)) + } } } else { i, found := findContractInAddressContracts(contract, c.Contracts) @@ -409,21 +634,41 @@ func (d *RocksDB) disconnectBlockTxsEthereumType(wb *gorocksdb.WriteBatch, heigh } for i := range blockTxs { blockTx := &blockTxs[i] - if err := disconnectAddress(blockTx.btxID, blockTx.from, nil); err != nil { + if err := disconnectAddress(blockTx.btxID, false, blockTx.from, nil); err != nil { return err } // if from==to, tx is counted only once and does not have to be disconnected again if !bytes.Equal(blockTx.from, blockTx.to) { - if err := disconnectAddress(blockTx.btxID, blockTx.to, nil); err != nil { + if err := disconnectAddress(blockTx.btxID, false, blockTx.to, nil); err != nil { return err } } + if blockTx.internalData != nil { + if blockTx.internalData.internalType == bchain.CREATE { + if err := disconnectAddress(blockTx.btxID, true, blockTx.internalData.contract, nil); err != nil { + return err + } + } + for j := range blockTx.internalData.transfers { + t := &blockTx.internalData.transfers[j] + if err := disconnectAddress(blockTx.btxID, true, t.from, nil); err != nil { + return err + } + // if from==to, tx is counted only once and does not have to be disconnected again + if !bytes.Equal(t.from, t.to) { + if err := disconnectAddress(blockTx.btxID, true, t.to, nil); err != nil { + return err + } + } + } + } for _, c := range blockTx.contracts { - if err := disconnectAddress(blockTx.btxID, c.addr, c.contract); err != nil { + if err := disconnectAddress(blockTx.btxID, false, c.addr, c.contract); err != nil { return err } } wb.DeleteCF(d.cfh[cfTransactions], blockTx.btxID) + wb.DeleteCF(d.cfh[cfInternalData], blockTx.btxID) } for a := range addresses { key := packAddressKey([]byte(a), height) diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index 55af30ff9e..20df296d85 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/juju/errors" + "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain/coins/eth" "github.com/trezor/blockbook/tests/dbtestdata" ) @@ -33,10 +34,11 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo } } if err := checkColumn(d, cfAddresses, []keyPair{ - {addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil}, - {addressKeyHex(dbtestdata.EthAddr55, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{0}), nil}, - {addressKeyHex(dbtestdata.EthAddr20, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^0, ^1}), nil}, - {addressKeyHex(dbtestdata.EthAddrContract4a, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{0}), nil}, + {addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1, ^1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil}, + {addressKeyHex(dbtestdata.EthAddr55, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{2}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{0}), nil}, + {addressKeyHex(dbtestdata.EthAddr20, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^0, ^2}), nil}, + {addressKeyHex(dbtestdata.EthAddr9f, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1}), nil}, + {addressKeyHex(dbtestdata.EthAddrContract4a, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{0, 1}), nil}, }); err != nil { { t.Fatal(err) @@ -44,10 +46,26 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo } if err := checkColumn(d, cfAddressContracts, []keyPair{ - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "010100", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "020102", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "020100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010100", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "010002", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010101", nil}, + }); err != nil { + { + t.Fatal(err) + } + } + + if err := checkColumn(d, cfInternalData, []keyPair{ + { + dbtestdata.EthTxidB1T2, + "06" + + "01" + dbtestdata.EthAddr9f + dbtestdata.EthAddrContract4a + "030f4240" + + "00" + dbtestdata.EthAddr3e + dbtestdata.EthAddr9f + "030f4241" + + "00" + dbtestdata.EthAddr3e + dbtestdata.EthAddr3e + "030f4242", + nil, + }, }); err != nil { { t.Fatal(err) @@ -62,9 +80,13 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo { "0041eee8", dbtestdata.EthTxidB1T1 + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + "00" + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + "00" + "00" + dbtestdata.EthTxidB1T2 + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + + "06" + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), @@ -97,15 +119,18 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) { } } if err := checkColumn(d, cfAddresses, []keyPair{ - {addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil}, - {addressKeyHex(dbtestdata.EthAddr55, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{0}), nil}, - {addressKeyHex(dbtestdata.EthAddr20, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^0, ^1}), nil}, - {addressKeyHex(dbtestdata.EthAddrContract4a, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{0}), nil}, - {addressKeyHex(dbtestdata.EthAddr55, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^2, 1}) + txIndexesHex(dbtestdata.EthTxidB2T1, []int32{^0}), nil}, - {addressKeyHex(dbtestdata.EthAddr9f, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T1, []int32{0}), nil}, - {addressKeyHex(dbtestdata.EthAddr4b, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^0, 1, ^2, 2, ^1}), nil}, - {addressKeyHex(dbtestdata.EthAddr7b, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^1, 2}), nil}, + {addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1, ^1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil}, + {addressKeyHex(dbtestdata.EthAddr55, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{2}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{0}), nil}, + {addressKeyHex(dbtestdata.EthAddr20, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^0, ^2}), nil}, + {addressKeyHex(dbtestdata.EthAddr9f, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1}), nil}, + {addressKeyHex(dbtestdata.EthAddrContract4a, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{0, 1}), nil}, + {addressKeyHex(dbtestdata.EthAddr55, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^3, 2}) + txIndexesHex(dbtestdata.EthTxidB2T1, []int32{^0}), nil}, + {addressKeyHex(dbtestdata.EthAddr9f, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{1, 1}) + txIndexesHex(dbtestdata.EthTxidB2T1, []int32{0}), nil}, + {addressKeyHex(dbtestdata.EthAddr4b, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^0, ^1, 2, ^3, 3, ^2}), nil}, + {addressKeyHex(dbtestdata.EthAddr7b, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^2, 3}), nil}, + {addressKeyHex(dbtestdata.EthAddrContract0d, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{1}), nil}, {addressKeyHex(dbtestdata.EthAddrContract47, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{0}), nil}, + {addressKeyHex(dbtestdata.EthAddrContract4a, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^1}), nil}, }); err != nil { { t.Fatal(err) @@ -113,13 +138,14 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) { } if err := checkColumn(d, cfAddressContracts, []keyPair{ - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "010100", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "020102", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "040200" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010100", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "010100", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "020102", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "030104", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), "010101" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser), "010000" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser), "010001", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser), "010100", nil}, }); err != nil { { @@ -127,13 +153,39 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) { } } + if err := checkColumn(d, cfInternalData, []keyPair{ + { + dbtestdata.EthTxidB1T2, + "06" + + "01" + dbtestdata.EthAddr9f + dbtestdata.EthAddrContract4a + "030f4240" + + "00" + dbtestdata.EthAddr3e + dbtestdata.EthAddr9f + "030f4241" + + "00" + dbtestdata.EthAddr3e + dbtestdata.EthAddr3e + "030f4242", + nil, + }, + { + dbtestdata.EthTxidB2T2, + "05" + dbtestdata.EthAddrContract0d + + "00" + dbtestdata.EthAddr4b + dbtestdata.EthAddr9f + "030f424a" + + "02" + dbtestdata.EthAddrContract4a + dbtestdata.EthAddr9f + "030f424b", + nil, + }, + }); err != nil { + { + t.Fatal(err) + } + } + if err := checkColumn(d, cfBlockTxs, []keyPair{ { "0041eee9", dbtestdata.EthTxidB2T1 + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + "00" + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + "00" + "00" + dbtestdata.EthTxidB2T2 + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser) + + "05" + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + "08" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + @@ -152,6 +204,19 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) { } } +func formatInternalData(in *bchain.EthereumInternalData) *bchain.EthereumInternalData { + out := *in + if out.Type == bchain.CREATE { + out.Contract = eth.EIP55AddressFromAddress(out.Contract) + } + for i := range out.Transfers { + t := &out.Transfers[i] + t.From = eth.EIP55AddressFromAddress(t.From) + t.To = eth.EIP55AddressFromAddress(t.To) + } + return &out +} + // TestRocksDB_Index_EthereumType is an integration test probing the whole indexing functionality for EthereumType chains // It does the following: // 1) Connect two blocks (inputs from 2nd block are spending some outputs from the 1st block) @@ -195,14 +260,27 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { // get transactions for various addresses / low-high ranges verifyGetTransactions(t, d, "0x"+dbtestdata.EthAddr55, 0, 10000000, []txidIndex{ - {"0x" + dbtestdata.EthTxidB2T2, ^2}, - {"0x" + dbtestdata.EthTxidB2T2, 1}, + {"0x" + dbtestdata.EthTxidB2T2, ^3}, + {"0x" + dbtestdata.EthTxidB2T2, 2}, {"0x" + dbtestdata.EthTxidB2T1, ^0}, - {"0x" + dbtestdata.EthTxidB1T2, 1}, + {"0x" + dbtestdata.EthTxidB1T2, 2}, {"0x" + dbtestdata.EthTxidB1T1, 0}, }, nil) verifyGetTransactions(t, d, "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eBad", 500000, 1000000, []txidIndex{}, errors.New("Address missing")) + id, err := d.GetEthereumInternalData(dbtestdata.EthTxidB1T1) + if err != nil || id != nil { + t.Errorf("GetEthereumInternalData(%s) = %+v, want %+v, err %v", dbtestdata.EthTxidB1T1, id, nil, err) + } + id, err = d.GetEthereumInternalData(dbtestdata.EthTxidB1T2) + if err != nil || !reflect.DeepEqual(id, formatInternalData(dbtestdata.EthTx2InternalData)) { + t.Errorf("GetEthereumInternalData(%s) = %+v, want %+v, err %v", dbtestdata.EthTxidB1T2, id, formatInternalData(dbtestdata.EthTx2InternalData), err) + } + id, err = d.GetEthereumInternalData(dbtestdata.EthTxidB2T2) + if err != nil || !reflect.DeepEqual(id, formatInternalData(dbtestdata.EthTx4InternalData)) { + t.Errorf("GetEthereumInternalData(%s) = %+v, want %+v, err %v", dbtestdata.EthTxidB2T2, id, formatInternalData(dbtestdata.EthTx4InternalData), err) + } + // GetBestBlock height, hash, err := d.GetBestBlock() if err != nil { diff --git a/tests/dbtestdata/dbtestdata_ethereumtype.go b/tests/dbtestdata/dbtestdata_ethereumtype.go index cebc9cb265..56e04ec58d 100644 --- a/tests/dbtestdata/dbtestdata_ethereumtype.go +++ b/tests/dbtestdata/dbtestdata_ethereumtype.go @@ -2,6 +2,7 @@ package dbtestdata import ( "encoding/hex" + "math/big" "github.com/trezor/blockbook/bchain" ) @@ -30,6 +31,48 @@ const ( EthTx4Packed = "08e9dd870210d4b5f0db051aa50b08f6be0712043b9aca001890a10f2ac40a4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f80000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c73843220c92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf23a14479cc461fecd078f766ecc58533d6f69580cf3ac42144bda106325c335df99eab7fe363cac8a0ba2a24d482422d40b0a03034d301201011a9e010a140d0f936ee4c93e25944694d6c121de94d9760f1112200000000000000000000000000000000000000000000000006a8313d60b1f606b1a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a21220000000000000000000000000000000000000000000000000000308fd0e798ac01a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1aa1030a14479cc461fecd078f766ecc58533d6f69580cf3ac1280020000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f606b000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac1a200d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb31a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1a2000000000000000000000000000000000000000000000000000000000000000001a205af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f1a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a2122000000000000000000000000000000000000000000000000000031855667df7a81a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a9e010a140d0f936ee4c93e25944694d6c121de94d9760f1112200000000000000000000000000000000000000000000000006a8313d60b1f80001a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1aa1030a14479cc461fecd078f766ecc58533d6f69580cf3ac1280020000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f481a200d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb31a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1a2000000000000000000000000000000000000000000000000000000000000000001a20b0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa" ) +var EthTx2InternalData = &bchain.EthereumInternalData{ + Transfers: []bchain.EthereumInternalTransfer{ + { + Type: bchain.CREATE, + From: EthAddr9f, + To: EthAddrContract4a, + Value: *big.NewInt(1000000), + }, + { + Type: bchain.CALL, + From: EthAddr3e, + To: EthAddr9f, + Value: *big.NewInt(1000001), + }, + { + Type: bchain.CALL, + From: EthAddr3e, + To: EthAddr3e, + Value: *big.NewInt(1000002), + }, + }, +} + +var EthTx4InternalData = &bchain.EthereumInternalData{ + Type: bchain.CREATE, + Contract: EthAddrContract0d, + Transfers: []bchain.EthereumInternalTransfer{ + { + Type: bchain.CALL, + From: EthAddr4b, + To: EthAddr9f, + Value: *big.NewInt(1000010), + }, + { + Type: bchain.SELFDESTRUCT, + From: EthAddrContract4a, + To: EthAddr9f, + Value: *big.NewInt(1000011), + }, + }, +} + type packedAndInternal struct { packed string internal *bchain.EthereumInternalData @@ -48,6 +91,7 @@ func unpackTxs(packed []packedAndInternal, parser bchain.BlockChainParser) []bch } c, _ := tx.CoinSpecificData.(bchain.EthereumSpecificData) c.InternalData = p.internal + tx.CoinSpecificData = c r[i] = *tx } return r @@ -66,7 +110,8 @@ func GetTestEthereumTypeBlock1(parser bchain.BlockChainParser) *bchain.Block { Txs: unpackTxs([]packedAndInternal{{ packed: EthTx1Packed, }, { - packed: EthTx2Packed, + packed: EthTx2Packed, + internal: EthTx2InternalData, }}, parser), } } @@ -84,7 +129,8 @@ func GetTestEthereumTypeBlock2(parser bchain.BlockChainParser) *bchain.Block { Txs: unpackTxs([]packedAndInternal{{ packed: EthTx3Packed, }, { - packed: EthTx4Packed, + packed: EthTx4Packed, + internal: EthTx4InternalData, }}, parser), } } From 89b1e756419e2e72de81bc5cbb61fbd656039b78 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 21 Dec 2021 00:07:13 +0100 Subject: [PATCH 040/530] Add public server unit tests for ethereum type coins --- server/public_ethereumtype_test.go | 60 +++++++++ server/public_test.go | 136 ++++++++++++--------- tests/dbtestdata/fakechain_ethereumtype.go | 128 +++++++++++++++++++ 3 files changed, 267 insertions(+), 57 deletions(-) create mode 100644 server/public_ethereumtype_test.go create mode 100644 tests/dbtestdata/fakechain_ethereumtype.go diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go new file mode 100644 index 0000000000..4c9c4878dd --- /dev/null +++ b/server/public_ethereumtype_test.go @@ -0,0 +1,60 @@ +//go:build unittest +// +build unittest + +package server + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/golang/glog" + "github.com/trezor/blockbook/bchain/coins/eth" + "github.com/trezor/blockbook/tests/dbtestdata" +) + +func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { + tests := []httpTests{ + { + name: "apiIndex", + r: newGetRequest(ts.URL + "/api"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"blockbook":{"coin":"Fakecoin"`, + `"bestHeight":4321001`, + `"decimals":18`, + `"backend":{"chain":"fakecoin","blocks":2,"headers":2,"bestBlockHash":"0x2b57e15e93a0ed197417a34c2498b7187df79099572c04a6b6e6ff418f74e6ee"`, + `"version":"001001","subversion":"/Fakecoin:0.0.1/"`, + }, + }, + { + name: "apiAddress EthAddr4b", + r: newGetRequest(ts.URL + "/api/v2/address/" + dbtestdata.EthAddr4b), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":18}],"erc20Contract":{"contract":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","name":"Contract 75","symbol":"S75","decimals":18}}`, + }, + }, + } + + performHttpTests(tests, t, ts) +} + +func Test_PublicServer_EthereumType(t *testing.T) { + parser := eth.NewEthereumParser(1) + chain, err := dbtestdata.NewFakeBlockChainEthereumType(parser) + if err != nil { + glog.Fatal("fakechain: ", err) + } + + s, dbpath := setupPublicHTTPServer(parser, chain, t) + defer closeAndDestroyPublicServer(t, s, dbpath) + s.ConnectFullPublicInterface() + // take the handler of the public server and pass it to the test server + ts := httptest.NewServer(s.https.Handler) + defer ts.Close() + + httpTestsEthereumType(t, ts) +} diff --git a/server/public_test.go b/server/public_test.go index 3de7c4ff5c..f06d55d2e1 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -36,7 +36,7 @@ func TestMain(m *testing.M) { os.Exit(c) } -func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *common.InternalState, string) { +func setupRocksDB(parser bchain.BlockChainParser, chain bchain.BlockChain, t *testing.T) (*db.RocksDB, *common.InternalState, string) { tmp, err := ioutil.TempDir("", "testdb") if err != nil { t.Fatal(err) @@ -50,7 +50,15 @@ func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *c t.Fatal(err) } d.SetInternalState(is) - block1 := dbtestdata.GetTestBitcoinTypeBlock1(parser) + // there are 2 simulated block, of height bestBlockHeight-1 and bestBlockHeight + bestHeight, err := chain.GetBestBlockHeight() + if err != nil { + t.Fatal(err) + } + block1, err := chain.GetBlock("", bestHeight-1) + if err != nil { + t.Fatal(err) + } // setup internal state BlockTimes for i := uint32(0); i < block1.Height; i++ { is.BlockTimes = append(is.BlockTimes, 0) @@ -59,7 +67,10 @@ func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *c if err := d.ConnectBlock(block1); err != nil { t.Fatal(err) } - block2 := dbtestdata.GetTestBitcoinTypeBlock2(parser) + block2, err := chain.GetBlock("", bestHeight) + if err != nil { + t.Fatal(err) + } if err := d.ConnectBlock(block2); err != nil { t.Fatal(err) } @@ -70,31 +81,22 @@ func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *c return d, is, tmp } -func setupPublicHTTPServer(t *testing.T) (*PublicServer, string) { - parser := btc.NewBitcoinParser( - btc.GetChainParams("test"), - &btc.Configuration{ - BlockAddressesToKeep: 1, - XPubMagic: 70617039, - XPubMagicSegwitP2sh: 71979618, - XPubMagicSegwitNative: 73342198, - Slip44: 1, - }) +var metrics *common.Metrics - d, is, path := setupRocksDB(t, parser) +func setupPublicHTTPServer(parser bchain.BlockChainParser, chain bchain.BlockChain, t *testing.T) (*PublicServer, string) { + d, is, path := setupRocksDB(parser, chain, t) // setup internal state and match BestHeight to test data is.Coin = "Fakecoin" is.CoinLabel = "Fake Coin" is.CoinShortcut = "FAKE" - metrics, err := common.GetMetrics("Fakecoin") - if err != nil { - glog.Fatal("metrics: ", err) - } - - chain, err := dbtestdata.NewFakeBlockChain(parser) - if err != nil { - glog.Fatal("fakechain: ", err) + var err error + // metrics can be setup only once + if metrics == nil { + metrics, err = common.GetMetrics("Fakecoin") + if err != nil { + glog.Fatal("metrics: ", err) + } } mempool, err := chain.CreateMempool(chain) @@ -204,14 +206,45 @@ func InitTestFiatRates(d *db.RocksDB) error { }, d) } +type httpTests struct { + name string + r *http.Request + status int + contentType string + body []string +} + +func performHttpTests(tests []httpTests, t *testing.T, ts *httptest.Server) { + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resp, err := http.DefaultClient.Do(tt.r) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + if resp.StatusCode != tt.status { + t.Errorf("StatusCode = %v, want %v", resp.StatusCode, tt.status) + } + if resp.Header["Content-Type"][0] != tt.contentType { + t.Errorf("Content-Type = %v, want %v", resp.Header["Content-Type"][0], tt.contentType) + } + bb, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + b := string(bb) + for _, c := range tt.body { + if !strings.Contains(b, c) { + t.Errorf("got %v, want to contain %v", b, c) + break + } + } + }) + } +} + func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { - tests := []struct { - name string - r *http.Request - status int - contentType string - body []string - }{ + tests := []httpTests{ { name: "explorerTx", r: newGetRequest(ts.URL + "/tx/fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db"), @@ -947,33 +980,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { }, }, } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - resp, err := http.DefaultClient.Do(tt.r) - if err != nil { - t.Fatal(err) - } - defer resp.Body.Close() - if resp.StatusCode != tt.status { - t.Errorf("StatusCode = %v, want %v", resp.StatusCode, tt.status) - } - if resp.Header["Content-Type"][0] != tt.contentType { - t.Errorf("Content-Type = %v, want %v", resp.Header["Content-Type"][0], tt.contentType) - } - bb, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatal(err) - } - b := string(bb) - for _, c := range tt.body { - if !strings.Contains(b, c) { - t.Errorf("got %v, want to contain %v", b, c) - break - } - } - }) - } + performHttpTests(tests, t, ts) } func socketioTestsBitcoinType(t *testing.T, ts *httptest.Server) { @@ -1558,7 +1565,22 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { } func Test_PublicServer_BitcoinType(t *testing.T) { - s, dbpath := setupPublicHTTPServer(t) + parser := btc.NewBitcoinParser( + btc.GetChainParams("test"), + &btc.Configuration{ + BlockAddressesToKeep: 1, + XPubMagic: 70617039, + XPubMagicSegwitP2sh: 71979618, + XPubMagicSegwitNative: 73342198, + Slip44: 1, + }) + + chain, err := dbtestdata.NewFakeBlockChain(parser) + if err != nil { + glog.Fatal("fakechain: ", err) + } + + s, dbpath := setupPublicHTTPServer(parser, chain, t) defer closeAndDestroyPublicServer(t, s, dbpath) s.ConnectFullPublicInterface() // take the handler of the public server and pass it to the test server diff --git a/tests/dbtestdata/fakechain_ethereumtype.go b/tests/dbtestdata/fakechain_ethereumtype.go new file mode 100644 index 0000000000..800ff03f18 --- /dev/null +++ b/tests/dbtestdata/fakechain_ethereumtype.go @@ -0,0 +1,128 @@ +package dbtestdata + +import ( + "encoding/json" + "math/big" + "strconv" + + "github.com/trezor/blockbook/bchain" +) + +type fakeBlockChainEthereumType struct { + *fakeBlockChain +} + +// NewFakeBlockChainEthereumType returns mocked blockchain RPC interface used for tests +func NewFakeBlockChainEthereumType(parser bchain.BlockChainParser) (bchain.BlockChain, error) { + return &fakeBlockChainEthereumType{&fakeBlockChain{&bchain.BaseChain{Parser: parser}}}, nil +} + +func (c *fakeBlockChainEthereumType) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) { + return bchain.NewMempoolEthereumType(chain, 1, false), nil +} + +func (c *fakeBlockChainEthereumType) GetChainInfo() (v *bchain.ChainInfo, err error) { + return &bchain.ChainInfo{ + Chain: c.GetNetworkName(), + Blocks: 2, + Headers: 2, + Bestblockhash: GetTestEthereumTypeBlock2(c.Parser).BlockHeader.Hash, + Version: "001001", + Subversion: c.GetSubversion(), + }, nil +} + +func (c *fakeBlockChainEthereumType) GetBestBlockHash() (v string, err error) { + return GetTestEthereumTypeBlock2(c.Parser).BlockHeader.Hash, nil +} + +func (c *fakeBlockChainEthereumType) GetBestBlockHeight() (v uint32, err error) { + return GetTestEthereumTypeBlock2(c.Parser).BlockHeader.Height, nil +} + +func (c *fakeBlockChainEthereumType) GetBlockHash(height uint32) (v string, err error) { + b1 := GetTestEthereumTypeBlock1(c.Parser) + if height == b1.BlockHeader.Height { + return b1.BlockHeader.Hash, nil + } + b2 := GetTestEthereumTypeBlock2(c.Parser) + if height == b2.BlockHeader.Height { + return b2.BlockHeader.Hash, nil + } + return "", bchain.ErrBlockNotFound +} + +func (c *fakeBlockChainEthereumType) GetBlockHeader(hash string) (v *bchain.BlockHeader, err error) { + b1 := GetTestEthereumTypeBlock1(c.Parser) + if hash == b1.BlockHeader.Hash { + return &b1.BlockHeader, nil + } + b2 := GetTestEthereumTypeBlock2(c.Parser) + if hash == b2.BlockHeader.Hash { + return &b2.BlockHeader, nil + } + return nil, bchain.ErrBlockNotFound +} + +func (c *fakeBlockChainEthereumType) GetBlock(hash string, height uint32) (v *bchain.Block, err error) { + b1 := GetTestEthereumTypeBlock1(c.Parser) + if hash == b1.BlockHeader.Hash || height == b1.BlockHeader.Height { + return b1, nil + } + b2 := GetTestEthereumTypeBlock2(c.Parser) + if hash == b2.BlockHeader.Hash || height == b2.BlockHeader.Height { + return b2, nil + } + return nil, bchain.ErrBlockNotFound +} + +func (c *fakeBlockChainEthereumType) GetBlockInfo(hash string) (v *bchain.BlockInfo, err error) { + b1 := GetTestEthereumTypeBlock1(c.Parser) + if hash == b1.BlockHeader.Hash { + return getBlockInfo(b1), nil + } + b2 := GetTestEthereumTypeBlock2(c.Parser) + if hash == b2.BlockHeader.Hash { + return getBlockInfo(b2), nil + } + return nil, bchain.ErrBlockNotFound +} + +func (c *fakeBlockChainEthereumType) GetTransaction(txid string) (v *bchain.Tx, err error) { + v = getTxInBlock(GetTestEthereumTypeBlock1(c.Parser), txid) + if v == nil { + v = getTxInBlock(GetTestEthereumTypeBlock2(c.Parser), txid) + } + if v != nil { + return v, nil + } + return nil, bchain.ErrTxNotFound +} + +func (c *fakeBlockChainEthereumType) GetTransactionSpecific(tx *bchain.Tx) (v json.RawMessage, err error) { + txS, _ := tx.CoinSpecificData.(bchain.EthereumSpecificData) + + rm, err := json.Marshal(txS) + if err != nil { + return nil, err + } + return json.RawMessage(rm), nil +} + +func (c *fakeBlockChainEthereumType) EthereumTypeGetBalance(addrDesc bchain.AddressDescriptor) (*big.Int, error) { + return big.NewInt(123450000 + int64(addrDesc[0])), nil +} + +func (c *fakeBlockChainEthereumType) EthereumTypeGetNonce(addrDesc bchain.AddressDescriptor) (uint64, error) { + return uint64(addrDesc[0]), nil +} + +func (c *fakeBlockChainEthereumType) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.Erc20Contract, error) { + addresses, _, _ := c.Parser.GetAddressesFromAddrDesc(contractDesc) + return &bchain.Erc20Contract{ + Contract: addresses[0], + Name: "Contract " + strconv.Itoa(int(contractDesc[0])), + Symbol: "S" + strconv.Itoa(int(contractDesc[0])), + Decimals: 18, + }, nil +} From 664f58bdd5df79863605f105cbe69729a11553cd Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 27 Dec 2021 00:31:08 +0100 Subject: [PATCH 041/530] Increase workers for ETH bulk import mode --- configs/coins/ethereum_archive.json | 2 +- configs/coins/ethereum_testnet_ropsten_archive.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 17d258ef16..8d7fc12ffb 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -43,7 +43,7 @@ "internal_binding_template": ":{{.Ports.BlockbookInternal}}", "public_binding_template": ":{{.Ports.BlockbookPublic}}", "explorer_url": "", - "additional_params": "", + "additional_params": "-workers=16", "block_chain": { "parse": true, "mempool_workers": 8, diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index 02ea50d802..f109bc02ff 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -42,7 +42,7 @@ "internal_binding_template": ":{{.Ports.BlockbookInternal}}", "public_binding_template": ":{{.Ports.BlockbookPublic}}", "explorer_url": "", - "additional_params": "", + "additional_params": "-workers=16", "block_chain": { "parse": true, "mempool_workers": 8, From 91031715f70f5d41218283d57713625b50a7662e Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 29 Dec 2021 00:20:52 +0100 Subject: [PATCH 042/530] Bulk import ETH internal transactions --- bchain/coins/eth/ethrpc.go | 15 ++++--- bchain/types.go | 3 +- bchain/types_ethereum_type.go | 4 ++ db/bulkconnect.go | 22 +++++++++ db/rocksdb.go | 13 +++++- db/rocksdb_ethereumtype.go | 28 +++++++++++- db/rocksdb_ethereumtype_test.go | 80 ++++++++++++++++++++++++++++++--- 7 files changed, 150 insertions(+), 15 deletions(-) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index da3e02d8d6..df3dc52f81 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -554,7 +554,6 @@ func (b *EthereumRPC) processCallTrace(call rpcCallTrace, d *bchain.EthereumInte } // getInternalDataForBlock fetches debug trace using callTracer, extracts internal transfers and creations and destructions of contracts -// by design, it never returns error so that missing internal transactions do not stop the rest of the blockchain import func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []bchain.RpcTransaction) ([]bchain.EthereumInternalData, error) { data := make([]bchain.EthereumInternalData, len(transactions)) if b.ChainConfig.ProcessInternalTransactions { @@ -564,11 +563,11 @@ func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []b err := b.rpc.CallContext(ctx, &trace, "debug_traceBlockByHash", blockHash, map[string]interface{}{"tracer": "callTracer"}) if err != nil { glog.Error("debug_traceBlockByHash block ", blockHash, ", error ", err) - return data, nil + return data, err } if len(trace) != len(data) { glog.Error("debug_traceBlockByHash block ", blockHash, ", error: trace length does not match block length ", len(trace), "!=", len(data)) - return data, nil + return data, err } for i, result := range trace { r := &result.Result @@ -610,10 +609,11 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error if err != nil { return nil, err } - + // error fetching internal data does not stop the block processing + var blockSpecificData *bchain.EthereumBlockSpecificData internalData, err := b.getInternalDataForBlock(head.Hash, body.Transactions) if err != nil { - return nil, err + blockSpecificData = &bchain.EthereumBlockSpecificData{InternalDataError: err.Error()} } btxs := make([]bchain.Tx, len(body.Transactions)) @@ -629,8 +629,9 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error } } bbk := bchain.Block{ - BlockHeader: *bbh, - Txs: btxs, + BlockHeader: *bbh, + Txs: btxs, + CoinSpecificData: blockSpecificData, } return &bbk, nil } diff --git a/bchain/types.go b/bchain/types.go index 555db84a84..ae590abcd0 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -116,7 +116,8 @@ type MempoolTx struct { // Block is block header and list of transactions type Block struct { BlockHeader - Txs []Tx `json:"tx"` + Txs []Tx `json:"tx"` + CoinSpecificData interface{} `json:"-"` } // BlockHeader contains limited data (as needed for indexing) from backend block header diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index 94e4ecc27e..93bf3c97f0 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -83,3 +83,7 @@ type EthereumSpecificData struct { InternalData *EthereumInternalData `json:"internalData,omitempty"` Receipt *RpcReceipt `json:"receipt,omitempty"` } + +type EthereumBlockSpecificData struct { + InternalDataError string +} diff --git a/db/bulkconnect.go b/db/bulkconnect.go index 27412eedc9..f6bf4ba033 100644 --- a/db/bulkconnect.go +++ b/db/bulkconnect.go @@ -25,6 +25,7 @@ type BulkConnect struct { chainType bchain.ChainType bulkAddresses []bulkAddresses bulkAddressesCount int + ethBlockTxs []ethBlockTx txAddressesMap map[string]*TxAddresses balances map[string]*AddrBalance addressContracts map[string]*AddrContracts @@ -280,6 +281,7 @@ func (b *BulkConnect) connectBlockEthereumType(block *bchain.Block, storeBlockTx if err != nil { return err } + b.ethBlockTxs = append(b.ethBlockTxs, blockTxs...) var storeAddrContracts chan error var sa bool if len(b.addressContracts) > maxBulkAddrContracts { @@ -309,6 +311,16 @@ func (b *BulkConnect) connectBlockEthereumType(block *bchain.Block, storeBlockTx return err } } + if err := b.d.storeInternalDataEthereumType(wb, b.ethBlockTxs); err != nil { + return err + } + b.ethBlockTxs = b.ethBlockTxs[:0] + blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) + if blockSpecificData != nil && blockSpecificData.InternalDataError != "" { + if err := b.d.storeBlockInternalDataErrorEthereumType(wb, block, blockSpecificData.InternalDataError); err != nil { + return err + } + } if storeBlockTxs { if err := b.d.storeAndCleanupBlockTxsEthereumType(wb, block, blockTxs); err != nil { return err @@ -320,6 +332,16 @@ func (b *BulkConnect) connectBlockEthereumType(block *bchain.Block, storeBlockTx if bac > b.bulkAddressesCount { glog.Info("rocksdb: height ", b.height, ", stored ", bac, " addresses, done in ", time.Since(start)) } + } else { + // if there is InternalDataError, store it + blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) + if blockSpecificData != nil && blockSpecificData.InternalDataError != "" { + wb := gorocksdb.NewWriteBatch() + defer wb.Destroy() + if err := b.d.storeBlockInternalDataErrorEthereumType(wb, block, blockSpecificData.InternalDataError); err != nil { + return err + } + } } if storeAddrContracts != nil { if err := <-storeAddrContracts; err != nil { diff --git a/db/rocksdb.go b/db/rocksdb.go index 62c771b065..4438db1748 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -117,6 +117,8 @@ const ( cfAddressContracts = iota - __break__ + cfAddressBalance - 1 cfInternalData cfContracts + cfFunctionSignatures + cfBlockInternalDataErrors ) // common columns @@ -125,7 +127,7 @@ var cfBaseNames = []string{"default", "height", "addresses", "blockTxs", "transa // type specific columns var cfNamesBitcoinType = []string{"addressBalance", "txAddresses"} -var cfNamesEthereumType = []string{"addressContracts", "internalData", "contracts"} +var cfNamesEthereumType = []string{"addressContracts", "internalData", "contracts", "functionSignatures", "blockInternalDataErrors"} func openDB(path string, c *gorocksdb.Cache, openFiles int) (*gorocksdb.DB, []*gorocksdb.ColumnFamilyHandle, error) { // opts with bloom filter @@ -479,6 +481,15 @@ func (d *RocksDB) ConnectBlock(block *bchain.Block) error { if err := d.storeAddressContracts(wb, addressContracts); err != nil { return err } + if err := d.storeInternalDataEthereumType(wb, blockTxs); err != nil { + return err + } + blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) + if blockSpecificData != nil && blockSpecificData.InternalDataError != "" { + if err := d.storeBlockInternalDataErrorEthereumType(wb, block, blockSpecificData.InternalDataError); err != nil { + return err + } + } if err := d.storeAndCleanupBlockTxsEthereumType(wb, block, blockTxs); err != nil { return err } diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index a46cac2764..cde8eab02a 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -419,6 +419,16 @@ func (d *RocksDB) GetEthereumInternalData(txid string) (*bchain.EthereumInternal return d.unpackEthInternalData(buf) } +func (d *RocksDB) storeInternalDataEthereumType(wb *gorocksdb.WriteBatch, blockTxs []ethBlockTx) error { + for i := range blockTxs { + blockTx := &blockTxs[i] + if blockTx.internalData != nil { + wb.PutCF(d.cfh[cfInternalData], blockTx.btxID, packEthInternalData(blockTx.internalData)) + } + } + return nil +} + func (d *RocksDB) storeAndCleanupBlockTxsEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block, blockTxs []ethBlockTx) error { pl := d.chainParser.PackedTxidLen() buf := make([]byte, 0, (pl+2*eth.EthereumTypeAddressDescriptorLen)*len(blockTxs)) @@ -438,7 +448,6 @@ func (d *RocksDB) storeAndCleanupBlockTxsEthereumType(wb *gorocksdb.WriteBatch, // internal data - store the number of addresses, with odd number the CREATE tx type var internalDataTransfers uint if blockTx.internalData != nil { - wb.PutCF(d.cfh[cfInternalData], blockTx.btxID, packEthInternalData(blockTx.internalData)) internalDataTransfers = uint(len(blockTx.internalData.transfers)) * 2 if blockTx.internalData.internalType == bchain.CREATE { internalDataTransfers++ @@ -470,6 +479,22 @@ func (d *RocksDB) storeAndCleanupBlockTxsEthereumType(wb *gorocksdb.WriteBatch, return d.cleanupBlockTxs(wb, block) } +func (d *RocksDB) storeBlockInternalDataErrorEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block, message string) error { + key := packUint(block.Height) + txid, err := d.chainParser.PackTxid(block.Hash) + if err != nil { + return err + } + m := []byte(message) + buf := make([]byte, 0, len(txid)+len(m)+1) + // the stored structure is txid+retry count (1 byte)+error message + buf = append(buf, txid...) + buf = append(buf, 0) + buf = append(buf, m...) + wb.PutCF(d.cfh[cfBlockInternalDataErrors], key, buf) + return nil +} + func (d *RocksDB) getBlockTxsEthereumType(height uint32) ([]ethBlockTx, error) { pl := d.chainParser.PackedTxidLen() val, err := d.db.GetCF(d.ro, d.cfh[cfBlockTxs], packUint(height)) @@ -702,6 +727,7 @@ func (d *RocksDB) DisconnectBlockRangeEthereumType(lower uint32, higher uint32) key := packUint(height) wb.DeleteCF(d.cfh[cfBlockTxs], key) wb.DeleteCF(d.cfh[cfHeight], key) + wb.DeleteCF(d.cfh[cfBlockInternalDataErrors], key) } d.storeAddressContracts(wb, contracts) err := d.db.Write(d.wo, wb) diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index 20df296d85..d3ce848b4b 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -10,6 +10,7 @@ import ( "github.com/juju/errors" "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain/coins/eth" + "github.com/trezor/blockbook/common" "github.com/trezor/blockbook/tests/dbtestdata" ) @@ -101,7 +102,7 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo } } -func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) { +func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDataError bool) { if err := checkColumn(d, cfHeight, []keyPair{ { "0041eee8", @@ -202,6 +203,22 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) { t.Fatal(err) } } + + var internalDataError []keyPair + if wantBlockInternalDataError { + internalDataError = []keyPair{ + { + "0041eee9", + "2b57e15e93a0ed197417a34c2498b7187df79099572c04a6b6e6ff418f74e6ee" + "00" + hex.EncodeToString([]byte("test error")), + nil, + }, + } + } + if err := checkColumn(d, cfBlockInternalDataErrors, internalDataError); err != nil { + { + t.Fatal(err) + } + } } func formatInternalData(in *bchain.EthereumInternalData) *bchain.EthereumInternalData { @@ -247,12 +264,14 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) } - // connect 2nd block + // connect 2nd block, simulate InternalDataError block2 := dbtestdata.GetTestEthereumTypeBlock2(d.chainParser) + block2.CoinSpecificData = &bchain.EthereumBlockSpecificData{InternalDataError: "test error"} if err := d.ConnectBlock(block2); err != nil { t.Fatal(err) } - verifyAfterEthereumTypeBlock2(t, d) + verifyAfterEthereumTypeBlock2(t, d, true) + block2.CoinSpecificData = nil if len(d.is.BlockTimes) != 2 { t.Fatal("Expecting is.BlockTimes 2, got ", len(d.is.BlockTimes)) @@ -350,7 +369,7 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { if err == nil || err.Error() != "Cannot disconnect blocks with height 4321000 and lower. It is necessary to rebuild index." { t.Fatal(err) } - verifyAfterEthereumTypeBlock2(t, d) + verifyAfterEthereumTypeBlock2(t, d, true) // disconnect the 2nd block, verify that the db contains only data from the 1st block with restored unspentTxs // and that the cached tx is removed @@ -373,10 +392,61 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { if err := d.ConnectBlock(block2); err != nil { t.Fatal(err) } - verifyAfterEthereumTypeBlock2(t, d) + verifyAfterEthereumTypeBlock2(t, d, false) if len(d.is.BlockTimes) != 2 { t.Fatal("Expecting is.BlockTimes 2, got ", len(d.is.BlockTimes)) } } + +func Test_BulkConnect_EthereumType(t *testing.T) { + d := setupRocksDB(t, &testEthereumParser{ + EthereumParser: ethereumTestnetParser(), + }) + defer closeAndDestroyRocksDB(t, d) + + bc, err := d.InitBulkConnect() + if err != nil { + t.Fatal(err) + } + + if d.is.DbState != common.DbStateInconsistent { + t.Fatal("DB not in DbStateInconsistent") + } + + if len(d.is.BlockTimes) != 0 { + t.Fatal("Expecting is.BlockTimes 0, got ", len(d.is.BlockTimes)) + } + + if err := bc.ConnectBlock(dbtestdata.GetTestEthereumTypeBlock1(d.chainParser), false); err != nil { + t.Fatal(err) + } + if err := checkColumn(d, cfBlockTxs, []keyPair{}); err != nil { + { + t.Fatal(err) + } + } + + // connect 2nd block, simulate InternalDataError + block2 := dbtestdata.GetTestEthereumTypeBlock2(d.chainParser) + block2.CoinSpecificData = &bchain.EthereumBlockSpecificData{InternalDataError: "test error"} + if err := bc.ConnectBlock(block2, true); err != nil { + t.Fatal(err) + } + block2.CoinSpecificData = nil + + if err := bc.Close(); err != nil { + t.Fatal(err) + } + + if d.is.DbState != common.DbStateOpen { + t.Fatal("DB not in DbStateOpen") + } + + verifyAfterEthereumTypeBlock2(t, d, true) + + if len(d.is.BlockTimes) != 4321002 { + t.Fatal("Expecting is.BlockTimes 4321002, got ", len(d.is.BlockTimes)) + } +} From 45a53e41a1f91c7a046eebe8f9dd1771c67f2a6a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 29 Dec 2021 23:53:27 +0100 Subject: [PATCH 043/530] Process ETH transaction failure reasons --- bchain/coins/eth/ethparser.go | 45 +++++++++- bchain/coins/eth/ethparser_test.go | 94 +++++++++++++++++++++ bchain/coins/eth/ethrpc.go | 45 +++++++--- bchain/types_ethereum_type.go | 1 + db/rocksdb_ethereumtype.go | 8 +- db/rocksdb_ethereumtype_test.go | 18 ++++ db/rocksdb_test.go | 2 +- tests/dbtestdata/dbtestdata_ethereumtype.go | 9 +- 8 files changed, 208 insertions(+), 14 deletions(-) diff --git a/bchain/coins/eth/ethparser.go b/bchain/coins/eth/ethparser.go index 8b8bdf1d94..2c975fac37 100644 --- a/bchain/coins/eth/ethparser.go +++ b/bchain/coins/eth/ethparser.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "math/big" "strconv" + "strings" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/protobuf/proto" @@ -88,7 +89,7 @@ func (p *EthereumParser) ethTxToTx(tx *bchain.RpcTransaction, receipt *bchain.Rp } if internalData != nil { // ignore empty internal data - if internalData.Type == bchain.CALL && len(internalData.Transfers) == 0 { + if internalData.Type == bchain.CALL && len(internalData.Transfers) == 0 && len(internalData.Error) == 0 { internalData = nil } else { if fixEIP55 { @@ -505,3 +506,45 @@ func GetEthereumTxDataFromSpecificData(coinSpecificData interface{}) *EthereumTx } return &etd } + +const errorOutputSignature = "08c379a0" + +// ParseErrorFromOutput takes output field from internal transaction data and extracts an error message from it +// the output must have errorOutputSignature to be parsed +func ParseErrorFromOutput(output string) string { + if has0xPrefix(output) { + output = output[2:] + } + if len(output) < 8+64+64+64 || output[:8] != errorOutputSignature { + return "" + } + return parseErc20StringProperty(nil, output[8:]) +} + +// PackInternalTransactionError packs common error messages to single byte to save DB space +func PackInternalTransactionError(e string) string { + if e == "execution reverted" { + return "\x01" + } + if e == "out of gas" { + return "\x02" + } + if e == "contract creation code storage out of gas" { + return "\x03" + } + if e == "max code size exceeded" { + return "\x04" + } + + return e +} + +// UnpackInternalTransactionError unpacks common error messages packed by PackInternalTransactionError +func UnpackInternalTransactionError(data []byte) string { + e := string(data) + e = strings.ReplaceAll(e, "\x01", "Reverted. ") + e = strings.ReplaceAll(e, "\x02", "Out of gas. ") + e = strings.ReplaceAll(e, "\x03", "Contract creation code storage out of gas. ") + e = strings.ReplaceAll(e, "\x04", "Max code size exceeded. ") + return strings.TrimSpace(e) +} diff --git a/bchain/coins/eth/ethparser_test.go b/bchain/coins/eth/ethparser_test.go index c990343cd7..ac54e6b3a4 100644 --- a/bchain/coins/eth/ethparser_test.go +++ b/bchain/coins/eth/ethparser_test.go @@ -400,3 +400,97 @@ func TestEthereumParser_GetEthereumTxData(t *testing.T) { }) } } + +func TestEthereumParser_ParseErrorFromOutput(t *testing.T) { + tests := []struct { + name string + output string + want string + }{ + { + name: "ParseErrorFromOutput 1", + output: "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000031546f74616c206e756d626572206f662067726f757073206d7573742062652067726561746572207468616e207a65726f2e000000000000000000000000000000", + want: "Total number of groups must be greater than zero.", + }, + { + name: "ParseErrorFromOutput 2", + output: "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000126e6f7420656e6f7567682062616c616e63650000000000000000000000000000", + want: "not enough balance", + }, + { + name: "ParseErrorFromOutput empty", + output: "", + want: "", + }, + { + name: "ParseErrorFromOutput short", + output: "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012", + want: "", + }, + { + name: "ParseErrorFromOutput invalid signature", + output: "0x08c379b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000126e6f7420656e6f7567682062616c616e63650000000000000000000000000000", + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ParseErrorFromOutput(tt.output) + if got != tt.want { + t.Errorf("EthereumParser.ParseErrorFromOutput() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestEthereumParser_PackInternalTransactionError_UnpackInternalTransactionError(t *testing.T) { + tests := []struct { + name string + original string + packed string + unpacked string + }{ + { + name: "execution reverted", + original: "execution reverted", + packed: "\x01", + unpacked: "Reverted.", + }, + { + name: "out of gas", + original: "out of gas", + packed: "\x02", + unpacked: "Out of gas.", + }, + { + name: "contract creation code storage out of gas", + original: "contract creation code storage out of gas", + packed: "\x03", + unpacked: "Contract creation code storage out of gas.", + }, + { + name: "max code size exceeded", + original: "max code size exceeded", + packed: "\x04", + unpacked: "Max code size exceeded.", + }, + { + name: "unknown error", + original: "unknown error", + packed: "unknown error", + unpacked: "unknown error", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + packed := PackInternalTransactionError(tt.original) + if packed != tt.packed { + t.Errorf("EthereumParser.PackInternalTransactionError() = %v, want %v", packed, tt.packed) + } + unpacked := UnpackInternalTransactionError([]byte(packed)) + if unpacked != tt.unpacked { + t.Errorf("EthereumParser.UnpackInternalTransactionError() = %v, want %v", unpacked, tt.unpacked) + } + }) + } +} diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index df3dc52f81..ac3ad3d9c5 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -6,6 +6,7 @@ import ( "fmt" "math/big" "strconv" + "strings" "sync" "time" @@ -512,19 +513,20 @@ func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]* type rpcCallTrace struct { // CREATE, CREATE2, SELFDESTRUCT, CALL, CALLCODE, DELEGATECALL, STATICCALL - Type string `json:"type"` - From string `json:"from"` - To string `json:"to"` - Value string `json:"value"` - Error string `json:"error"` - Calls []rpcCallTrace `json:"calls"` + Type string `json:"type"` + From string `json:"from"` + To string `json:"to"` + Value string `json:"value"` + Error string `json:"error"` + Output string `json:"output"` + Calls []rpcCallTrace `json:"calls"` } type rpcTraceResult struct { Result rpcCallTrace `json:"result"` } -func (b *EthereumRPC) processCallTrace(call rpcCallTrace, d *bchain.EthereumInternalData) { +func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInternalData) { value, err := hexutil.DecodeBig(call.Value) if call.Type == "CREATE" { d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ @@ -548,8 +550,11 @@ func (b *EthereumRPC) processCallTrace(call rpcCallTrace, d *bchain.EthereumInte To: call.To, }) } + if call.Error != "" { + d.Error = call.Error + } for i := range call.Calls { - b.processCallTrace(call.Calls[i], d) + b.processCallTrace(&call.Calls[i], d) } } @@ -579,7 +584,28 @@ func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []b d.Type = bchain.SELFDESTRUCT } for j := range r.Calls { - b.processCallTrace(r.Calls[j], d) + b.processCallTrace(&r.Calls[j], d) + } + if r.Error != "" { + baseError := PackInternalTransactionError(r.Error) + if len(baseError) > 1 { + // n, _ := ethNumber(transactions[i].BlockNumber) + // glog.Infof("Internal Data Error %d %s: unknown base error %s", n, transactions[i].Hash, baseError) + baseError = strings.ToUpper(baseError[:1]) + baseError[1:] + ". " + } + outputError := ParseErrorFromOutput(r.Output) + if len(outputError) > 0 { + d.Error = baseError + strings.ToUpper(outputError[:1]) + outputError[1:] + } else { + traceError := PackInternalTransactionError(d.Error) + if traceError == baseError { + d.Error = baseError + } else { + d.Error = baseError + traceError + } + } + // n, _ := ethNumber(transactions[i].BlockNumber) + // glog.Infof("Internal Data Error %d %s: %s", n, transactions[i].Hash, UnpackInternalTransactionError([]byte(d.Error))) } } } @@ -719,7 +745,6 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { if err != nil { return nil, errors.Annotatef(err, "txid %v", txid) } - // TODO - handle internal tx btx, err = b.Parser.ethTxToTx(tx, &receipt, nil, time, confirmations, true) if err != nil { return nil, errors.Annotatef(err, "txid %v", txid) diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index 93bf3c97f0..f4ca166b75 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -27,6 +27,7 @@ type EthereumInternalData struct { Type EthereumInternalTransactionType `json:"type"` Contract string `json:"contract,omitempty"` Transfers []EthereumInternalTransfer `json:"transfers,omitempty"` + Error string } // Erc20Contract contains info about ERC20 contract diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index cde8eab02a..4abc0bd463 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -189,6 +189,7 @@ type ethInternalData struct { internalType bchain.EthereumInternalTransactionType contract bchain.AddressDescriptor transfers []ethInternalTransfer + errorMsg string } type ethBlockTx struct { @@ -242,6 +243,7 @@ func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses ad if eid.InternalData != nil { blockTx.internalData = ðInternalData{ internalType: eid.InternalData.Type, + errorMsg: eid.InternalData.Error, } // index contract creation if eid.InternalData.Type == bchain.CREATE { @@ -365,6 +367,9 @@ func packEthInternalData(data *ethInternalData) []byte { l = packBigint(&t.value, varBuf) buf = append(buf, varBuf[:l]...) } + if len(data.errorMsg) > 0 { + buf = append(buf, []byte(data.errorMsg)...) + } return buf } @@ -398,6 +403,7 @@ func (d *RocksDB) unpackEthInternalData(buf []byte) (*bchain.EthereumInternalDat t.Value, ll = unpackBigint(buf[l:]) l += ll } + id.Error = eth.UnpackInternalTransactionError(buf[l:]) return &id, nil } @@ -423,7 +429,7 @@ func (d *RocksDB) storeInternalDataEthereumType(wb *gorocksdb.WriteBatch, blockT for i := range blockTxs { blockTx := &blockTxs[i] if blockTx.internalData != nil { - wb.PutCF(d.cfh[cfInternalData], blockTx.btxID, packEthInternalData(blockTx.internalData)) + wb.PutCF(d.cfh[cfInternalData], blockTx.btxID, packEthInternalData(blockTx.internalData)) } } return nil diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index d3ce848b4b..fb5d70eb1e 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -163,6 +163,11 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa "00" + dbtestdata.EthAddr3e + dbtestdata.EthAddr3e + "030f4242", nil, }, + { + dbtestdata.EthTxidB2T1, + "00" + hex.EncodeToString([]byte(dbtestdata.EthTx3InternalData.Error)), + nil, + }, { dbtestdata.EthTxidB2T2, "05" + dbtestdata.EthAddrContract0d + @@ -231,6 +236,7 @@ func formatInternalData(in *bchain.EthereumInternalData) *bchain.EthereumInterna t.From = eth.EIP55AddressFromAddress(t.From) t.To = eth.EIP55AddressFromAddress(t.To) } + out.Error = eth.UnpackInternalTransactionError([]byte(in.Error)) return &out } @@ -295,6 +301,10 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { if err != nil || !reflect.DeepEqual(id, formatInternalData(dbtestdata.EthTx2InternalData)) { t.Errorf("GetEthereumInternalData(%s) = %+v, want %+v, err %v", dbtestdata.EthTxidB1T2, id, formatInternalData(dbtestdata.EthTx2InternalData), err) } + id, err = d.GetEthereumInternalData(dbtestdata.EthTxidB2T1) + if err != nil || !reflect.DeepEqual(id, formatInternalData(dbtestdata.EthTx3InternalData)) { + t.Errorf("GetEthereumInternalData(%s) = %+v, want %+v, err %v", dbtestdata.EthTxidB2T1, id, formatInternalData(dbtestdata.EthTx3InternalData), err) + } id, err = d.GetEthereumInternalData(dbtestdata.EthTxidB2T2) if err != nil || !reflect.DeepEqual(id, formatInternalData(dbtestdata.EthTx4InternalData)) { t.Errorf("GetEthereumInternalData(%s) = %+v, want %+v, err %v", dbtestdata.EthTxidB2T2, id, formatInternalData(dbtestdata.EthTx4InternalData), err) @@ -348,7 +358,15 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { // Test tx caching functionality, leave one tx in db to test cleanup in DisconnectBlock testTxCache(t, d, block1, &block1.Txs[0]) + // InternalData are not packed and stored in DB, remove them so that the test does not fail + esd, _ := block2.Txs[0].CoinSpecificData.(bchain.EthereumSpecificData) + eid := esd.InternalData + esd.InternalData = nil + block2.Txs[0].CoinSpecificData = esd testTxCache(t, d, block2, &block2.Txs[0]) + // restore InternalData + esd.InternalData = eid + block2.Txs[0].CoinSpecificData = esd if err = d.PutTx(&block2.Txs[1], block2.Height, block2.Txs[1].Blocktime); err != nil { t.Fatal(err) } diff --git a/db/rocksdb_test.go b/db/rocksdb_test.go index c74e8b2369..f511850195 100644 --- a/db/rocksdb_test.go +++ b/db/rocksdb_test.go @@ -515,7 +515,7 @@ func testTxCache(t *testing.T, d *RocksDB, b *bchain.Block, tx *bchain.Tx) { // Confirmations are not stored in the DB, set them from input tx gtx.Confirmations = tx.Confirmations if !reflect.DeepEqual(gtx, tx) { - t.Errorf("GetTx: %v, want %v", gtx, tx) + t.Errorf("GetTx: %+v, want %+v", gtx, tx) } if err := d.DeleteTx(tx.Txid); err != nil { t.Fatal(err) diff --git a/tests/dbtestdata/dbtestdata_ethereumtype.go b/tests/dbtestdata/dbtestdata_ethereumtype.go index 56e04ec58d..65683ec58a 100644 --- a/tests/dbtestdata/dbtestdata_ethereumtype.go +++ b/tests/dbtestdata/dbtestdata_ethereumtype.go @@ -54,6 +54,12 @@ var EthTx2InternalData = &bchain.EthereumInternalData{ }, } +var EthTx3InternalData = &bchain.EthereumInternalData{ + Type: bchain.CALL, + Transfers: []bchain.EthereumInternalTransfer{}, + Error: "\x01Something wrong", +} + var EthTx4InternalData = &bchain.EthereumInternalData{ Type: bchain.CREATE, Contract: EthAddrContract0d, @@ -127,7 +133,8 @@ func GetTestEthereumTypeBlock2(parser bchain.BlockChainParser) *bchain.Block { Confirmations: 1, }, Txs: unpackTxs([]packedAndInternal{{ - packed: EthTx3Packed, + packed: EthTx3Packed, + internal: EthTx3InternalData, }, { packed: EthTx4Packed, internal: EthTx4InternalData, From 9a0790a71d546520226c6edfc405400705a29f82 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 18 Jan 2022 22:19:49 +0100 Subject: [PATCH 044/530] Process ERC721 and ERC1155 tokens --- api/types.go | 40 +- api/worker.go | 35 +- bchain/baseparser.go | 4 +- bchain/coins/eth/contract.go | 329 +++++++ bchain/coins/eth/contract_test.go | 291 ++++++ bchain/coins/eth/dataparser.go | 58 ++ bchain/coins/eth/dataparser_test.go | 53 ++ bchain/coins/eth/erc20.go | 245 ----- bchain/coins/eth/erc20_test.go | 204 ----- bchain/coins/eth/ethparser.go | 19 +- bchain/coins/eth/ethrpc.go | 8 +- bchain/mempool_ethereum_type.go | 6 +- bchain/types.go | 29 +- bchain/types_ethereum_type.go | 23 +- db/rocksdb_ethereumtype.go | 935 ++++++++++++-------- db/rocksdb_ethereumtype_test.go | 739 +++++++++++++++- db/rocksdb_test.go | 4 +- docs/rocksdb.md | 27 +- server/websocket.go | 6 +- tests/dbtestdata/dbtestdata_ethereumtype.go | 71 +- 20 files changed, 2204 insertions(+), 922 deletions(-) create mode 100644 bchain/coins/eth/contract.go create mode 100644 bchain/coins/eth/contract_test.go create mode 100644 bchain/coins/eth/dataparser.go create mode 100644 bchain/coins/eth/dataparser_test.go delete mode 100644 bchain/coins/eth/erc20.go delete mode 100644 bchain/coins/eth/erc20_test.go diff --git a/api/types.go b/api/types.go index ce1ecd475a..3e7ffed5c3 100644 --- a/api/types.go +++ b/api/types.go @@ -138,11 +138,20 @@ type Vout struct { // TokenType specifies type of token type TokenType string -// ERC20TokenType is Ethereum ERC20 token -const ERC20TokenType TokenType = "ERC20" +// Token types +const ( + // Ethereum token types + ERC20TokenType TokenType = "ERC20" + ERC771TokenType TokenType = "ERC721" + ERC1155TokenType TokenType = "ERC1155" + + // XPUBAddressTokenType is address derived from xpub + XPUBAddressTokenType TokenType = "XPUBAddress" +) -// XPUBAddressTokenType is address derived from xpub -const XPUBAddressTokenType TokenType = "XPUBAddress" +// TokenTypeMap maps bchain.TokenTransferType to TokenType +// the map must match all bchain.TokenTransferTypes to avoid index out of range panic +var TokenTypeMap []TokenType = []TokenType{ERC20TokenType, ERC771TokenType, ERC1155TokenType} // Token contains info about tokens held by an address type Token struct { @@ -159,16 +168,23 @@ type Token struct { ContractIndex string `json:"-"` } +// TokenTransferValues contains values for ERC1155 contract +type TokenTransferValues struct { + Id *Amount `json:"id,omitempty"` + Value *Amount `json:"value,omitempty"` +} + // TokenTransfer contains info about a token transfer done in a transaction type TokenTransfer struct { - Type TokenType `json:"type"` - From string `json:"from"` - To string `json:"to"` - Token string `json:"token"` - Name string `json:"name"` - Symbol string `json:"symbol"` - Decimals int `json:"decimals"` - Value *Amount `json:"value"` + Type TokenType `json:"type"` + From string `json:"from"` + To string `json:"to"` + Token string `json:"token"` + Name string `json:"name"` + Symbol string `json:"symbol"` + Decimals int `json:"decimals"` + Value *Amount `json:"value,omitempty"` + Values []TokenTransferValues `json:"values,omitempty"` } // EthereumSpecific contains ethereum specific transaction data diff --git a/api/worker.go b/api/worker.go index ccd54a4307..19583aa97a 100644 --- a/api/worker.go +++ b/api/worker.go @@ -255,11 +255,11 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe } pValInSat = &valInSat } else if w.chainType == bchain.ChainEthereumType { - ets, err := w.chainParser.EthereumTypeGetErc20FromTx(bchainTx) + tokenTransfers, err := w.chainParser.EthereumTypeGetTokenTransfersFromTx(bchainTx) if err != nil { - glog.Errorf("GetErc20FromTx error %v, %v", err, bchainTx) + glog.Errorf("GetTokenTransfersFromTx error %v, %v", err, bchainTx) } - tokens = w.getTokensFromErc20(ets) + tokens = w.getEthereumTokensTransfers(tokenTransfers) ethTxData := eth.GetEthereumTxData(bchainTx) // mempool txs do not have fees yet if ethTxData.GasUsed != nil { @@ -381,7 +381,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, if len(mempoolTx.Vout) > 0 { valOutSat = mempoolTx.Vout[0].ValueSat } - tokens = w.getTokensFromErc20(mempoolTx.Erc20) + tokens = w.getEthereumTokensTransfers(mempoolTx.TokenTransfers) ethTxData := eth.GetEthereumTxDataFromSpecificData(mempoolTx.CoinSpecificData) ethSpecific = &EthereumSpecific{ GasLimit: ethTxData.GasLimit, @@ -410,29 +410,30 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, return r, nil } -func (w *Worker) getTokensFromErc20(erc20 []bchain.Erc20Transfer) []TokenTransfer { - tokens := make([]TokenTransfer, len(erc20)) - for i := range erc20 { - e := &erc20[i] - cd, err := w.chainParser.GetAddrDescFromAddress(e.Contract) +func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers) []TokenTransfer { + sort.Sort(transfers) + tokens := make([]TokenTransfer, len(transfers)) + for i := range transfers { + t := transfers[i] + cd, err := w.chainParser.GetAddrDescFromAddress(t.Contract) if err != nil { - glog.Errorf("GetAddrDescFromAddress error %v, contract %v", err, e.Contract) + glog.Errorf("GetAddrDescFromAddress error %v, contract %v", err, t.Contract) continue } erc20c, err := w.chain.EthereumTypeGetErc20ContractInfo(cd) if err != nil { - glog.Errorf("GetErc20ContractInfo error %v, contract %v", err, e.Contract) + glog.Errorf("GetErc20ContractInfo error %v, contract %v", err, t.Contract) } if erc20c == nil { - erc20c = &bchain.Erc20Contract{Name: e.Contract} + erc20c = &bchain.Erc20Contract{Name: t.Contract} } tokens[i] = TokenTransfer{ - Type: ERC20TokenType, - Token: e.Contract, - From: e.From, - To: e.To, + Type: TokenTypeMap[t.Type], + Token: t.Contract, + From: t.From, + To: t.To, Decimals: erc20c.Decimals, - Value: (*Amount)(&e.Tokens), + Value: (*Amount)(&t.Value), Name: erc20c.Name, Symbol: erc20c.Symbol, } diff --git a/bchain/baseparser.go b/bchain/baseparser.go index 0f1ebe57f3..3f342a5327 100644 --- a/bchain/baseparser.go +++ b/bchain/baseparser.go @@ -300,7 +300,7 @@ func (p *BaseParser) DeriveAddressDescriptorsFromTo(descriptor *XpubDescriptor, return nil, errors.New("Not supported") } -// EthereumTypeGetErc20FromTx is unsupported -func (p *BaseParser) EthereumTypeGetErc20FromTx(tx *Tx) ([]Erc20Transfer, error) { +// EthereumTypeGetTokenTransfersFromTx is unsupported +func (p *BaseParser) EthereumTypeGetTokenTransfersFromTx(tx *Tx) (TokenTransfers, error) { return nil, errors.New("Not supported") } diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go new file mode 100644 index 0000000000..11fe89d042 --- /dev/null +++ b/bchain/coins/eth/contract.go @@ -0,0 +1,329 @@ +package eth + +import ( + "context" + "math/big" + "strings" + "sync" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/golang/glog" + "github.com/juju/errors" + "github.com/trezor/blockbook/bchain" +) + +const erc20TransferMethodSignature = "0xa9059cbb" // transfer(address,uint256) +const erc721TransferFromMethodSignature = "0x23b872dd" // transferFrom(address,address,uint256) +const erc721SafeTransferFromMethodSignature = "0x42842e0e" // safeTransferFrom(address,address,uint256) +const erc721SafeTransferFromWithDataMethodSignature = "0xb88d4fde" // safeTransferFrom(address,address,uint256,bytes) + +const tokenTransferEventSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" +const tokenERC1155TransferSingleEventSignature = "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62" +const tokenERC1155TransferBatchEventSignature = "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb" + +const contractNameSignature = "0x06fdde03" +const contractSymbolSignature = "0x95d89b41" +const contractDecimalsSignature = "0x313ce567" +const contractBalanceOf = "0x70a08231" + +var cachedContracts = make(map[string]*bchain.Erc20Contract) +var cachedContractsMux sync.Mutex + +func addressFromPaddedHex(s string) (string, error) { + var t big.Int + var ok bool + if has0xPrefix(s) { + _, ok = t.SetString(s[2:], 16) + } else { + _, ok = t.SetString(s, 16) + } + if !ok { + return "", errors.New("Data is not a number") + } + a := ethcommon.BigToAddress(&t) + return a.String(), nil +} + +func processTransferEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { + tl := len(l.Topics) + var ttt bchain.TokenTransferType + var value big.Int + if tl == 3 { + ttt = bchain.ERC20 + _, ok := value.SetString(l.Data, 0) + if !ok { + return nil, errors.New("ERC20 log Data is not a number") + } + } else if tl == 4 { + ttt = bchain.ERC721 + _, ok := value.SetString(l.Topics[3], 0) + if !ok { + return nil, errors.New("ERC721 log Topics[3] is not a number") + } + } else { + return nil, nil + } + from, err := addressFromPaddedHex(l.Topics[1]) + if err != nil { + return nil, err + } + to, err := addressFromPaddedHex(l.Topics[2]) + if err != nil { + return nil, err + } + return &bchain.TokenTransfer{ + Type: ttt, + Contract: EIP55AddressFromAddress(l.Address), + From: EIP55AddressFromAddress(from), + To: EIP55AddressFromAddress(to), + Value: value, + }, nil +} + +func processERC1155TransferSingleEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { + from, err := addressFromPaddedHex(l.Topics[2]) + if err != nil { + return nil, err + } + to, err := addressFromPaddedHex(l.Topics[3]) + if err != nil { + return nil, err + } + var id, value big.Int + data := l.Data + if has0xPrefix(l.Data) { + data = data[2:] + } + _, ok := id.SetString(data[:64], 16) + if !ok { + return nil, errors.New("ERC1155 log Data id is not a number") + } + _, ok = value.SetString(data[64:128], 16) + if !ok { + return nil, errors.New("ERC1155 log Data value is not a number") + } + return &bchain.TokenTransfer{ + Type: bchain.ERC1155, + Contract: EIP55AddressFromAddress(l.Address), + From: EIP55AddressFromAddress(from), + To: EIP55AddressFromAddress(to), + IdValues: []bchain.TokenTransferIdValue{{Id: id, Value: value}}, + }, nil +} + +func processERC1155TransferBatchEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { + from, err := addressFromPaddedHex(l.Topics[2]) + if err != nil { + return nil, err + } + to, err := addressFromPaddedHex(l.Topics[3]) + if err != nil { + return nil, err + } + data := l.Data + if has0xPrefix(l.Data) { + data = data[2:] + } + var b big.Int + _, ok := b.SetString(data[:64], 16) + if !ok || !b.IsInt64() { + return nil, errors.New("ERC1155 TransferBatch, not a number") + } + offsetIds := int(b.Int64()) * 2 + _, ok = b.SetString(data[64:128], 16) + if !ok || !b.IsInt64() { + return nil, errors.New("ERC1155 TransferBatch, not a number") + } + offsetValues := int(b.Int64()) * 2 + _, ok = b.SetString(data[offsetIds:offsetIds+64], 16) + if !ok || !b.IsInt64() { + return nil, errors.New("ERC1155 TransferBatch, not a number") + } + countIds := int(b.Int64()) + _, ok = b.SetString(data[offsetValues:offsetValues+64], 16) + if !ok || !b.IsInt64() { + return nil, errors.New("ERC1155 TransferBatch, not a number") + } + countValues := int(b.Int64()) + if countIds != countValues { + return nil, errors.New("ERC1155 TransferBatch, count values and ids does not match") + } + idValues := make([]bchain.TokenTransferIdValue, countValues) + for i := 0; i < countValues; i++ { + var id, value big.Int + o := offsetIds + 64 + 64*i + _, ok := id.SetString(data[o:o+64], 16) + if !ok { + return nil, errors.New("ERC1155 log Data id is not a number") + } + o = offsetValues + 64 + 64*i + _, ok = value.SetString(data[o:o+64], 16) + if !ok { + return nil, errors.New("ERC1155 log Data value is not a number") + } + idValues[i] = bchain.TokenTransferIdValue{Id: id, Value: value} + } + return &bchain.TokenTransfer{ + Type: bchain.ERC1155, + Contract: EIP55AddressFromAddress(l.Address), + From: EIP55AddressFromAddress(from), + To: EIP55AddressFromAddress(to), + IdValues: idValues, + }, nil +} +func contractGetTransfersFromLog(logs []*bchain.RpcLog) (bchain.TokenTransfers, error) { + var r bchain.TokenTransfers + var tt *bchain.TokenTransfer + var err error + for _, l := range logs { + tl := len(l.Topics) + if tl > 0 { + signature := l.Topics[0] + if signature == tokenTransferEventSignature { + tt, err = processTransferEvent(l) + } else if signature == tokenERC1155TransferSingleEventSignature && tl == 4 { + tt, err = processERC1155TransferSingleEvent(l) + } else if signature == tokenERC1155TransferBatchEventSignature { + tt, err = processERC1155TransferBatchEvent(l) + } else { + continue + } + if err != nil { + return nil, err + } + if tt != nil { + r = append(r, tt) + } + } + } + return r, nil +} + +func contractGetTransfersFromTx(tx *bchain.RpcTransaction) (bchain.TokenTransfers, error) { + var r bchain.TokenTransfers + if len(tx.Payload) == 10+128 && strings.HasPrefix(tx.Payload, erc20TransferMethodSignature) { + to, err := addressFromPaddedHex(tx.Payload[10 : 10+64]) + if err != nil { + return nil, err + } + var t big.Int + _, ok := t.SetString(tx.Payload[10+64:], 16) + if !ok { + return nil, errors.New("Data is not a number") + } + r = append(r, &bchain.TokenTransfer{ + Type: bchain.ERC20, + Contract: EIP55AddressFromAddress(tx.To), + From: EIP55AddressFromAddress(tx.From), + To: EIP55AddressFromAddress(to), + Value: t, + }) + } else if len(tx.Payload) >= 10+192 && + (strings.HasPrefix(tx.Payload, erc721TransferFromMethodSignature) || + strings.HasPrefix(tx.Payload, erc721SafeTransferFromMethodSignature) || + strings.HasPrefix(tx.Payload, erc721SafeTransferFromWithDataMethodSignature)) { + from, err := addressFromPaddedHex(tx.Payload[10 : 10+64]) + if err != nil { + return nil, err + } + to, err := addressFromPaddedHex(tx.Payload[10+64 : 10+128]) + if err != nil { + return nil, err + } + var t big.Int + _, ok := t.SetString(tx.Payload[10+128:10+192], 16) + if !ok { + return nil, errors.New("Data is not a number") + } + r = append(r, &bchain.TokenTransfer{ + Type: bchain.ERC721, + Contract: EIP55AddressFromAddress(tx.To), + From: EIP55AddressFromAddress(from), + To: EIP55AddressFromAddress(to), + Value: t, + }) + } + return r, nil +} + +func (b *EthereumRPC) ethCall(data, to string) (string, error) { + ctx, cancel := context.WithTimeout(context.Background(), b.timeout) + defer cancel() + var r string + err := b.rpc.CallContext(ctx, &r, "eth_call", map[string]interface{}{ + "data": data, + "to": to, + }, "latest") + if err != nil { + return "", err + } + return r, nil +} + +// EthereumTypeGetErc20ContractInfo returns information about ERC20 contract +func (b *EthereumRPC) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.Erc20Contract, error) { + cds := string(contractDesc) + cachedContractsMux.Lock() + contract, found := cachedContracts[cds] + cachedContractsMux.Unlock() + if !found { + address := EIP55Address(contractDesc) + data, err := b.ethCall(contractNameSignature, address) + if err != nil { + // ignore the error from the eth_call - since geth v1.9.15 they changed the behavior + // and returning error "execution reverted" for some non contract addresses + // https://github.com/ethereum/go-ethereum/issues/21249#issuecomment-648647672 + glog.Warning(errors.Annotatef(err, "erc20NameSignature %v", address)) + return nil, nil + // return nil, errors.Annotatef(err, "erc20NameSignature %v", address) + } + name := parseSimpleStringProperty(data) + if name != "" { + data, err = b.ethCall(contractSymbolSignature, address) + if err != nil { + glog.Warning(errors.Annotatef(err, "erc20SymbolSignature %v", address)) + return nil, nil + // return nil, errors.Annotatef(err, "erc20SymbolSignature %v", address) + } + symbol := parseSimpleStringProperty(data) + data, err = b.ethCall(contractDecimalsSignature, address) + if err != nil { + glog.Warning(errors.Annotatef(err, "erc20DecimalsSignature %v", address)) + // return nil, errors.Annotatef(err, "erc20DecimalsSignature %v", address) + } + contract = &bchain.Erc20Contract{ + Contract: address, + Name: name, + Symbol: symbol, + } + d := parseSimpleNumericProperty(data) + if d != nil { + contract.Decimals = int(uint8(d.Uint64())) + } else { + contract.Decimals = EtherAmountDecimalPoint + } + } else { + contract = nil + } + cachedContractsMux.Lock() + cachedContracts[cds] = contract + cachedContractsMux.Unlock() + } + return contract, nil +} + +// EthereumTypeGetErc20ContractBalance returns balance of ERC20 contract for given address +func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) { + addr := EIP55Address(addrDesc) + contract := EIP55Address(contractDesc) + req := contractBalanceOf + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr)-2:] + addr[2:] + data, err := b.ethCall(req, contract) + if err != nil { + return nil, err + } + r := parseSimpleNumericProperty(data) + if r == nil { + return nil, errors.New("Invalid balance") + } + return r, nil +} diff --git a/bchain/coins/eth/contract_test.go b/bchain/coins/eth/contract_test.go new file mode 100644 index 0000000000..2efa1eaef8 --- /dev/null +++ b/bchain/coins/eth/contract_test.go @@ -0,0 +1,291 @@ +//go:build unittest + +package eth + +import ( + "fmt" + "math/big" + "strings" + "testing" + + "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/tests/dbtestdata" +) + +func Test_contractGetTransfersFromLog(t *testing.T) { + tests := []struct { + name string + args []*bchain.RpcLog + want bchain.TokenTransfers + wantErr bool + }{ + { + name: "ERC20 transfer 1", + args: []*bchain.RpcLog{ + { + Address: "0x76a45e8976499ab9ae223cc584019341d5a84e96", + Topics: []string{ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000002aacf811ac1a60081ea39f7783c0d26c500871a8", + "0x000000000000000000000000e9a5216ff992cfa01594d43501a56e12769eb9d2", + }, + Data: "0x0000000000000000000000000000000000000000000000000000000000000123", + }, + }, + want: bchain.TokenTransfers{ + { + Contract: "0x76a45e8976499ab9ae223cc584019341d5a84e96", + From: "0x2aacf811ac1a60081ea39f7783c0d26c500871a8", + To: "0xe9a5216ff992cfa01594d43501a56e12769eb9d2", + Value: *big.NewInt(0x123), + }, + }, + }, + { + name: "ERC20 transfer 2", + args: []*bchain.RpcLog{ + { // Transfer + Address: "0x0d0f936ee4c93e25944694d6c121de94d9760f11", + Topics: []string{ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000006f44cceb49b4a5812d54b6f494fc2febf25511ed", + "0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d", + }, + Data: "0x0000000000000000000000000000000000000000000000006a8313d60b1f606b", + }, + { // Transfer + Address: "0xc778417e063141139fce010982780140aa0cd5ab", + Topics: []string{ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d", + "0x0000000000000000000000006f44cceb49b4a5812d54b6f494fc2febf25511ed", + }, + Data: "0x000000000000000000000000000000000000000000000000000308fd0e798ac0", + }, + { // not Transfer + Address: "0x479cc461fecd078f766ecc58533d6f69580cf3ac", + Topics: []string{ + "0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3", + "0x0000000000000000000000006f44cceb49b4a5812d54b6f494fc2febf25511ed", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f", + }, + Data: "0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000", + }, + { // not Transfer + Address: "0x0d0f936ee4c93e25944694d6c121de94d9760f11", + Topics: []string{ + "0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3", + "0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b", + "0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa", + }, + Data: "0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d000000000000000000000000c778417e063141139fce010982780140aa0cd5ab0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + }, + want: bchain.TokenTransfers{ + { + Contract: "0x0d0f936ee4c93e25944694d6c121de94d9760f11", + From: "0x6f44cceb49b4a5812d54b6f494fc2febf25511ed", + To: "0x4bda106325c335df99eab7fe363cac8a0ba2a24d", + Value: *big.NewInt(0x6a8313d60b1f606b), + }, + { + Contract: "0xc778417e063141139fce010982780140aa0cd5ab", + From: "0x4bda106325c335df99eab7fe363cac8a0ba2a24d", + To: "0x6f44cceb49b4a5812d54b6f494fc2febf25511ed", + Value: *big.NewInt(0x308fd0e798ac0), + }, + }, + }, + { + name: "ERC721 transfer 1", + args: []*bchain.RpcLog{ + { // Approval + Address: "0x5689b918D34C038901870105A6C7fc24744D31eB", + Topics: []string{ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x0000000000000000000000000a206d4d5ff79cb5069def7fe3598421cff09391", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000001396", + }, + Data: "0x", + }, + { // Transfer + Address: "0x5689b918D34C038901870105A6C7fc24744D31eB", + Topics: []string{ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000a206d4d5ff79cb5069def7fe3598421cff09391", + "0x0000000000000000000000006a016d7eec560549ffa0fbdb7f15c2b27302087f", + "0x0000000000000000000000000000000000000000000000000000000000001396", + }, + Data: "0x", + }, + { // OrdersMatched + Address: "0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b", + Topics: []string{ + "0xc4109843e0b7d514e4c093114b863f8e7d8d9a458c372cd51bfe526b588006c9", + "0x0000000000000000000000000a206d4d5ff79cb5069def7fe3598421cff09391", + "0x0000000000000000000000006a016d7eec560549ffa0fbdb7f15c2b27302087f", + "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + Data: "0x000000000000000000000000000000000000000000000000000000000000000069d3f0cc25f121f2aa96215f51ec4b4f1966f2d2ffbd3d8d8a45ad27b1c90323000000000000000000000000000000000000000000000000008e1bc9bf040000", + }, + }, + want: bchain.TokenTransfers{ + { + Type: bchain.ERC721, + Contract: "0x5689b918D34C038901870105A6C7fc24744D31eB", + From: "0x0a206d4d5ff79cb5069def7fe3598421cff09391", + To: "0x6a016d7eec560549ffa0fbdb7f15c2b27302087f", + Value: *big.NewInt(0x1396), + }, + }, + }, + { + name: "ERC1155 TransferSingle", + args: []*bchain.RpcLog{ + { // Transfer + Address: "0x6Fd712E3A5B556654044608F9129040A4839E36c", + Topics: []string{ + "0x5f9832c7244497a64c11c4a4f7597934bdf02b0361c54ad8e90091c2ce1f9e3c", + }, + Data: "0x000000000000000000000000a3950b823cb063dd9afc0d27f35008b805b3ed530000000000000000000000004392faf3bb96b5694ecc6ef64726f61cdd4bb0ec000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001", + }, + { // TransferSingle + Address: "0x6Fd712E3A5B556654044608F9129040A4839E36c", + Topics: []string{ + "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62", + "0x0000000000000000000000009248a6048a58db9f0212dc7cd85ee8741128be72", + "0x000000000000000000000000a3950b823cb063dd9afc0d27f35008b805b3ed53", + "0x0000000000000000000000004392faf3bb96b5694ecc6ef64726f61cdd4bb0ec", + }, + Data: "0x00000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000011", + }, + { // unknown + Address: "0x9248A6048a58db9f0212dC7CD85eE8741128be72", + Topics: []string{ + "0x0b7bef9468bee71526deef3cbbded0ec1a0aa3d5a3e81eaffb0e758552b33199", + }, + Data: "0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000a3950b823cb063dd9afc0d27f35008b805b3ed530000000000000000000000004392faf3bb96b5694ecc6ef64726f61cdd4bb0ec0000000000000000000000000000000000000000000000000000000000000001", + }, + }, + want: bchain.TokenTransfers{ + { + Type: bchain.ERC1155, + Contract: "0x6Fd712E3A5B556654044608F9129040A4839E36c", + From: "0xa3950b823cb063dd9afc0d27f35008b805b3ed53", + To: "0x4392faf3bb96b5694ecc6ef64726f61cdd4bb0ec", + IdValues: []bchain.TokenTransferIdValue{{Id: *big.NewInt(150), Value: *big.NewInt(0x11)}}, + }, + }, + }, + { + name: "ERC1155 TransferBatch", + args: []*bchain.RpcLog{ + { // TransferBatch + Address: "0x6c42C26a081c2F509F8bb68fb7Ac3062311cCfB7", + Topics: []string{ + "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb", + "0x0000000000000000000000005dc6288b35e0807a3d6feb89b3a2ff4ab773168e", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000005dc6288b35e0807a3d6feb89b3a2ff4ab773168e", + }, + Data: "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000006f0000000000000000000000000000000000000000000000000000000000000076a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a", + }, + }, + want: bchain.TokenTransfers{ + { + Type: bchain.ERC1155, + Contract: "0x6c42c26a081c2f509f8bb68fb7ac3062311ccfb7", + From: "0x0000000000000000000000000000000000000000", + To: "0x5dc6288b35e0807a3d6feb89b3a2ff4ab773168e", + IdValues: []bchain.TokenTransferIdValue{ + {Id: *big.NewInt(1776), Value: *big.NewInt(1)}, + {Id: *big.NewInt(1898), Value: *big.NewInt(10)}, + }, + }, + }, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := contractGetTransfersFromLog(tt.args) + if (err != nil) != tt.wantErr { + t.Errorf("contractGetTransfersFromLog error = %v, wantErr %v", err, tt.wantErr) + return + } + if len(got) != len(tt.want) { + t.Errorf("contractGetTransfersFromLog len not same, %+v, want %+v", got, tt.want) + } + for i := range got { + // the addresses could have different case + if strings.ToLower(fmt.Sprint(got[i])) != strings.ToLower(fmt.Sprint(tt.want[i])) { + t.Errorf("contractGetTransfersFromLog %d = %+v, want %+v", i, got[i], tt.want[i]) + } + + } + }) + } +} + +func Test_contractGetTransfersFromTx(t *testing.T) { + p := NewEthereumParser(1) + b1 := dbtestdata.GetTestEthereumTypeBlock1(p) + b2 := dbtestdata.GetTestEthereumTypeBlock2(p) + bn, _ := new(big.Int).SetString("21e19e0c9bab2400000", 16) + tests := []struct { + name string + args *bchain.RpcTransaction + want bchain.TokenTransfers + }{ + { + name: "no contract transfer", + args: (b1.Txs[0].CoinSpecificData.(bchain.EthereumSpecificData)).Tx, + want: bchain.TokenTransfers{}, + }, + { + name: "ERC20 transfer", + args: (b1.Txs[1].CoinSpecificData.(bchain.EthereumSpecificData)).Tx, + want: bchain.TokenTransfers{ + { + Type: bchain.ERC20, + Contract: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2", + From: "0x20cd153de35d469ba46127a0c8f18626b59a256a", + To: "0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f", + Value: *bn, + }, + }, + }, + { + name: "ERC721 transferFrom", + args: (b2.Txs[2].CoinSpecificData.(bchain.EthereumSpecificData)).Tx, + want: bchain.TokenTransfers{ + { + Type: bchain.ERC721, + Contract: "0xcda9fc258358ecaa88845f19af595e908bb7efe9", + From: "0x837e3f699d85a4b0b99894567e9233dfb1dcb081", + To: "0x7b62eb7fe80350dc7ec945c0b73242cb9877fb1b", + Value: *big.NewInt(1), + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := contractGetTransfersFromTx(tt.args) + if err != nil { + t.Errorf("contractGetTransfersFromTx error = %v", err) + return + } + if len(got) != len(tt.want) { + t.Errorf("contractGetTransfersFromTx len not same, %+v, want %+v", got, tt.want) + } + for i := range got { + // the addresses could have different case + if strings.ToLower(fmt.Sprint(got[i])) != strings.ToLower(fmt.Sprint(tt.want[i])) { + t.Errorf("contractGetTransfersFromTx %d = %+v, want %+v", i, got[i], tt.want[i]) + } + + } + }) + } +} diff --git a/bchain/coins/eth/dataparser.go b/bchain/coins/eth/dataparser.go new file mode 100644 index 0000000000..399ef2d1ae --- /dev/null +++ b/bchain/coins/eth/dataparser.go @@ -0,0 +1,58 @@ +package eth + +import ( + "bytes" + "encoding/hex" + "math/big" + "unicode/utf8" +) + +func parseSimpleNumericProperty(data string) *big.Int { + if has0xPrefix(data) { + data = data[2:] + } + if len(data) > 64 { + data = data[:64] + } + if len(data) == 64 { + var n big.Int + _, ok := n.SetString(data, 16) + if ok { + return &n + } + } + return nil +} + +func parseSimpleStringProperty(data string) string { + if has0xPrefix(data) { + data = data[2:] + } + if len(data) > 128 { + n := parseSimpleNumericProperty(data[64:128]) + if n != nil { + l := n.Uint64() + if l > 0 && 2*int(l) <= len(data)-128 { + b, err := hex.DecodeString(data[128 : 128+2*l]) + if err == nil { + return string(b) + } + } + } + } + // allow string properties as UTF-8 data + b, err := hex.DecodeString(data) + if err == nil { + i := bytes.Index(b, []byte{0}) + if i > 32 { + i = 32 + } + if i > 0 { + b = b[:i] + } + if utf8.Valid(b) { + return string(b) + } + } + return "" +} diff --git a/bchain/coins/eth/dataparser_test.go b/bchain/coins/eth/dataparser_test.go new file mode 100644 index 0000000000..9af84c1f4d --- /dev/null +++ b/bchain/coins/eth/dataparser_test.go @@ -0,0 +1,53 @@ +//go:build unittest + +package eth + +import "testing" + +func Test_parseSimpleStringProperty(t *testing.T) { + tests := []struct { + name string + args string + want string + }{ + { + name: "1", + args: "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000758504c4f44444500000000000000000000000000000000000000000000000000", + want: "XPLODDE", + }, + { + name: "2", + args: "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000022426974436c617665202d20436f6e73756d657220416374697669747920546f6b656e00000000000000", + want: "BitClave - Consumer Activity Token", + }, + { + name: "short", + args: "0x44616920537461626c65636f696e2076312e3000000000000000000000000000", + want: "Dai Stablecoin v1.0", + }, + { + name: "short2", + args: "0x44616920537461626c65636f696e2076312e3020444444444444444444444444", + want: "Dai Stablecoin v1.0 DDDDDDDDDDDD", + }, + { + name: "long", + args: "0x556e6973776170205631000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + want: "Uniswap V1", + }, + { + name: "garbage", + args: "0x2234880850896048596206002535425366538144616734015984380565810000", + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := parseSimpleStringProperty(tt.args) + // the addresses could have different case + if got != tt.want { + t.Errorf("parseSimpleStringProperty = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/bchain/coins/eth/erc20.go b/bchain/coins/eth/erc20.go deleted file mode 100644 index 6971f70728..0000000000 --- a/bchain/coins/eth/erc20.go +++ /dev/null @@ -1,245 +0,0 @@ -package eth - -import ( - "bytes" - "context" - "encoding/hex" - "math/big" - "strings" - "sync" - "unicode/utf8" - - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/golang/glog" - "github.com/juju/errors" - "github.com/trezor/blockbook/bchain" -) - -var erc20abi = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","signature":"0x06fdde03"}, -{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","signature":"0x95d89b41"}, -{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function","signature":"0x313ce567"}, -{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function","signature":"0x18160ddd"}, -{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function","signature":"0x70a08231"}, -{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function","signature":"0xa9059cbb"}, -{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function","signature":"0x23b872dd"}, -{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function","signature":"0x095ea7b3"}, -{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function","signature":"0xdd62ed3e"}, -{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event","signature":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"}, -{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event","signature":"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"}, -{"inputs":[{"name":"_initialAmount","type":"uint256"},{"name":"_tokenName","type":"string"},{"name":"_decimalUnits","type":"uint8"},{"name":"_tokenSymbol","type":"string"}],"payable":false,"type":"constructor"}, -{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function","signature":"0xcae9ca51"}, -{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","signature":"0x54fd4d50"}]` - -// doing the parsing/processing without using go-ethereum/accounts/abi library, it is simple to get data from Transfer event -const erc20TransferMethodSignature = "0xa9059cbb" -const erc20TransferEventSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" -const erc20NameSignature = "0x06fdde03" -const erc20SymbolSignature = "0x95d89b41" -const erc20DecimalsSignature = "0x313ce567" -const erc20BalanceOf = "0x70a08231" - -var cachedContracts = make(map[string]*bchain.Erc20Contract) -var cachedContractsMux sync.Mutex - -func addressFromPaddedHex(s string) (string, error) { - var t big.Int - var ok bool - if has0xPrefix(s) { - _, ok = t.SetString(s[2:], 16) - } else { - _, ok = t.SetString(s, 16) - } - if !ok { - return "", errors.New("Data is not a number") - } - a := ethcommon.BigToAddress(&t) - return a.String(), nil -} - -func erc20GetTransfersFromLog(logs []*bchain.RpcLog) ([]bchain.Erc20Transfer, error) { - var r []bchain.Erc20Transfer - for _, l := range logs { - if len(l.Topics) == 3 && l.Topics[0] == erc20TransferEventSignature { - var t big.Int - _, ok := t.SetString(l.Data, 0) - if !ok { - return nil, errors.New("Data is not a number") - } - from, err := addressFromPaddedHex(l.Topics[1]) - if err != nil { - return nil, err - } - to, err := addressFromPaddedHex(l.Topics[2]) - if err != nil { - return nil, err - } - r = append(r, bchain.Erc20Transfer{ - Contract: EIP55AddressFromAddress(l.Address), - From: EIP55AddressFromAddress(from), - To: EIP55AddressFromAddress(to), - Tokens: t, - }) - } - } - return r, nil -} - -func erc20GetTransfersFromTx(tx *bchain.RpcTransaction) ([]bchain.Erc20Transfer, error) { - var r []bchain.Erc20Transfer - if len(tx.Payload) == 128+len(erc20TransferMethodSignature) && strings.HasPrefix(tx.Payload, erc20TransferMethodSignature) { - to, err := addressFromPaddedHex(tx.Payload[len(erc20TransferMethodSignature) : 64+len(erc20TransferMethodSignature)]) - if err != nil { - return nil, err - } - var t big.Int - _, ok := t.SetString(tx.Payload[len(erc20TransferMethodSignature)+64:], 16) - if !ok { - return nil, errors.New("Data is not a number") - } - r = append(r, bchain.Erc20Transfer{ - Contract: EIP55AddressFromAddress(tx.To), - From: EIP55AddressFromAddress(tx.From), - To: EIP55AddressFromAddress(to), - Tokens: t, - }) - } - return r, nil -} - -func (b *EthereumRPC) ethCall(data, to string) (string, error) { - ctx, cancel := context.WithTimeout(context.Background(), b.timeout) - defer cancel() - var r string - err := b.rpc.CallContext(ctx, &r, "eth_call", map[string]interface{}{ - "data": data, - "to": to, - }, "latest") - if err != nil { - return "", err - } - return r, nil -} - -func parseErc20NumericProperty(contractDesc bchain.AddressDescriptor, data string) *big.Int { - if has0xPrefix(data) { - data = data[2:] - } - if len(data) > 64 { - data = data[:64] - } - if len(data) == 64 { - var n big.Int - _, ok := n.SetString(data, 16) - if ok { - return &n - } - } - if glog.V(1) { - glog.Warning("Cannot parse '", data, "' for contract ", contractDesc) - } - return nil -} - -func parseErc20StringProperty(contractDesc bchain.AddressDescriptor, data string) string { - if has0xPrefix(data) { - data = data[2:] - } - if len(data) > 128 { - n := parseErc20NumericProperty(contractDesc, data[64:128]) - if n != nil { - l := n.Uint64() - if l > 0 && 2*int(l) <= len(data)-128 { - b, err := hex.DecodeString(data[128 : 128+2*l]) - if err == nil { - return string(b) - } - } - } - } - // allow string properties as UTF-8 data - b, err := hex.DecodeString(data) - if err == nil { - i := bytes.Index(b, []byte{0}) - if i > 32 { - i = 32 - } - if i > 0 { - b = b[:i] - } - if utf8.Valid(b) { - return string(b) - } - } - if glog.V(1) { - glog.Warning("Cannot parse '", data, "' for contract ", contractDesc) - } - return "" -} - -// EthereumTypeGetErc20ContractInfo returns information about ERC20 contract -func (b *EthereumRPC) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.Erc20Contract, error) { - cds := string(contractDesc) - cachedContractsMux.Lock() - contract, found := cachedContracts[cds] - cachedContractsMux.Unlock() - if !found { - address := EIP55Address(contractDesc) - data, err := b.ethCall(erc20NameSignature, address) - if err != nil { - // ignore the error from the eth_call - since geth v1.9.15 they changed the behavior - // and returning error "execution reverted" for some non contract addresses - // https://github.com/ethereum/go-ethereum/issues/21249#issuecomment-648647672 - glog.Warning(errors.Annotatef(err, "erc20NameSignature %v", address)) - return nil, nil - // return nil, errors.Annotatef(err, "erc20NameSignature %v", address) - } - name := parseErc20StringProperty(contractDesc, data) - if name != "" { - data, err = b.ethCall(erc20SymbolSignature, address) - if err != nil { - glog.Warning(errors.Annotatef(err, "erc20SymbolSignature %v", address)) - return nil, nil - // return nil, errors.Annotatef(err, "erc20SymbolSignature %v", address) - } - symbol := parseErc20StringProperty(contractDesc, data) - data, err = b.ethCall(erc20DecimalsSignature, address) - if err != nil { - glog.Warning(errors.Annotatef(err, "erc20DecimalsSignature %v", address)) - // return nil, errors.Annotatef(err, "erc20DecimalsSignature %v", address) - } - contract = &bchain.Erc20Contract{ - Contract: address, - Name: name, - Symbol: symbol, - } - d := parseErc20NumericProperty(contractDesc, data) - if d != nil { - contract.Decimals = int(uint8(d.Uint64())) - } else { - contract.Decimals = EtherAmountDecimalPoint - } - } else { - contract = nil - } - cachedContractsMux.Lock() - cachedContracts[cds] = contract - cachedContractsMux.Unlock() - } - return contract, nil -} - -// EthereumTypeGetErc20ContractBalance returns balance of ERC20 contract for given address -func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) { - addr := EIP55Address(addrDesc) - contract := EIP55Address(contractDesc) - req := erc20BalanceOf + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr)-2:] + addr[2:] - data, err := b.ethCall(req, contract) - if err != nil { - return nil, err - } - r := parseErc20NumericProperty(contractDesc, data) - if r == nil { - return nil, errors.New("Invalid balance") - } - return r, nil -} diff --git a/bchain/coins/eth/erc20_test.go b/bchain/coins/eth/erc20_test.go deleted file mode 100644 index 574144d498..0000000000 --- a/bchain/coins/eth/erc20_test.go +++ /dev/null @@ -1,204 +0,0 @@ -//go:build unittest - -package eth - -import ( - "fmt" - "math/big" - "strings" - "testing" - - "github.com/trezor/blockbook/bchain" - "github.com/trezor/blockbook/tests/dbtestdata" -) - -func TestErc20_erc20GetTransfersFromLog(t *testing.T) { - tests := []struct { - name string - args []*bchain.RpcLog - want []bchain.Erc20Transfer - wantErr bool - }{ - { - name: "1", - args: []*bchain.RpcLog{ - { - Address: "0x76a45e8976499ab9ae223cc584019341d5a84e96", - Topics: []string{ - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "0x0000000000000000000000002aacf811ac1a60081ea39f7783c0d26c500871a8", - "0x000000000000000000000000e9a5216ff992cfa01594d43501a56e12769eb9d2", - }, - Data: "0x0000000000000000000000000000000000000000000000000000000000000123", - }, - }, - want: []bchain.Erc20Transfer{ - { - Contract: "0x76a45e8976499ab9ae223cc584019341d5a84e96", - From: "0x2aacf811ac1a60081ea39f7783c0d26c500871a8", - To: "0xe9a5216ff992cfa01594d43501a56e12769eb9d2", - Tokens: *big.NewInt(0x123), - }, - }, - }, - { - name: "2", - args: []*bchain.RpcLog{ - { // Transfer - Address: "0x0d0f936ee4c93e25944694d6c121de94d9760f11", - Topics: []string{ - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "0x0000000000000000000000006f44cceb49b4a5812d54b6f494fc2febf25511ed", - "0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d", - }, - Data: "0x0000000000000000000000000000000000000000000000006a8313d60b1f606b", - }, - { // Transfer - Address: "0xc778417e063141139fce010982780140aa0cd5ab", - Topics: []string{ - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d", - "0x0000000000000000000000006f44cceb49b4a5812d54b6f494fc2febf25511ed", - }, - Data: "0x000000000000000000000000000000000000000000000000000308fd0e798ac0", - }, - { // not Transfer - Address: "0x479cc461fecd078f766ecc58533d6f69580cf3ac", - Topics: []string{ - "0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3", - "0x0000000000000000000000006f44cceb49b4a5812d54b6f494fc2febf25511ed", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f", - }, - Data: "0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000", - }, - { // not Transfer - Address: "0x0d0f936ee4c93e25944694d6c121de94d9760f11", - Topics: []string{ - "0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3", - "0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b", - "0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa", - }, - Data: "0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d000000000000000000000000c778417e063141139fce010982780140aa0cd5ab0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - }, - }, - want: []bchain.Erc20Transfer{ - { - Contract: "0x0d0f936ee4c93e25944694d6c121de94d9760f11", - From: "0x6f44cceb49b4a5812d54b6f494fc2febf25511ed", - To: "0x4bda106325c335df99eab7fe363cac8a0ba2a24d", - Tokens: *big.NewInt(0x6a8313d60b1f606b), - }, - { - Contract: "0xc778417e063141139fce010982780140aa0cd5ab", - From: "0x4bda106325c335df99eab7fe363cac8a0ba2a24d", - To: "0x6f44cceb49b4a5812d54b6f494fc2febf25511ed", - Tokens: *big.NewInt(0x308fd0e798ac0), - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := erc20GetTransfersFromLog(tt.args) - if (err != nil) != tt.wantErr { - t.Errorf("erc20GetTransfersFromLog error = %v, wantErr %v", err, tt.wantErr) - return - } - // the addresses could have different case - if strings.ToLower(fmt.Sprint(got)) != strings.ToLower(fmt.Sprint(tt.want)) { - t.Errorf("erc20GetTransfersFromLog = %+v, want %+v", got, tt.want) - } - }) - } -} - -func TestErc20_parseErc20StringProperty(t *testing.T) { - tests := []struct { - name string - args string - want string - }{ - { - name: "1", - args: "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000758504c4f44444500000000000000000000000000000000000000000000000000", - want: "XPLODDE", - }, - { - name: "2", - args: "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000022426974436c617665202d20436f6e73756d657220416374697669747920546f6b656e00000000000000", - want: "BitClave - Consumer Activity Token", - }, - { - name: "short", - args: "0x44616920537461626c65636f696e2076312e3000000000000000000000000000", - want: "Dai Stablecoin v1.0", - }, - { - name: "short2", - args: "0x44616920537461626c65636f696e2076312e3020444444444444444444444444", - want: "Dai Stablecoin v1.0 DDDDDDDDDDDD", - }, - { - name: "long", - args: "0x556e6973776170205631000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - want: "Uniswap V1", - }, - { - name: "garbage", - args: "0x2234880850896048596206002535425366538144616734015984380565810000", - want: "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := parseErc20StringProperty(nil, tt.args) - // the addresses could have different case - if got != tt.want { - t.Errorf("parseErc20StringProperty = %v, want %v", got, tt.want) - } - }) - } -} - -func TestErc20_erc20GetTransfersFromTx(t *testing.T) { - p := NewEthereumParser(1) - b := dbtestdata.GetTestEthereumTypeBlock1(p) - bn, _ := new(big.Int).SetString("21e19e0c9bab2400000", 16) - tests := []struct { - name string - args *bchain.RpcTransaction - want []bchain.Erc20Transfer - }{ - { - name: "0", - args: (b.Txs[0].CoinSpecificData.(bchain.EthereumSpecificData)).Tx, - want: []bchain.Erc20Transfer{}, - }, - { - name: "1", - args: (b.Txs[1].CoinSpecificData.(bchain.EthereumSpecificData)).Tx, - want: []bchain.Erc20Transfer{ - { - Contract: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2", - From: "0x20cd153de35d469ba46127a0c8f18626b59a256a", - To: "0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f", - Tokens: *bn, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := erc20GetTransfersFromTx(tt.args) - if err != nil { - t.Errorf("erc20GetTransfersFromTx error = %v", err) - return - } - // the addresses could have different case - if strings.ToLower(fmt.Sprint(got)) != strings.ToLower(fmt.Sprint(tt.want)) { - t.Errorf("erc20GetTransfersFromTx = %+v, want %+v", got, tt.want) - } - }) - } -} diff --git a/bchain/coins/eth/ethparser.go b/bchain/coins/eth/ethparser.go index 2c975fac37..92ed0054ba 100644 --- a/bchain/coins/eth/ethparser.go +++ b/bchain/coins/eth/ethparser.go @@ -13,9 +13,12 @@ import ( "golang.org/x/crypto/sha3" ) -// EthereumTypeAddressDescriptorLen - in case of EthereumType, the AddressDescriptor has fixed length +// EthereumTypeAddressDescriptorLen - the AddressDescriptor of EthereumType has fixed length const EthereumTypeAddressDescriptorLen = 20 +// EthereumTypeTxidLen - the length of Txid +const EthereumTypeTxidLen = 32 + // EtherAmountDecimalPoint defines number of decimal points in Ether amounts const EtherAmountDecimalPoint = 18 @@ -388,7 +391,7 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { // PackedTxidLen returns length in bytes of packed txid func (p *EthereumParser) PackedTxidLen() int { - return 32 + return EthereumTypeTxidLen } // PackTxid packs txid to byte array @@ -437,16 +440,16 @@ func GetHeightFromTx(tx *bchain.Tx) (uint32, error) { return uint32(n), nil } -// EthereumTypeGetErc20FromTx returns Erc20 data from bchain.Tx -func (p *EthereumParser) EthereumTypeGetErc20FromTx(tx *bchain.Tx) ([]bchain.Erc20Transfer, error) { - var r []bchain.Erc20Transfer +// EthereumTypeGetTokenTransfersFromTx returns contract transfers from bchain.Tx +func (p *EthereumParser) EthereumTypeGetTokenTransfersFromTx(tx *bchain.Tx) (bchain.TokenTransfers, error) { + var r bchain.TokenTransfers var err error csd, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData) if ok { if csd.Receipt != nil { - r, err = erc20GetTransfersFromLog(csd.Receipt.Logs) + r, err = contractGetTransfersFromLog(csd.Receipt.Logs) } else { - r, err = erc20GetTransfersFromTx(csd.Tx) + r, err = contractGetTransfersFromTx(csd.Tx) } if err != nil { return nil, err @@ -518,7 +521,7 @@ func ParseErrorFromOutput(output string) string { if len(output) < 8+64+64+64 || output[:8] != errorOutputSignature { return "" } - return parseErc20StringProperty(nil, output[8:]) + return parseSimpleStringProperty(output[8:]) } // PackInternalTransactionError packs common error messages to single byte to save DB space diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index ac3ad3d9c5..1b6f2803a2 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -491,14 +491,14 @@ func (b *EthereumRPC) getBlockRaw(hash string, height uint32, fullTxs bool) (jso return raw, nil } -func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]*bchain.RpcLog, error) { +func (b *EthereumRPC) getTokenTransferEventsForBlock(blockNumber string) (map[string][]*bchain.RpcLog, error) { ctx, cancel := context.WithTimeout(context.Background(), b.timeout) defer cancel() var logs []rpcLogWithTxHash err := b.rpc.CallContext(ctx, &logs, "eth_getLogs", map[string]interface{}{ "fromBlock": blockNumber, "toBlock": blockNumber, - "topics": []string{erc20TransferEventSignature}, + "topics": []string{tokenTransferEventSignature, tokenERC1155TransferSingleEventSignature, tokenERC1155TransferBatchEventSignature}, }) if err != nil { return nil, errors.Annotatef(err, "blockNumber %v", blockNumber) @@ -630,8 +630,8 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error if err != nil { return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) } - // get ERC20 events - logs, err := b.getERC20EventsForBlock(head.Number) + // get contract transfers events + logs, err := b.getTokenTransferEventsForBlock(head.Number) if err != nil { return nil, err } diff --git a/bchain/mempool_ethereum_type.go b/bchain/mempool_ethereum_type.go index 91fdeeac6e..23d333fd32 100644 --- a/bchain/mempool_ethereum_type.go +++ b/bchain/mempool_ethereum_type.go @@ -74,11 +74,11 @@ func (m *MempoolEthereumType) createTxEntry(txid string, txTime uint32) (txEntry addrIndexes, input.AddrDesc = appendAddress(addrIndexes, ^int32(i), a, parser) } } - t, err := parser.EthereumTypeGetErc20FromTx(tx) + t, err := parser.EthereumTypeGetTokenTransfersFromTx(tx) if err != nil { - glog.Error("GetErc20FromTx for tx ", txid, ", ", err) + glog.Error("GetGetTokenTransfersFromTx for tx ", txid, ", ", err) } else { - mtx.Erc20 = t + mtx.TokenTransfers = t for i := range t { addrIndexes, _ = appendAddress(addrIndexes, ^int32(i+1), t[i].From, parser) addrIndexes, _ = appendAddress(addrIndexes, int32(i+1), t[i].To, parser) diff --git a/bchain/types.go b/bchain/types.go index ae590abcd0..d2fe44c484 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -102,15 +102,24 @@ type MempoolVin struct { // MempoolTx is blockchain transaction in mempool // optimized for onNewTx notification type MempoolTx struct { - Hex string `json:"hex"` - Txid string `json:"txid"` - Version int32 `json:"version"` - LockTime uint32 `json:"locktime"` - Vin []MempoolVin `json:"vin"` - Vout []Vout `json:"vout"` - Blocktime int64 `json:"blocktime,omitempty"` - Erc20 []Erc20Transfer `json:"-"` - CoinSpecificData interface{} `json:"-"` + Hex string `json:"hex"` + Txid string `json:"txid"` + Version int32 `json:"version"` + LockTime uint32 `json:"locktime"` + Vin []MempoolVin `json:"vin"` + Vout []Vout `json:"vout"` + Blocktime int64 `json:"blocktime,omitempty"` + TokenTransfers TokenTransfers `json:"-"` + CoinSpecificData interface{} `json:"-"` +} + +// TokenTransfers is array of TokenTransfer +type TokenTransfers []*TokenTransfer + +func (a TokenTransfers) Len() int { return len(a) } +func (a TokenTransfers) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a TokenTransfers) Less(i, j int) bool { + return a[i].Type < a[j].Type } // Block is block header and list of transactions @@ -328,7 +337,7 @@ type BlockChainParser interface { DeriveAddressDescriptors(descriptor *XpubDescriptor, change uint32, indexes []uint32) ([]AddressDescriptor, error) DeriveAddressDescriptorsFromTo(descriptor *XpubDescriptor, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error) // EthereumType specific - EthereumTypeGetErc20FromTx(tx *Tx) ([]Erc20Transfer, error) + EthereumTypeGetTokenTransfersFromTx(tx *Tx) (TokenTransfers, error) } // Mempool defines common interface to mempool diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index f4ca166b75..4061eea7a8 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -22,6 +22,16 @@ const ( SELFDESTRUCT ) +// TokenTransferType - type of token transfer +type TokenTransferType int + +// TokenTransferType enumeration +const ( + ERC20 = TokenTransferType(iota) + ERC721 + ERC1155 +) + // EthereumInternalTransaction contains internal transfers type EthereumInternalData struct { Type EthereumInternalTransactionType `json:"type"` @@ -38,12 +48,19 @@ type Erc20Contract struct { Decimals int `json:"decimals"` } -// Erc20Transfer contains a single ERC20 token transfer -type Erc20Transfer struct { +type TokenTransferIdValue struct { + Id big.Int + Value big.Int +} + +// TokenTransfer contains a single ERC20/ERC721/ERC1155 token transfer +type TokenTransfer struct { + Type TokenTransferType Contract string From string To string - Tokens big.Int + Value big.Int + IdValues []TokenTransferIdValue } // RpcTransaction is returned by eth_getTransactionByHash diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index 4abc0bd463..03bbcf49e9 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "math/big" - vlq "github.com/bsm/go-vlq" "github.com/flier/gorocksdb" "github.com/golang/glog" "github.com/juju/errors" @@ -18,8 +17,12 @@ const ContractIndexOffset = 2 // AddrContract is Contract address with number of transactions done by given address type AddrContract struct { + Type bchain.TokenTransferType Contract bchain.AddressDescriptor Txs uint + Value big.Int // single value of ERC20 + Ids []big.Int // multiple ERC721 tokens + IdValues []bchain.TokenTransferIdValue // multiple ERC1155 tokens } // AddrContracts contains number of transactions and contracts for an address @@ -30,43 +33,45 @@ type AddrContracts struct { Contracts []AddrContract } -func (d *RocksDB) storeAddressContracts(wb *gorocksdb.WriteBatch, acm map[string]*AddrContracts) error { - buf := make([]byte, 64) - varBuf := make([]byte, vlq.MaxLen64) - for addrDesc, acs := range acm { - // address with 0 contracts is removed from db - happens on disconnect - if acs == nil || (acs.NonContractTxs == 0 && acs.InternalTxs == 0 && len(acs.Contracts) == 0) { - wb.DeleteCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc)) - } else { - buf = buf[:0] - l := packVaruint(acs.TotalTxs, varBuf) +// packAddrContract packs AddrContracts into a byte buffer +func packAddrContracts(acs *AddrContracts) []byte { + buf := make([]byte, 0, 128) + varBuf := make([]byte, maxPackedBigintBytes) + l := packVaruint(acs.TotalTxs, varBuf) + buf = append(buf, varBuf[:l]...) + l = packVaruint(acs.NonContractTxs, varBuf) + buf = append(buf, varBuf[:l]...) + l = packVaruint(acs.InternalTxs, varBuf) + buf = append(buf, varBuf[:l]...) + for _, ac := range acs.Contracts { + buf = append(buf, ac.Contract...) + l = packVaruint(uint(ac.Type)+ac.Txs<<2, varBuf) + buf = append(buf, varBuf[:l]...) + if ac.Type == bchain.ERC20 { + l = packBigint(&ac.Value, varBuf) buf = append(buf, varBuf[:l]...) - l = packVaruint(acs.NonContractTxs, varBuf) + } else if ac.Type == bchain.ERC721 { + l = packVaruint(uint(len(ac.Ids)), varBuf) buf = append(buf, varBuf[:l]...) - l = packVaruint(acs.InternalTxs, varBuf) + for i := range ac.Ids { + l = packBigint(&ac.Ids[i], varBuf) + buf = append(buf, varBuf[:l]...) + } + } else { // bchain.ERC1155 + l = packVaruint(uint(len(ac.IdValues)), varBuf) buf = append(buf, varBuf[:l]...) - for _, ac := range acs.Contracts { - buf = append(buf, ac.Contract...) - l = packVaruint(ac.Txs, varBuf) + for i := range ac.IdValues { + l = packBigint(&ac.IdValues[i].Id, varBuf) + buf = append(buf, varBuf[:l]...) + l = packBigint(&ac.IdValues[i].Value, varBuf) buf = append(buf, varBuf[:l]...) } - wb.PutCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc), buf) } } - return nil + return buf } -// GetAddrDescContracts returns AddrContracts for given addrDesc -func (d *RocksDB) GetAddrDescContracts(addrDesc bchain.AddressDescriptor) (*AddrContracts, error) { - val, err := d.db.GetCF(d.ro, d.cfh[cfAddressContracts], addrDesc) - if err != nil { - return nil, err - } - defer val.Free() - buf := val.Data() - if len(buf) == 0 { - return nil, nil - } +func unpackAddrContracts(buf []byte, addrDesc bchain.AddressDescriptor) (*AddrContracts, error) { tt, l := unpackVaruint(buf) buf = buf[l:] nct, l := unpackVaruint(buf) @@ -78,13 +83,43 @@ func (d *RocksDB) GetAddrDescContracts(addrDesc bchain.AddressDescriptor) (*Addr if len(buf) < eth.EthereumTypeAddressDescriptorLen { return nil, errors.New("Invalid data stored in cfAddressContracts for AddrDesc " + addrDesc.String()) } - txs, l := unpackVaruint(buf[eth.EthereumTypeAddressDescriptorLen:]) contract := append(bchain.AddressDescriptor(nil), buf[:eth.EthereumTypeAddressDescriptorLen]...) - c = append(c, AddrContract{ + txs, l := unpackVaruint(buf[eth.EthereumTypeAddressDescriptorLen:]) + buf = buf[eth.EthereumTypeAddressDescriptorLen+l:] + ttt := bchain.TokenTransferType(txs & 3) + txs >>= 2 + ac := AddrContract{ + Type: ttt, Contract: contract, Txs: txs, - }) - buf = buf[eth.EthereumTypeAddressDescriptorLen+l:] + } + if ttt == bchain.ERC20 { + b, ll := unpackBigint(buf) + buf = buf[ll:] + ac.Value = b + } else { + len, ll := unpackVaruint(buf) + buf = buf[ll:] + if ttt == bchain.ERC721 { + ac.Ids = make([]big.Int, len) + for i := uint(0); i < len; i++ { + b, ll := unpackBigint(buf) + buf = buf[ll:] + ac.Ids[i] = b + } + } else { + ac.IdValues = make([]bchain.TokenTransferIdValue, len) + for i := uint(0); i < len; i++ { + b, ll := unpackBigint(buf) + buf = buf[ll:] + ac.IdValues[i].Id = b + b, ll = unpackBigint(buf) + buf = buf[ll:] + ac.IdValues[i].Value = b + } + } + } + c = append(c, ac) } return &AddrContracts{ TotalTxs: tt, @@ -94,6 +129,33 @@ func (d *RocksDB) GetAddrDescContracts(addrDesc bchain.AddressDescriptor) (*Addr }, nil } +func (d *RocksDB) storeAddressContracts(wb *gorocksdb.WriteBatch, acm map[string]*AddrContracts) error { + for addrDesc, acs := range acm { + // address with 0 contracts is removed from db - happens on disconnect + if acs == nil || (acs.NonContractTxs == 0 && acs.InternalTxs == 0 && len(acs.Contracts) == 0) { + wb.DeleteCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc)) + } else { + buf := packAddrContracts(acs) + wb.PutCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc), buf) + } + } + return nil +} + +// GetAddrDescContracts returns AddrContracts for given addrDesc +func (d *RocksDB) GetAddrDescContracts(addrDesc bchain.AddressDescriptor) (*AddrContracts, error) { + val, err := d.db.GetCF(d.ro, d.cfh[cfAddressContracts], addrDesc) + if err != nil { + return nil, err + } + defer val.Free() + buf := val.Data() + if len(buf) == 0 { + return nil, nil + } + return unpackAddrContracts(buf, addrDesc) +} + func findContractInAddressContracts(contract bchain.AddressDescriptor, contracts []AddrContract) (int, bool) { for i := range contracts { if bytes.Equal(contract, contracts[i].Contract) { @@ -117,7 +179,94 @@ const transferFrom = ^int32(0) const internalTransferTo = int32(1) const internalTransferFrom = ^int32(1) -func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.AddressDescriptor, btxID []byte, index int32, contract bchain.AddressDescriptor, addresses addressesMap, addressContracts map[string]*AddrContracts, addTxCount bool) error { +// addToAddressesMapEthereumType maintains mapping between addresses and transactions in one block +// it ensures that each index is there only once, there can be for example multiple internal transactions of the same address +// the return value is true if the tx was processed before, to not to count the tx multiple times +func addToAddressesMapEthereumType(addresses addressesMap, strAddrDesc string, btxID []byte, index int32) bool { + // check that the address was already processed in this block + // if not found, it has certainly not been counted + at, found := addresses[strAddrDesc] + if found { + // if the tx is already in the slice, append the index to the array of indexes + for i, t := range at { + if bytes.Equal(btxID, t.btxID) { + for _, existing := range t.indexes { + if existing == index { + return true + } + } + at[i].indexes = append(t.indexes, index) + return true + } + } + } + addresses[strAddrDesc] = append(at, txIndexes{ + btxID: btxID, + indexes: []int32{index}, + }) + return false +} + +func addToContract(c *AddrContract, contractIndex int, index int32, contract bchain.AddressDescriptor, transfer *bchain.TokenTransfer, addTxCount bool) int32 { + var aggregate func(*big.Int, *big.Int) + // index 0 is for ETH transfers, index 1 (InternalTxIndexOffset) is for internal transfers, contract indexes start with 2 (ContractIndexOffset) + if index < 0 { + index = ^int32(contractIndex + ContractIndexOffset) + aggregate = func(s, v *big.Int) { + s.Sub(s, v) + if s.Sign() < 0 { + glog.Warningf("rocksdb: addToContracts: contract %s, from %s, negative aggregate", transfer.Contract, transfer.From) + s.SetInt64(0) + } + } + } else { + index = int32(contractIndex + ContractIndexOffset) + aggregate = func(s, v *big.Int) { + s.Add(s, v) + } + } + if transfer.Type == bchain.ERC20 { + aggregate(&c.Value, &transfer.Value) + } else if transfer.Type == bchain.ERC721 { + if index < 0 { + // remove token from the list + for i := range c.Ids { + if c.Ids[i].Cmp(&transfer.Value) == 0 { + c.Ids = append(c.Ids[:i], c.Ids[i+1:]...) + break + } + } + } else { + // add token to the list + c.Ids = append(c.Ids, transfer.Value) + } + } else { // bchain.ERC1155 + for _, t := range transfer.IdValues { + for i := range c.IdValues { + // find the token in the list + if c.IdValues[i].Id.Cmp(&t.Id) == 0 { + aggregate(&c.IdValues[i].Value, &t.Value) + // if transfer from, remove if the value is zero + if index < 0 && len(c.IdValues[i].Value.Bits()) == 0 { + c.IdValues = append(c.IdValues[:i], c.IdValues[i+1:]...) + } + goto nextTransfer + } + } + // if not found and transfer to, add to the list + if index >= 0 { + c.IdValues = append(c.IdValues, t) + } + nextTransfer: + } + } + if addTxCount { + c.Txs++ + } + return index +} + +func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.AddressDescriptor, btxID []byte, index int32, contract bchain.AddressDescriptor, transfer *bchain.TokenTransfer, addTxCount bool, addresses addressesMap, addressContracts map[string]*AddrContracts) error { var err error strAddrDesc := string(addrDesc) ac, e := addressContracts[strAddrDesc] @@ -146,20 +295,16 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address // do not store contracts for 0x0000000000000000000000000000000000000000 address if !isZeroAddress(addrDesc) { // locate the contract and set i to the index in the array of contracts - i, found := findContractInAddressContracts(contract, ac.Contracts) + contractIndex, found := findContractInAddressContracts(contract, ac.Contracts) if !found { - i = len(ac.Contracts) - ac.Contracts = append(ac.Contracts, AddrContract{Contract: contract}) - } - // index 0 is for ETH transfers, index 1 (InternalTxIndexOffset) is for internal transfers, contract indexes start with 2 (ContractIndexOffset) - if index < 0 { - index = ^int32(i + ContractIndexOffset) - } else { - index = int32(i + ContractIndexOffset) - } - if addTxCount { - ac.Contracts[i].Txs++ - } + contractIndex = len(ac.Contracts) + ac.Contracts = append(ac.Contracts, AddrContract{ + Contract: contract, + Type: transfer.Type, + }) + } + c := &ac.Contracts[contractIndex] + index = addToContract(c, contractIndex, index, contract, transfer, addTxCount) } else { if index < 0 { index = transferFrom @@ -168,7 +313,7 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address } } } - counted := addToAddressesMap(addresses, strAddrDesc, btxID, index) + counted := addToAddressesMapEthereumType(addresses, strAddrDesc, btxID, index) if !counted { ac.TotalTxs++ } @@ -176,7 +321,10 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address } type ethBlockTxContract struct { - addr, contract bchain.AddressDescriptor + from, to, contract bchain.AddressDescriptor + transferType bchain.TokenTransferType + value big.Int + idValues []bchain.TokenTransferIdValue } type ethInternalTransfer struct { @@ -199,171 +347,190 @@ type ethBlockTx struct { internalData *ethInternalData } -func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses addressesMap, addressContracts map[string]*AddrContracts) ([]ethBlockTx, error) { - blockTxs := make([]ethBlockTx, len(block.Txs)) - for txi, tx := range block.Txs { - btxID, err := d.chainParser.PackTxid(tx.Txid) +func (d *RocksDB) processBaseTxData(blockTx *ethBlockTx, tx *bchain.Tx, addresses addressesMap, addressContracts map[string]*AddrContracts) error { + var from, to bchain.AddressDescriptor + var err error + // there is only one output address in EthereumType transaction, store it in format txid 0 + if len(tx.Vout) == 1 && len(tx.Vout[0].ScriptPubKey.Addresses) == 1 { + to, err = d.chainParser.GetAddrDescFromAddress(tx.Vout[0].ScriptPubKey.Addresses[0]) if err != nil { - return nil, err + // do not log ErrAddressMissing, transactions can be without to address (for example eth contracts) + if err != bchain.ErrAddressMissing { + glog.Warningf("rocksdb: processBaseTxData: %v, tx %v, output", err, tx.Txid) + } + } else { + if err = d.addToAddressesAndContractsEthereumType(to, blockTx.btxID, transferTo, nil, nil, true, addresses, addressContracts); err != nil { + return err + } + blockTx.to = to } - blockTx := &blockTxs[txi] - blockTx.btxID = btxID - var from, to bchain.AddressDescriptor - // there is only one output address in EthereumType transaction, store it in format txid 0 - if len(tx.Vout) == 1 && len(tx.Vout[0].ScriptPubKey.Addresses) == 1 { - to, err = d.chainParser.GetAddrDescFromAddress(tx.Vout[0].ScriptPubKey.Addresses[0]) + } + // there is only one input address in EthereumType transaction, store it in format txid ^0 + if len(tx.Vin) == 1 && len(tx.Vin[0].Addresses) == 1 { + from, err = d.chainParser.GetAddrDescFromAddress(tx.Vin[0].Addresses[0]) + if err != nil { + if err != bchain.ErrAddressMissing { + glog.Warningf("rocksdb: processBaseTxData: %v, tx %v, input", err, tx.Txid) + } + } else { + if err = d.addToAddressesAndContractsEthereumType(from, blockTx.btxID, transferFrom, nil, nil, !bytes.Equal(from, to), addresses, addressContracts); err != nil { + return err + } + blockTx.from = from + } + } + return nil +} + +func (d *RocksDB) processInternalData(blockTx *ethBlockTx, tx *bchain.Tx, id *bchain.EthereumInternalData, addresses addressesMap, addressContracts map[string]*AddrContracts) error { + blockTx.internalData = ðInternalData{ + internalType: id.Type, + errorMsg: id.Error, + } + // index contract creation + if id.Type == bchain.CREATE { + to, err := d.chainParser.GetAddrDescFromAddress(id.Contract) + if err != nil { + if err != bchain.ErrAddressMissing { + glog.Warningf("rocksdb: processInternalData: %v, tx %v, create contract", err, tx.Txid) + } + // set the internalType to CALL if incorrect contract so that it is not breaking the packing of data to DB + blockTx.internalData.internalType = bchain.CALL + } else { + blockTx.internalData.contract = to + if err = d.addToAddressesAndContractsEthereumType(to, blockTx.btxID, internalTransferTo, nil, nil, true, addresses, addressContracts); err != nil { + return err + } + } + } + // index internal transfers + if len(id.Transfers) > 0 { + blockTx.internalData.transfers = make([]ethInternalTransfer, len(id.Transfers)) + for i := range id.Transfers { + iti := &id.Transfers[i] + ito := &blockTx.internalData.transfers[i] + to, err := d.chainParser.GetAddrDescFromAddress(iti.To) if err != nil { // do not log ErrAddressMissing, transactions can be without to address (for example eth contracts) if err != bchain.ErrAddressMissing { - glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, output", err, block.Height, tx.Txid) + glog.Warningf("rocksdb: processInternalData: %v, tx %v, internal transfer %d to", err, tx.Txid, i) } } else { - if err = d.addToAddressesAndContractsEthereumType(to, btxID, transferTo, nil, addresses, addressContracts, true); err != nil { - return nil, err + if err = d.addToAddressesAndContractsEthereumType(to, blockTx.btxID, internalTransferTo, nil, nil, true, addresses, addressContracts); err != nil { + return err } - blockTx.to = to + ito.to = to } - } - // there is only one input address in EthereumType transaction, store it in format txid ^0 - if len(tx.Vin) == 1 && len(tx.Vin[0].Addresses) == 1 { - from, err = d.chainParser.GetAddrDescFromAddress(tx.Vin[0].Addresses[0]) + from, err := d.chainParser.GetAddrDescFromAddress(iti.From) if err != nil { if err != bchain.ErrAddressMissing { - glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, input", err, block.Height, tx.Txid) + glog.Warningf("rocksdb: processInternalData: %v, tx %v, internal transfer %d from", err, tx.Txid, i) } } else { - if err = d.addToAddressesAndContractsEthereumType(from, btxID, transferFrom, nil, addresses, addressContracts, !bytes.Equal(from, to)); err != nil { - return nil, err + if err = d.addToAddressesAndContractsEthereumType(from, blockTx.btxID, internalTransferFrom, nil, nil, !bytes.Equal(from, to), addresses, addressContracts); err != nil { + return err } - blockTx.from = from + ito.from = from } + ito.internalType = iti.Type + ito.value = iti.Value } - // process internal data - eid, _ := tx.CoinSpecificData.(bchain.EthereumSpecificData) - if eid.InternalData != nil { - blockTx.internalData = ðInternalData{ - internalType: eid.InternalData.Type, - errorMsg: eid.InternalData.Error, - } - // index contract creation - if eid.InternalData.Type == bchain.CREATE { - to, err = d.chainParser.GetAddrDescFromAddress(eid.InternalData.Contract) - if err != nil { - if err != bchain.ErrAddressMissing { - glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, create contract", err, block.Height, tx.Txid) - } - // set the internalType to CALL if incorrect contract so that it is not breaking the packing of data to DB - blockTx.internalData.internalType = bchain.CALL - } else { - blockTx.internalData.contract = to - if err = d.addToAddressesAndContractsEthereumType(to, btxID, internalTransferTo, nil, addresses, addressContracts, true); err != nil { - return nil, err - } - } - } - // index internal transfers - if len(eid.InternalData.Transfers) > 0 { - blockTx.internalData.transfers = make([]ethInternalTransfer, len(eid.InternalData.Transfers)) - for i := range eid.InternalData.Transfers { - iti := &eid.InternalData.Transfers[i] - ito := &blockTx.internalData.transfers[i] - to, err = d.chainParser.GetAddrDescFromAddress(iti.To) - if err != nil { - // do not log ErrAddressMissing, transactions can be without to address (for example eth contracts) - if err != bchain.ErrAddressMissing { - glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, internal transfer %d to", err, block.Height, tx.Txid, i) - } - } else { - if err = d.addToAddressesAndContractsEthereumType(to, btxID, internalTransferTo, nil, addresses, addressContracts, true); err != nil { - return nil, err - } - ito.to = to - } - from, err = d.chainParser.GetAddrDescFromAddress(iti.From) - if err != nil { - if err != bchain.ErrAddressMissing { - glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, internal transfer %d from", err, block.Height, tx.Txid, i) - } - } else { - if err = d.addToAddressesAndContractsEthereumType(from, btxID, internalTransferFrom, nil, addresses, addressContracts, !bytes.Equal(from, to)); err != nil { - return nil, err - } - ito.from = from - } - ito.internalType = iti.Type - ito.value = iti.Value - } + } + return nil +} + +func (d *RocksDB) processContractTransfers(blockTx *ethBlockTx, tx *bchain.Tx, addresses addressesMap, addressContracts map[string]*AddrContracts) error { + tokenTransfers, err := d.chainParser.EthereumTypeGetTokenTransfersFromTx(tx) + if err != nil { + glog.Warningf("rocksdb: processContractTransfers %v, tx %v", err, tx.Txid) + } + blockTx.contracts = make([]ethBlockTxContract, len(tokenTransfers)) + for i, t := range tokenTransfers { + var contract, from, to bchain.AddressDescriptor + contract, err = d.chainParser.GetAddrDescFromAddress(t.Contract) + if err == nil { + from, err = d.chainParser.GetAddrDescFromAddress(t.From) + if err == nil { + to, err = d.chainParser.GetAddrDescFromAddress(t.To) } } - // store erc20 transfers - erc20, err := d.chainParser.EthereumTypeGetErc20FromTx(&tx) if err != nil { - glog.Warningf("rocksdb: GetErc20FromTx %v - height %d, tx %v", err, block.Height, tx.Txid) + glog.Warningf("rocksdb: processContractTransfers %v, tx %v, transfer %v", err, tx.Txid, t) + continue } - blockTx.contracts = make([]ethBlockTxContract, len(erc20)*2) - j := 0 - for i, t := range erc20 { - var contract, from, to bchain.AddressDescriptor - contract, err = d.chainParser.GetAddrDescFromAddress(t.Contract) - if err == nil { - from, err = d.chainParser.GetAddrDescFromAddress(t.From) - if err == nil { - to, err = d.chainParser.GetAddrDescFromAddress(t.To) - } - } - if err != nil { - glog.Warningf("rocksdb: GetErc20FromTx %v - height %d, tx %v, transfer %v", err, block.Height, tx.Txid, t) - continue - } - if err = d.addToAddressesAndContractsEthereumType(to, btxID, int32(i), contract, addresses, addressContracts, true); err != nil { - return nil, err - } - eq := bytes.Equal(from, to) - bc := &blockTx.contracts[j] - j++ - bc.addr = from - bc.contract = contract - if err = d.addToAddressesAndContractsEthereumType(from, btxID, ^int32(i), contract, addresses, addressContracts, !eq); err != nil { + if err = d.addToAddressesAndContractsEthereumType(to, blockTx.btxID, int32(i), contract, t, true, addresses, addressContracts); err != nil { + return err + } + eq := bytes.Equal(from, to) + if err = d.addToAddressesAndContractsEthereumType(from, blockTx.btxID, ^int32(i), contract, t, !eq, addresses, addressContracts); err != nil { + return err + } + bc := &blockTx.contracts[i] + bc.transferType = t.Type + bc.from = from + bc.to = to + bc.contract = contract + bc.value = t.Value + bc.idValues = t.IdValues + } + return nil +} + +func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses addressesMap, addressContracts map[string]*AddrContracts) ([]ethBlockTx, error) { + blockTxs := make([]ethBlockTx, len(block.Txs)) + for txi := range block.Txs { + tx := &block.Txs[txi] + btxID, err := d.chainParser.PackTxid(tx.Txid) + if err != nil { + return nil, err + } + blockTx := &blockTxs[txi] + blockTx.btxID = btxID + if err = d.processBaseTxData(blockTx, tx, addresses, addressContracts); err != nil { + return nil, err + } + // process internal data + eid, _ := tx.CoinSpecificData.(bchain.EthereumSpecificData) + if eid.InternalData != nil { + if err = d.processInternalData(blockTx, tx, eid.InternalData, addresses, addressContracts); err != nil { return nil, err } - // add to address to blockTx.contracts only if it is different from from address - if !eq { - bc = &blockTx.contracts[j] - j++ - bc.addr = to - bc.contract = contract - } } - blockTx.contracts = blockTx.contracts[:j] + // store contract transfers + if err = d.processContractTransfers(blockTx, tx, addresses, addressContracts); err != nil { + return nil, err + } } return blockTxs, nil } var ethZeroAddress []byte = make([]byte, eth.EthereumTypeAddressDescriptorLen) +func appendAddress(buf []byte, a bchain.AddressDescriptor) []byte { + if len(a) != eth.EthereumTypeAddressDescriptorLen { + buf = append(buf, ethZeroAddress...) + } else { + buf = append(buf, a...) + } + return buf +} + func packEthInternalData(data *ethInternalData) []byte { // allocate enough for type+contract+all transfers with bigint value buf := make([]byte, 0, (2*len(data.transfers)+1)*(eth.EthereumTypeAddressDescriptorLen+16)) - appendAddress := func(a bchain.AddressDescriptor) { - if len(a) != eth.EthereumTypeAddressDescriptorLen { - buf = append(buf, ethZeroAddress...) - } else { - buf = append(buf, a...) - } - } varBuf := make([]byte, maxPackedBigintBytes) // internalType is one bit (CALL|CREATE), it is joined with count of internal transfers*2 l := packVaruint(uint(data.internalType)&1+uint(len(data.transfers))<<1, varBuf) buf = append(buf, varBuf[:l]...) if data.internalType == bchain.CREATE { - appendAddress(data.contract) + buf = appendAddress(buf, data.contract) } for i := range data.transfers { t := &data.transfers[i] buf = append(buf, byte(t.internalType)) - appendAddress(t.from) - appendAddress(t.to) + buf = appendAddress(buf, t.from) + buf = appendAddress(buf, t.to) l = packBigint(&t.value, varBuf) buf = append(buf, varBuf[:l]...) } @@ -412,7 +579,10 @@ func (d *RocksDB) GetEthereumInternalData(txid string) (*bchain.EthereumInternal if err != nil { return nil, err } + return d.getEthereumInternalData(btxID) +} +func (d *RocksDB) getEthereumInternalData(btxID []byte) (*bchain.EthereumInternalData, error) { val, err := d.db.GetCF(d.ro, d.cfh[cfInternalData], btxID) if err != nil { return nil, err @@ -435,50 +605,44 @@ func (d *RocksDB) storeInternalDataEthereumType(wb *gorocksdb.WriteBatch, blockT return nil } +func packBlockTx(buf []byte, blockTx *ethBlockTx) []byte { + varBuf := make([]byte, maxPackedBigintBytes) + buf = append(buf, blockTx.btxID...) + buf = appendAddress(buf, blockTx.from) + buf = appendAddress(buf, blockTx.to) + // internal data are not stored in blockTx, they are fetched on disconnect directly from the cfInternalData column + // contracts - store the number of address pairs + l := packVaruint(uint(len(blockTx.contracts)), varBuf) + buf = append(buf, varBuf[:l]...) + for j := range blockTx.contracts { + c := &blockTx.contracts[j] + buf = appendAddress(buf, c.from) + buf = appendAddress(buf, c.to) + buf = appendAddress(buf, c.contract) + l = packVaruint(uint(c.transferType), varBuf) + buf = append(buf, varBuf[:l]...) + if c.transferType == bchain.ERC1155 { + l = packVaruint(uint(len(c.idValues)), varBuf) + buf = append(buf, varBuf[:l]...) + for i := range c.idValues { + l = packBigint(&c.idValues[i].Id, varBuf) + buf = append(buf, varBuf[:l]...) + l = packBigint(&c.idValues[i].Value, varBuf) + buf = append(buf, varBuf[:l]...) + } + } else { // ERC20, ERC721 + l = packBigint(&c.value, varBuf) + buf = append(buf, varBuf[:l]...) + } + } + return buf +} + func (d *RocksDB) storeAndCleanupBlockTxsEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block, blockTxs []ethBlockTx) error { pl := d.chainParser.PackedTxidLen() buf := make([]byte, 0, (pl+2*eth.EthereumTypeAddressDescriptorLen)*len(blockTxs)) - varBuf := make([]byte, vlq.MaxLen64) - appendAddress := func(a bchain.AddressDescriptor) { - if len(a) != eth.EthereumTypeAddressDescriptorLen { - buf = append(buf, ethZeroAddress...) - } else { - buf = append(buf, a...) - } - } for i := range blockTxs { - blockTx := &blockTxs[i] - buf = append(buf, blockTx.btxID...) - appendAddress(blockTx.from) - appendAddress(blockTx.to) - // internal data - store the number of addresses, with odd number the CREATE tx type - var internalDataTransfers uint - if blockTx.internalData != nil { - internalDataTransfers = uint(len(blockTx.internalData.transfers)) * 2 - if blockTx.internalData.internalType == bchain.CREATE { - internalDataTransfers++ - } - } - l := packVaruint(internalDataTransfers, varBuf) - buf = append(buf, varBuf[:l]...) - if internalDataTransfers > 0 { - if blockTx.internalData.internalType == bchain.CREATE { - appendAddress(blockTx.internalData.contract) - } - for j := range blockTx.internalData.transfers { - c := &blockTx.internalData.transfers[j] - appendAddress(c.from) - appendAddress(c.to) - } - } - // contracts - store the number of address pairs - l = packVaruint(uint(len(blockTx.contracts)), varBuf) - buf = append(buf, varBuf[:l]...) - for j := range blockTx.contracts { - c := &blockTx.contracts[j] - appendAddress(c.addr) - appendAddress(c.contract) - } + buf = packBlockTx(buf, &blockTxs[i]) } key := packUint(block.Height) wb.PutCF(d.cfh[cfBlockTxs], key, buf) @@ -501,8 +665,78 @@ func (d *RocksDB) storeBlockInternalDataErrorEthereumType(wb *gorocksdb.WriteBat return nil } +// unpackBlockTx unpacks ethBlockTx from buf, starting at position pos +// the position is updated as the data is unpacked and returned to the caller +func unpackBlockTx(buf []byte, pos int) (*ethBlockTx, int, error) { + getAddress := func(i int) (bchain.AddressDescriptor, int, error) { + if len(buf)-i < eth.EthereumTypeAddressDescriptorLen { + glog.Error("rocksdb: Inconsistent data in blockTxs ", hex.EncodeToString(buf)) + return nil, 0, errors.New("Inconsistent data in blockTxs") + } + a := append(bchain.AddressDescriptor(nil), buf[i:i+eth.EthereumTypeAddressDescriptorLen]...) + return a, i + eth.EthereumTypeAddressDescriptorLen, nil + } + var from, to bchain.AddressDescriptor + var err error + if len(buf)-pos < eth.EthereumTypeTxidLen { + glog.Error("rocksdb: Inconsistent data in blockTxs ", hex.EncodeToString(buf)) + return nil, 0, errors.New("Inconsistent data in blockTxs") + } + txid := append([]byte(nil), buf[pos:pos+eth.EthereumTypeTxidLen]...) + pos += eth.EthereumTypeTxidLen + from, pos, err = getAddress(pos) + if err != nil { + return nil, 0, err + } + to, pos, err = getAddress(pos) + if err != nil { + return nil, 0, err + } + // contracts + cc, l := unpackVaruint(buf[pos:]) + pos += l + contracts := make([]ethBlockTxContract, cc) + for j := range contracts { + c := &contracts[j] + c.from, pos, err = getAddress(pos) + if err != nil { + return nil, 0, err + } + c.to, pos, err = getAddress(pos) + if err != nil { + return nil, 0, err + } + c.contract, pos, err = getAddress(pos) + if err != nil { + return nil, 0, err + } + cc, l = unpackVaruint(buf[pos:]) + c.transferType = bchain.TokenTransferType(cc) + pos += l + if c.transferType == bchain.ERC1155 { + cc, l = unpackVaruint(buf[pos:]) + pos += l + c.idValues = make([]bchain.TokenTransferIdValue, cc) + for i := range c.idValues { + c.idValues[i].Id, l = unpackBigint(buf[pos:]) + pos += l + c.idValues[i].Value, l = unpackBigint(buf[pos:]) + pos += l + } + } else { // ERC20, ERC721 + c.value, l = unpackBigint(buf[pos:]) + pos += l + } + } + return ðBlockTx{ + btxID: txid, + from: from, + to: to, + contracts: contracts, + }, pos, nil +} + func (d *RocksDB) getBlockTxsEthereumType(height uint32) ([]ethBlockTx, error) { - pl := d.chainParser.PackedTxidLen() val, err := d.db.GetCF(d.ro, d.cfh[cfBlockTxs], packUint(height)) if err != nil { return nil, err @@ -514,187 +748,170 @@ func (d *RocksDB) getBlockTxsEthereumType(height uint32) ([]ethBlockTx, error) { return nil, nil } // buf can be empty slice, this means the block did not contain any transactions - bt := make([]ethBlockTx, 0, 8) - getAddress := func(i int) (bchain.AddressDescriptor, int, error) { - if len(buf)-i < eth.EthereumTypeAddressDescriptorLen { - glog.Error("rocksdb: Inconsistent data in blockTxs ", hex.EncodeToString(buf)) - return nil, 0, errors.New("Inconsistent data in blockTxs") - } - a := append(bchain.AddressDescriptor(nil), buf[i:i+eth.EthereumTypeAddressDescriptorLen]...) - // return null addresses as nil - for _, b := range a { - if b != 0 { - return a, i + eth.EthereumTypeAddressDescriptorLen, nil - } - } - return nil, i + eth.EthereumTypeAddressDescriptorLen, nil - } - var from, to bchain.AddressDescriptor + bt := make([]ethBlockTx, 0, 16) + var btx *ethBlockTx for i := 0; i < len(buf); { - if len(buf)-i < pl { - glog.Error("rocksdb: Inconsistent data in blockTxs ", hex.EncodeToString(buf)) - return nil, errors.New("Inconsistent data in blockTxs") - } - txid := append([]byte(nil), buf[i:i+pl]...) - i += pl - from, i, err = getAddress(i) + btx, i, err = unpackBlockTx(buf, i) if err != nil { return nil, err } - to, i, err = getAddress(i) + bt = append(bt, *btx) + } + return bt, nil +} + +func (d *RocksDB) disconnectAddress(btxID []byte, internal bool, addrDesc bchain.AddressDescriptor, btxContract *ethBlockTxContract, addresses map[string]map[string]struct{}, contracts map[string]*AddrContracts) error { + var err error + // do not process empty address + if len(addrDesc) == 0 { + return nil + } + s := string(addrDesc) + txid := string(btxID) + // find if tx for this address was already encountered + mtx, ftx := addresses[s] + if !ftx { + mtx = make(map[string]struct{}) + mtx[txid] = struct{}{} + addresses[s] = mtx + } else { + _, ftx = mtx[txid] + if !ftx { + mtx[txid] = struct{}{} + } + } + addrContracts, fc := contracts[s] + if !fc { + addrContracts, err = d.GetAddrDescContracts(addrDesc) if err != nil { - return nil, err + return err } - // internal data - var internalData *ethInternalData - cc, l := unpackVaruint(buf[i:]) - i += l - if cc > 0 { - internalData = ðInternalData{} - // odd count of internal transfers means it is CREATE transaction with the contract added to the list - if cc&1 == 1 { - internalData.internalType = bchain.CREATE - internalData.contract, i, err = getAddress(i) - if err != nil { - return nil, err + if addrContracts != nil { + contracts[s] = addrContracts + } + } + if addrContracts != nil { + if !ftx { + addrContracts.TotalTxs-- + } + if btxContract == nil { + if internal { + if addrContracts.InternalTxs > 0 { + addrContracts.InternalTxs-- + } else { + glog.Warning("AddressContracts ", addrDesc, ", InternalTxs would be negative, tx ", hex.EncodeToString(btxID)) + } + } else { + if addrContracts.NonContractTxs > 0 { + addrContracts.NonContractTxs-- + } else { + glog.Warning("AddressContracts ", addrDesc, ", EthTxs would be negative, tx ", hex.EncodeToString(btxID)) } } - internalData.transfers = make([]ethInternalTransfer, cc/2) - for j := range internalData.transfers { - t := &internalData.transfers[j] - t.from, i, err = getAddress(i) - t.to, i, err = getAddress(i) - if err != nil { - return nil, err + } else { + contractIndex, found := findContractInAddressContracts(btxContract.contract, addrContracts.Contracts) + if found { + addrContract := &addrContracts.Contracts[contractIndex] + if addrContract.Txs > 0 { + addrContract.Txs-- + if addrContract.Txs == 0 { + // no transactions, remove the contract + addrContracts.Contracts = append(addrContracts.Contracts[:contractIndex], addrContracts.Contracts[contractIndex+1:]...) + } else { + // update the values of the contract, reverse the direction + var index int32 + if bytes.Equal(addrDesc, btxContract.to) { + index = transferFrom + } else { + index = transferTo + } + addToContract(addrContract, contractIndex, index, btxContract.contract, &bchain.TokenTransfer{ + Type: btxContract.transferType, + Value: btxContract.value, + IdValues: btxContract.idValues, + }, false) + } + } else { + glog.Warning("AddressContracts ", addrDesc, ", contract ", contractIndex, " Txs would be negative, tx ", hex.EncodeToString(btxID)) } + } else { + glog.Warning("AddressContracts ", addrDesc, ", contract ", btxContract.contract, " not found, tx ", hex.EncodeToString(btxID)) } } - // contracts - cc, l = unpackVaruint(buf[i:]) - i += l - contracts := make([]ethBlockTxContract, cc) - for j := range contracts { - contracts[j].addr, i, err = getAddress(i) - if err != nil { - return nil, err - } - contracts[j].contract, i, err = getAddress(i) - if err != nil { - return nil, err - } + } else { + if !isZeroAddress(addrDesc) { + glog.Warning("AddressContracts ", addrDesc, " not found, tx ", hex.EncodeToString(btxID)) } - bt = append(bt, ethBlockTx{ - btxID: txid, - from: from, - to: to, - internalData: internalData, - contracts: contracts, - }) } - return bt, nil + return nil } -func (d *RocksDB) disconnectBlockTxsEthereumType(wb *gorocksdb.WriteBatch, height uint32, blockTxs []ethBlockTx, contracts map[string]*AddrContracts) error { - glog.Info("Disconnecting block ", height, " containing ", len(blockTxs), " transactions") - addresses := make(map[string]map[string]struct{}) - disconnectAddress := func(btxID []byte, internal bool, addrDesc, contract bchain.AddressDescriptor) error { - var err error - // do not process empty address - if len(addrDesc) == 0 { - return nil - } - s := string(addrDesc) - txid := string(btxID) - // find if tx for this address was already encountered - mtx, ftx := addresses[s] - if !ftx { - mtx = make(map[string]struct{}) - mtx[txid] = struct{}{} - addresses[s] = mtx - } else { - _, ftx = mtx[txid] - if !ftx { - mtx[txid] = struct{}{} +func (d *RocksDB) disconnectInternalData(btxID []byte, addresses map[string]map[string]struct{}, contracts map[string]*AddrContracts) error { + internalData, err := d.getEthereumInternalData(btxID) + if err != nil { + return err + } + if internalData != nil { + if internalData.Type == bchain.CREATE { + contract, err := d.chainParser.GetAddrDescFromAddress(internalData.Contract) + if err != nil { + return err + } + if err := d.disconnectAddress(btxID, true, contract, nil, addresses, contracts); err != nil { + return err } } - c, fc := contracts[s] - if !fc { - c, err = d.GetAddrDescContracts(addrDesc) + for j := range internalData.Transfers { + t := &internalData.Transfers[j] + var from, to bchain.AddressDescriptor + from, err = d.chainParser.GetAddrDescFromAddress(t.From) + if err == nil { + to, err = d.chainParser.GetAddrDescFromAddress(t.To) + } if err != nil { return err } - contracts[s] = c - } - if c != nil { - if !ftx { - c.TotalTxs-- + if err := d.disconnectAddress(btxID, true, from, nil, addresses, contracts); err != nil { + return err } - if contract == nil { - if internal { - if c.InternalTxs > 0 { - c.InternalTxs-- - } else { - glog.Warning("AddressContracts ", addrDesc, ", InternalTxs would be negative, tx ", hex.EncodeToString(btxID)) - } - } else { - if c.NonContractTxs > 0 { - c.NonContractTxs-- - } else { - glog.Warning("AddressContracts ", addrDesc, ", EthTxs would be negative, tx ", hex.EncodeToString(btxID)) - } - } - } else { - i, found := findContractInAddressContracts(contract, c.Contracts) - if found { - if c.Contracts[i].Txs > 0 { - c.Contracts[i].Txs-- - if c.Contracts[i].Txs == 0 { - c.Contracts = append(c.Contracts[:i], c.Contracts[i+1:]...) - } - } else { - glog.Warning("AddressContracts ", addrDesc, ", contract ", i, " Txs would be negative, tx ", hex.EncodeToString(btxID)) - } - } else { - glog.Warning("AddressContracts ", addrDesc, ", contract ", contract, " not found, tx ", hex.EncodeToString(btxID)) + // if from==to, tx is counted only once and does not have to be disconnected again + if !bytes.Equal(from, to) { + if err := d.disconnectAddress(btxID, true, to, nil, addresses, contracts); err != nil { + return err } } - } else { - glog.Warning("AddressContracts ", addrDesc, " not found, tx ", hex.EncodeToString(btxID)) } - return nil } + return nil +} + +func (d *RocksDB) disconnectBlockTxsEthereumType(wb *gorocksdb.WriteBatch, height uint32, blockTxs []ethBlockTx, contracts map[string]*AddrContracts) error { + glog.Info("Disconnecting block ", height, " containing ", len(blockTxs), " transactions") + addresses := make(map[string]map[string]struct{}) for i := range blockTxs { blockTx := &blockTxs[i] - if err := disconnectAddress(blockTx.btxID, false, blockTx.from, nil); err != nil { + if err := d.disconnectAddress(blockTx.btxID, false, blockTx.from, nil, addresses, contracts); err != nil { return err } // if from==to, tx is counted only once and does not have to be disconnected again if !bytes.Equal(blockTx.from, blockTx.to) { - if err := disconnectAddress(blockTx.btxID, false, blockTx.to, nil); err != nil { + if err := d.disconnectAddress(blockTx.btxID, false, blockTx.to, nil, addresses, contracts); err != nil { return err } } - if blockTx.internalData != nil { - if blockTx.internalData.internalType == bchain.CREATE { - if err := disconnectAddress(blockTx.btxID, true, blockTx.internalData.contract, nil); err != nil { - return err - } - } - for j := range blockTx.internalData.transfers { - t := &blockTx.internalData.transfers[j] - if err := disconnectAddress(blockTx.btxID, true, t.from, nil); err != nil { - return err - } - // if from==to, tx is counted only once and does not have to be disconnected again - if !bytes.Equal(t.from, t.to) { - if err := disconnectAddress(blockTx.btxID, true, t.to, nil); err != nil { - return err - } - } - } + // internal data + err := d.disconnectInternalData(blockTx.btxID, addresses, contracts) + if err != nil { + return err + } - for _, c := range blockTx.contracts { - if err := disconnectAddress(blockTx.btxID, false, c.addr, c.contract); err != nil { + // contracts + for j := range blockTx.contracts { + c := &blockTx.contracts[j] + if err := d.disconnectAddress(blockTx.btxID, false, c.from, c, addresses, contracts); err != nil { + return err + } + if err := d.disconnectAddress(blockTx.btxID, false, c.to, c, addresses, contracts); err != nil { return err } } diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index fb5d70eb1e..6f9ed711b7 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -4,6 +4,7 @@ package db import ( "encoding/hex" + "math/big" "reflect" "testing" @@ -22,6 +23,12 @@ func ethereumTestnetParser() *eth.EthereumParser { return eth.NewEthereumParser(1) } +func bigintFromStringToHex(s string) string { + var b big.Int + b.SetString(s, 0) + return bigintToHex(&b) +} + func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect bool) { if err := checkColumn(d, cfHeight, []keyPair{ { @@ -35,7 +42,7 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo } } if err := checkColumn(d, cfAddresses, []keyPair{ - {addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1, ^1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil}, + {addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil}, {addressKeyHex(dbtestdata.EthAddr55, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{2}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{0}), nil}, {addressKeyHex(dbtestdata.EthAddr20, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^0, ^2}), nil}, {addressKeyHex(dbtestdata.EthAddr9f, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1}), nil}, @@ -48,8 +55,14 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo if err := checkColumn(d, cfAddressContracts, []keyPair{ {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "020102", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "020100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), + "020100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("10000000000000000000000"), nil, + }, + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), + "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintToHex(big.NewInt(0)), nil, + }, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "010002", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010101", nil}, }); err != nil { @@ -81,16 +94,10 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo { "0041eee8", dbtestdata.EthTxidB1T1 + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + "00" + "00" + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + "00" + dbtestdata.EthTxidB1T2 + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + - "06" + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + - "02" + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("10000000000000000000000"), nil, }, } @@ -111,7 +118,7 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa }, { "0041eee9", - "2b57e15e93a0ed197417a34c2498b7187df79099572c04a6b6e6ff418f74e6ee" + uintToHex(1534859988) + varuintToHex(2) + varuintToHex(2345678), + "2b57e15e93a0ed197417a34c2498b7187df79099572c04a6b6e6ff418f74e6ee" + uintToHex(1534859988) + varuintToHex(6) + varuintToHex(2345678), nil, }, }); err != nil { @@ -120,18 +127,27 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa } } if err := checkColumn(d, cfAddresses, []keyPair{ - {addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1, ^1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil}, + {addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil}, {addressKeyHex(dbtestdata.EthAddr55, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{2}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{0}), nil}, {addressKeyHex(dbtestdata.EthAddr20, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^0, ^2}), nil}, {addressKeyHex(dbtestdata.EthAddr9f, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1}), nil}, {addressKeyHex(dbtestdata.EthAddrContract4a, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{0, 1}), nil}, - {addressKeyHex(dbtestdata.EthAddr55, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^3, 2}) + txIndexesHex(dbtestdata.EthTxidB2T1, []int32{^0}), nil}, - {addressKeyHex(dbtestdata.EthAddr9f, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{1, 1}) + txIndexesHex(dbtestdata.EthTxidB2T1, []int32{0}), nil}, + + {addressKeyHex(dbtestdata.EthAddrZero, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T5, []int32{transferFrom}), nil}, + {addressKeyHex(dbtestdata.EthAddr3e, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T4, []int32{^0, 2}), nil}, {addressKeyHex(dbtestdata.EthAddr4b, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^0, ^1, 2, ^3, 3, ^2}), nil}, - {addressKeyHex(dbtestdata.EthAddr7b, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^2, 3}), nil}, + {addressKeyHex(dbtestdata.EthAddr55, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T6, []int32{0, ^0, 4, ^4}) + txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^3, 2}) + txIndexesHex(dbtestdata.EthTxidB2T1, []int32{^0}), nil}, + {addressKeyHex(dbtestdata.EthAddr5d, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T5, []int32{^0, 2}), nil}, + {addressKeyHex(dbtestdata.EthAddr7b, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T3, []int32{4}) + txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^2, 3}), nil}, + {addressKeyHex(dbtestdata.EthAddr83, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T3, []int32{^0, ^2}), nil}, + {addressKeyHex(dbtestdata.EthAddr92, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T4, []int32{0}), nil}, + {addressKeyHex(dbtestdata.EthAddr9f, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{1}) + txIndexesHex(dbtestdata.EthTxidB2T1, []int32{0}), nil}, + {addressKeyHex(dbtestdata.EthAddrA3, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T4, []int32{^2}), nil}, {addressKeyHex(dbtestdata.EthAddrContract0d, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{1}), nil}, {addressKeyHex(dbtestdata.EthAddrContract47, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{0}), nil}, {addressKeyHex(dbtestdata.EthAddrContract4a, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^1}), nil}, + {addressKeyHex(dbtestdata.EthAddrContract6f, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T5, []int32{0}), nil}, + {addressKeyHex(dbtestdata.EthAddrContractCd, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T3, []int32{0}), nil}, }); err != nil { { t.Fatal(err) @@ -139,15 +155,53 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa } if err := checkColumn(d, cfAddressContracts, []keyPair{ - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "020102", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "040200" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "020102", nil}, + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), + "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintToHex(big.NewInt(0)), nil, + }, + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), + "030202" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC1155)) + varuintToHex(1) + bigintFromStringToHex("150") + bigintFromStringToHex("1"), nil, + }, + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), + "010101" + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(2<<2+uint(bchain.ERC20)) + bigintFromStringToHex("8086") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.ERC20)) + bigintFromStringToHex("871180000950184"), nil, + }, + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), + "050300" + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.ERC20)) + bigintFromStringToHex("10000000854307892726464") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("0") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("0"), nil, + }, + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser), + "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC1155)) + varuintToHex(2) + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10"), nil, + }, + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser), + "020000" + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("0") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("7674999999999991915") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC721)) + varuintToHex(1) + bigintFromStringToHex("1"), nil, + }, + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser), + "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC721)) + varuintToHex(0), nil, + }, + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrA3, d.chainParser), + "010000" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC1155)) + varuintToHex(0), nil, + }, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr92, d.chainParser), "010100", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "030104", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), "010101" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser), "010000" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser), "010001", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser), "010100", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "020102", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser), "010100", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser), "010100", nil}, }); err != nil { { t.Fatal(err) @@ -185,22 +239,25 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa { "0041eee9", dbtestdata.EthTxidB2T1 + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + "00" + "00" + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + "00" + dbtestdata.EthTxidB2T2 + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser) + - "05" + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + - "08" + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser), + "04" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("7675000000000000001") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("854307892726464") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("871180000950184") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("7674999999999991915") + + dbtestdata.EthTxidB2T3 + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(uint(bchain.ERC721)) + bigintFromStringToHex("1") + + dbtestdata.EthTxidB2T4 + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr92, d.chainParser) + + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrA3, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(uint(bchain.ERC1155)) + "01" + bigintFromStringToHex("150") + bigintFromStringToHex("1") + + dbtestdata.EthTxidB2T5 + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrZero, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(uint(bchain.ERC1155)) + "02" + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10") + + dbtestdata.EthTxidB2T6 + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("10000000000000000000000"), nil, }, }); err != nil { @@ -285,6 +342,10 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { // get transactions for various addresses / low-high ranges verifyGetTransactions(t, d, "0x"+dbtestdata.EthAddr55, 0, 10000000, []txidIndex{ + {"0x" + dbtestdata.EthTxidB2T6, 0}, + {"0x" + dbtestdata.EthTxidB2T6, ^0}, + {"0x" + dbtestdata.EthTxidB2T6, 4}, + {"0x" + dbtestdata.EthTxidB2T6, ^4}, {"0x" + dbtestdata.EthTxidB2T2, ^3}, {"0x" + dbtestdata.EthTxidB2T2, 2}, {"0x" + dbtestdata.EthTxidB2T1, ^0}, @@ -347,7 +408,7 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { } iw := &BlockInfo{ Hash: "0x2b57e15e93a0ed197417a34c2498b7187df79099572c04a6b6e6ff418f74e6ee", - Txs: 2, + Txs: 6, Size: 2345678, Time: 1534859988, Height: 4321001, @@ -468,3 +529,607 @@ func Test_BulkConnect_EthereumType(t *testing.T) { t.Fatal("Expecting is.BlockTimes 4321002, got ", len(d.is.BlockTimes)) } } + +func Test_packUnpackEthInternalData(t *testing.T) { + parser := ethereumTestnetParser() + db := &RocksDB{chainParser: parser} + tests := []struct { + name string + data ethInternalData + want *bchain.EthereumInternalData + }{ + { + name: "CALL 1", + data: ethInternalData{ + internalType: bchain.CALL, + transfers: []ethInternalTransfer{ + { + internalType: bchain.CALL, + from: addressToAddrDesc(dbtestdata.EthAddr3e, parser), + to: addressToAddrDesc(dbtestdata.EthAddr20, parser), + value: *big.NewInt(412342134), + }, + }, + }, + want: &bchain.EthereumInternalData{ + Type: bchain.CALL, + Transfers: []bchain.EthereumInternalTransfer{ + { + Type: bchain.CALL, + From: eth.EIP55AddressFromAddress(dbtestdata.EthAddr3e), + To: eth.EIP55AddressFromAddress(dbtestdata.EthAddr20), + Value: *big.NewInt(412342134), + }, + }, + }, + }, + { + name: "CALL 2", + data: ethInternalData{ + internalType: bchain.CALL, + errorMsg: "error error error", + transfers: []ethInternalTransfer{ + { + internalType: bchain.CALL, + from: addressToAddrDesc(dbtestdata.EthAddr3e, parser), + to: addressToAddrDesc(dbtestdata.EthAddr20, parser), + value: *big.NewInt(4123421341), + }, + { + internalType: bchain.CREATE, + from: addressToAddrDesc(dbtestdata.EthAddr4b, parser), + to: addressToAddrDesc(dbtestdata.EthAddr55, parser), + value: *big.NewInt(123), + }, + { + internalType: bchain.SELFDESTRUCT, + from: addressToAddrDesc(dbtestdata.EthAddr7b, parser), + to: addressToAddrDesc(dbtestdata.EthAddr83, parser), + value: *big.NewInt(67890), + }, + }, + }, + want: &bchain.EthereumInternalData{ + Type: bchain.CALL, + Error: "error error error", + Transfers: []bchain.EthereumInternalTransfer{ + { + Type: bchain.CALL, + From: eth.EIP55AddressFromAddress(dbtestdata.EthAddr3e), + To: eth.EIP55AddressFromAddress(dbtestdata.EthAddr20), + Value: *big.NewInt(4123421341), + }, + { + Type: bchain.CREATE, + From: eth.EIP55AddressFromAddress(dbtestdata.EthAddr4b), + To: eth.EIP55AddressFromAddress(dbtestdata.EthAddr55), + Value: *big.NewInt(123), + }, + { + Type: bchain.SELFDESTRUCT, + From: eth.EIP55AddressFromAddress(dbtestdata.EthAddr7b), + To: eth.EIP55AddressFromAddress(dbtestdata.EthAddr83), + Value: *big.NewInt(67890), + }, + }, + }, + }, + { + name: "CREATE", + data: ethInternalData{ + internalType: bchain.CREATE, + contract: addressToAddrDesc(dbtestdata.EthAddrContract0d, parser), + }, + want: &bchain.EthereumInternalData{ + Type: bchain.CREATE, + Contract: eth.EIP55AddressFromAddress(dbtestdata.EthAddrContract0d), + Transfers: []bchain.EthereumInternalTransfer{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + packed := packEthInternalData(&tt.data) + got, err := db.unpackEthInternalData(packed) + if err != nil { + t.Errorf("unpackEthInternalData() error = %v", err) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("packEthInternalData/unpackEthInternalData = %+v, want %+v", got, tt.want) + } + }) + } +} + +func Test_packUnpackAddrContracts(t *testing.T) { + parser := ethereumTestnetParser() + type args struct { + buf []byte + addrDesc bchain.AddressDescriptor + } + tests := []struct { + name string + data AddrContracts + }{ + { + name: "1", + data: AddrContracts{ + TotalTxs: 30, + NonContractTxs: 20, + InternalTxs: 10, + Contracts: []AddrContract{}, + }, + }, + { + name: "2", + data: AddrContracts{ + TotalTxs: 12345, + NonContractTxs: 444, + InternalTxs: 8873, + Contracts: []AddrContract{ + { + Type: bchain.ERC20, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract0d, parser), + Txs: 8, + Value: *big.NewInt(793201132), + }, + { + Type: bchain.ERC721, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + Txs: 41235, + Ids: []big.Int{ + *big.NewInt(1), + *big.NewInt(2), + *big.NewInt(3), + *big.NewInt(3144223412344123), + *big.NewInt(5), + }, + }, + { + Type: bchain.ERC1155, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), + Txs: 64, + IdValues: []bchain.TokenTransferIdValue{ + { + Id: *big.NewInt(1), + Value: *big.NewInt(1412341234), + }, + { + Id: *big.NewInt(123412341234), + Value: *big.NewInt(3), + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + packed := packAddrContracts(&tt.data) + got, err := unpackAddrContracts(packed, nil) + if err != nil { + t.Errorf("unpackAddrContracts() error = %v", err) + return + } + if !reflect.DeepEqual(got, &tt.data) { + t.Errorf("unpackAddrContracts() = %v, want %v", got, tt.data) + } + }) + } +} + +func Test_addToContracts(t *testing.T) { + // the test builds addToContracts that keeps contracts of an address + // the test adds and removes values from addToContracts, therefore the order of tests is important + addrContracts := &AddrContracts{} + parser := ethereumTestnetParser() + type args struct { + index int32 + contract bchain.AddressDescriptor + transfer *bchain.TokenTransfer + addTxCount bool + } + tests := []struct { + name string + args args + wantIndex int32 + wantAddrContracts *AddrContracts + }{ + { + name: "ERC20 to", + args: args{ + index: 1, + contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + transfer: &bchain.TokenTransfer{ + Type: bchain.ERC20, + Value: *big.NewInt(123456), + }, + addTxCount: true, + }, + wantIndex: 0 + ContractIndexOffset, // the first contract of the address + wantAddrContracts: &AddrContracts{ + Contracts: []AddrContract{ + { + Type: bchain.ERC20, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + Txs: 1, + Value: *big.NewInt(123456), + }, + }, + }, + }, + { + name: "ERC20 from", + args: args{ + index: ^1, + contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + transfer: &bchain.TokenTransfer{ + Type: bchain.ERC20, + Value: *big.NewInt(23456), + }, + addTxCount: true, + }, + wantIndex: ^(0 + ContractIndexOffset), // the first contract of the address + wantAddrContracts: &AddrContracts{ + Contracts: []AddrContract{ + { + Type: bchain.ERC20, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + Value: *big.NewInt(100000), + Txs: 2, + }, + }, + }, + }, + { + name: "ERC721 to id 1", + args: args{ + index: 1, + contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), + transfer: &bchain.TokenTransfer{ + Type: bchain.ERC721, + Value: *big.NewInt(1), + }, + addTxCount: true, + }, + wantIndex: 1 + ContractIndexOffset, // the 2nd contract of the address + wantAddrContracts: &AddrContracts{ + Contracts: []AddrContract{ + { + Type: bchain.ERC20, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + Value: *big.NewInt(100000), + Txs: 2, + }, + { + Type: bchain.ERC721, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), + Txs: 1, + Ids: []big.Int{*big.NewInt(1)}, + }, + }, + }, + }, + { + name: "ERC721 to id 2", + args: args{ + index: 1, + contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), + transfer: &bchain.TokenTransfer{ + Type: bchain.ERC721, + Value: *big.NewInt(2), + }, + addTxCount: true, + }, + wantIndex: 1 + ContractIndexOffset, // the 2nd contract of the address + wantAddrContracts: &AddrContracts{ + Contracts: []AddrContract{ + { + Type: bchain.ERC20, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + Value: *big.NewInt(100000), + Txs: 2, + }, + { + Type: bchain.ERC721, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), + Txs: 2, + Ids: []big.Int{*big.NewInt(1), *big.NewInt(2)}, + }, + }, + }, + }, + { + name: "ERC721 from id 1, addTxCount=false", + args: args{ + index: ^1, + contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), + transfer: &bchain.TokenTransfer{ + Type: bchain.ERC721, + Value: *big.NewInt(1), + }, + addTxCount: false, + }, + wantIndex: ^(1 + ContractIndexOffset), // the 2nd contract of the address + wantAddrContracts: &AddrContracts{ + Contracts: []AddrContract{ + { + Type: bchain.ERC20, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + Value: *big.NewInt(100000), + Txs: 2, + }, + { + Type: bchain.ERC721, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), + Txs: 2, + Ids: []big.Int{*big.NewInt(2)}, + }, + }, + }, + }, + { + name: "ERC1155 to id 11, value 56789", + args: args{ + index: 1, + contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), + transfer: &bchain.TokenTransfer{ + Type: bchain.ERC1155, + IdValues: []bchain.TokenTransferIdValue{ + { + Id: *big.NewInt(11), + Value: *big.NewInt(56789), + }, + }, + }, + addTxCount: true, + }, + wantIndex: 2 + ContractIndexOffset, // the 3nd contract of the address + wantAddrContracts: &AddrContracts{ + Contracts: []AddrContract{ + { + Type: bchain.ERC20, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + Value: *big.NewInt(100000), + Txs: 2, + }, + { + Type: bchain.ERC721, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), + Txs: 2, + Ids: []big.Int{*big.NewInt(2)}, + }, + { + Type: bchain.ERC1155, + Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), + Txs: 1, + IdValues: []bchain.TokenTransferIdValue{ + { + Id: *big.NewInt(11), + Value: *big.NewInt(56789), + }, + }, + }, + }, + }, + }, + { + name: "ERC1155 to id 11, value 111 and id 22, value 222", + args: args{ + index: 1, + contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), + transfer: &bchain.TokenTransfer{ + Type: bchain.ERC1155, + IdValues: []bchain.TokenTransferIdValue{ + { + Id: *big.NewInt(11), + Value: *big.NewInt(111), + }, + { + Id: *big.NewInt(22), + Value: *big.NewInt(222), + }, + }, + }, + addTxCount: true, + }, + wantIndex: 2 + ContractIndexOffset, // the 3nd contract of the address + wantAddrContracts: &AddrContracts{ + Contracts: []AddrContract{ + { + Type: bchain.ERC20, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + Value: *big.NewInt(100000), + Txs: 2, + }, + { + Type: bchain.ERC721, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), + Txs: 2, + Ids: []big.Int{*big.NewInt(2)}, + }, + { + Type: bchain.ERC1155, + Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), + Txs: 2, + IdValues: []bchain.TokenTransferIdValue{ + { + Id: *big.NewInt(11), + Value: *big.NewInt(56900), + }, + { + Id: *big.NewInt(22), + Value: *big.NewInt(222), + }, + }, + }, + }, + }, + }, + { + name: "ERC1155 from id 11, value 112 and id 22, value 222", + args: args{ + index: ^1, + contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), + transfer: &bchain.TokenTransfer{ + Type: bchain.ERC1155, + IdValues: []bchain.TokenTransferIdValue{ + { + Id: *big.NewInt(11), + Value: *big.NewInt(112), + }, + { + Id: *big.NewInt(22), + Value: *big.NewInt(222), + }, + }, + }, + addTxCount: true, + }, + wantIndex: ^(2 + ContractIndexOffset), // the 3nd contract of the address + wantAddrContracts: &AddrContracts{ + Contracts: []AddrContract{ + { + Type: bchain.ERC20, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + Value: *big.NewInt(100000), + Txs: 2, + }, + { + Type: bchain.ERC721, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), + Txs: 2, + Ids: []big.Int{*big.NewInt(2)}, + }, + { + Type: bchain.ERC1155, + Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), + Txs: 3, + IdValues: []bchain.TokenTransferIdValue{ + { + Id: *big.NewInt(11), + Value: *big.NewInt(56788), + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + contractIndex, found := findContractInAddressContracts(tt.args.contract, addrContracts.Contracts) + if !found { + contractIndex = len(addrContracts.Contracts) + addrContracts.Contracts = append(addrContracts.Contracts, AddrContract{ + Contract: tt.args.contract, + Type: tt.args.transfer.Type, + }) + } + if got := addToContract(&addrContracts.Contracts[contractIndex], contractIndex, tt.args.index, tt.args.contract, tt.args.transfer, tt.args.addTxCount); got != tt.wantIndex { + t.Errorf("addToContracts() = %v, want %v", got, tt.wantIndex) + } + if !reflect.DeepEqual(addrContracts, tt.wantAddrContracts) { + t.Errorf("addToContracts() = %+v, want %+v", addrContracts, tt.wantAddrContracts) + } + }) + } +} + +func Test_packUnpackBlockTx(t *testing.T) { + parser := ethereumTestnetParser() + tests := []struct { + name string + blockTx ethBlockTx + pos int + }{ + { + name: "no contract", + blockTx: ethBlockTx{ + btxID: hexToBytes(dbtestdata.EthTxidB1T1), + from: addressToAddrDesc(dbtestdata.EthAddr3e, parser), + to: addressToAddrDesc(dbtestdata.EthAddr55, parser), + contracts: []ethBlockTxContract{}, + }, + pos: 73, + }, + { + name: "ERC20", + blockTx: ethBlockTx{ + btxID: hexToBytes(dbtestdata.EthTxidB1T1), + from: addressToAddrDesc(dbtestdata.EthAddr3e, parser), + to: addressToAddrDesc(dbtestdata.EthAddr55, parser), + contracts: []ethBlockTxContract{ + { + from: addressToAddrDesc(dbtestdata.EthAddr20, parser), + to: addressToAddrDesc(dbtestdata.EthAddr5d, parser), + contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), + transferType: bchain.ERC20, + value: *big.NewInt(10000), + }, + }, + }, + pos: 137, + }, + { + name: "multiple contracts", + blockTx: ethBlockTx{ + btxID: hexToBytes(dbtestdata.EthTxidB1T1), + from: addressToAddrDesc(dbtestdata.EthAddr3e, parser), + to: addressToAddrDesc(dbtestdata.EthAddr55, parser), + contracts: []ethBlockTxContract{ + { + from: addressToAddrDesc(dbtestdata.EthAddr20, parser), + to: addressToAddrDesc(dbtestdata.EthAddr3e, parser), + contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), + transferType: bchain.ERC20, + value: *big.NewInt(987654321), + }, + { + from: addressToAddrDesc(dbtestdata.EthAddr4b, parser), + to: addressToAddrDesc(dbtestdata.EthAddr55, parser), + contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), + transferType: bchain.ERC721, + value: *big.NewInt(13), + }, + { + from: addressToAddrDesc(dbtestdata.EthAddr5d, parser), + to: addressToAddrDesc(dbtestdata.EthAddr7b, parser), + contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), + transferType: bchain.ERC1155, + idValues: []bchain.TokenTransferIdValue{ + { + Id: *big.NewInt(1234), + Value: *big.NewInt(98765), + }, + { + Id: *big.NewInt(5566), + Value: *big.NewInt(12341234421), + }, + }, + }, + }, + }, + pos: 280, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := make([]byte, 0) + packed := packBlockTx(buf, &tt.blockTx) + got, pos, err := unpackBlockTx(packed, 0) + if err != nil { + t.Errorf("unpackBlockTx() error = %v", err) + return + } + if !reflect.DeepEqual(*got, tt.blockTx) { + t.Errorf("unpackBlockTx() got = %v, want %v", *got, tt.blockTx) + } + if pos != tt.pos { + t.Errorf("unpackBlockTx() pos = %v, want %v", pos, tt.pos) + } + }) + } +} diff --git a/db/rocksdb_test.go b/db/rocksdb_test.go index f511850195..22912104e1 100644 --- a/db/rocksdb_test.go +++ b/db/rocksdb_test.go @@ -152,10 +152,10 @@ func checkColumn(d *RocksDB, col int, kp []keyPair) error { defer it.Close() i := 0 for it.SeekToFirst(); it.Valid(); it.Next() { + key := hex.EncodeToString(it.Key().Data()) if i >= len(kp) { - return errors.Errorf("Expected less rows in column %v", cfNames[col]) + return errors.Errorf("Expected less rows in column %v, superfluous key %v", cfNames[col], key) } - key := hex.EncodeToString(it.Key().Data()) if key != kp[i].Key { return errors.Errorf("Incorrect key %v found in column %v row %v, expecting %v", key, cfNames[col], i, kp[i].Key) } diff --git a/docs/rocksdb.md b/docs/rocksdb.md index 3860a70b3d..755049d205 100644 --- a/docs/rocksdb.md +++ b/docs/rocksdb.md @@ -84,9 +84,21 @@ Column families used only by **Ethereum type** coins: - **addressContracts** (used only by Ethereum type coins) - Maps *addrDesc* to *total number of transactions*, *number of non contract transactions*, *number of internal transactions* and array of *contracts* with *number of transfers* of given address. + Maps *addrDesc* to *total number of transactions*, *number of non contract transactions*, *number of internal transactions* + and array of *contracts* with *number of transfers* of given address. ``` - (addrDesc []byte) -> (total_txs vuint)+(non-contract_txs vuint)+(internal_txs vuint)+[]((contractAddrDesc []byte)+(nr_transfers vuint)) + (addrDesc []byte) -> (total_txs vuint)+(non-contract_txs vuint)+(internal_txs vuint)+ + []((contractAddrDesc []byte)+(type+4*nr_transfers vuint))+ + <(value bigInt) if ERC20> or <(nr_values vuint)+[](id bigInt) if ERC721> or <(nr_values vuint)+[]((id bigInt)+(value bigInt)) if ERC1155> + ``` + +- **internalData** (used only by Ethereum type coins) + + Maps *txid* to *type (CALL 0 | CREATE 1)*, *addrDesc of created contract for CREATE type*, array of *type (CALL 0 | CREATE 1 | SELFDESTRUCT 2)*, *from addrDesc*, *to addrDesc*, *value bigInt* and possible *error*. + ``` + (txid []byte) -> (type+2*nr_transfers vuint)+<(addrDesc []byte) if CREATE>+ + []((type byte)+(fromAddrDesc []byte)+(toAddrDesc []byte)+(value bigInt))+ + (error []byte) ``` - **blockTxs** @@ -104,9 +116,14 @@ Column families used only by **Ethereum type** coins: - Ethereum type The value is an array of transaction data. For each transaction is stored *txid*, - *from* and *to* address descriptors and array of *contract address descriptors* with *transfer address descriptors*. - ``` - (height uint32) -> []((txid [32]byte)+(from addrDesc)+(to addrDesc)+(nr_contracts vuint)+[]((contract addrDesc)+(addr addrDesc))) + *from* and *to* address descriptors and array of contract transfer infos consisting of + *from*, *to* and *contract* address descriptors, *type (ERC20 0 | ERC721 1 | ERC1155 2)* and value (or list of id+value for ERC1155) + ``` + (height uint32) -> []( + (txid [32]byte)+(from addrDesc)+(to addrDesc)+(nr_contracts vuint)+ + []((from addrDesc)+(to addrDesc)+(contract addrDesc)+(type byte)+ + <(value bigInt) if ERC20 or ERC721> or <(nr_values vuint)+[]((id bigInt)+(value bigInt)) if ERC1155>) + ) ``` - **transactions** diff --git a/server/websocket.go b/server/websocket.go index 1942fdabe4..27a738275a 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -919,8 +919,8 @@ func (s *WebsocketServer) getNewTxSubscriptions(tx *bchain.MempoolTx) map[string } } } - for i := range tx.Erc20 { - addrDesc, err := s.chainParser.GetAddrDescFromAddress(tx.Erc20[i].From) + for i := range tx.TokenTransfers { + addrDesc, err := s.chainParser.GetAddrDescFromAddress(tx.TokenTransfers[i].From) if err == nil && len(addrDesc) > 0 { sad := string(addrDesc) as, ok := s.addressSubscriptions[sad] @@ -928,7 +928,7 @@ func (s *WebsocketServer) getNewTxSubscriptions(tx *bchain.MempoolTx) map[string subscribed[sad] = struct{}{} } } - addrDesc, err = s.chainParser.GetAddrDescFromAddress(tx.Erc20[i].To) + addrDesc, err = s.chainParser.GetAddrDescFromAddress(tx.TokenTransfers[i].To) if err == nil && len(addrDesc) > 0 { sad := string(addrDesc) as, ok := s.addressSubscriptions[sad] diff --git a/tests/dbtestdata/dbtestdata_ethereumtype.go b/tests/dbtestdata/dbtestdata_ethereumtype.go index 65683ec58a..03e72f56e7 100644 --- a/tests/dbtestdata/dbtestdata_ethereumtype.go +++ b/tests/dbtestdata/dbtestdata_ethereumtype.go @@ -9,26 +9,73 @@ import ( // Addresses const ( + EthAddrZero = "0000000000000000000000000000000000000000" EthAddr3e = "3e3a3d69dc66ba10737f531ed088954a9ec89d97" EthAddr55 = "555ee11fbddc0e49a9bab358a8941ad95ffdb48f" EthAddr20 = "20cd153de35d469ba46127a0c8f18626b59a256a" EthAddr9f = "9f4981531fda132e83c44680787dfa7ee31e4f8d" EthAddr4b = "4bda106325c335df99eab7fe363cac8a0ba2a24d" EthAddr7b = "7b62eb7fe80350dc7ec945c0b73242cb9877fb1b" - EthAddrContract4a = "4af4114f73d1c1c903ac9e0361b379d1291808a2" // ERC-20 (VTY) - EthAddrContract0d = "0d0f936ee4c93e25944694d6c121de94d9760f11" // ERC-20 (MTT) + EthAddr83 = "837e3f699d85a4b0b99894567e9233dfb1dcb081" + EthAddrA3 = "a3950b823cb063dd9afc0d27f35008b805b3ed53" + EthAddr5d = "5dc6288b35e0807a3d6feb89b3a2ff4ab773168e" + EthAddr92 = "9248A6048a58db9f0212dC7CD85eE8741128be72" + EthAddrContract4a = "4af4114f73d1c1c903ac9e0361b379d1291808a2" // ERC20 (VTY) + EthAddrContract0d = "0d0f936ee4c93e25944694d6c121de94d9760f11" // ERC20 (MTT) EthAddrContract47 = "479cc461fecd078f766ecc58533d6f69580cf3ac" // non ERC20 + EthAddrContractCd = "cda9fc258358ecaa88845f19af595e908bb7efe9" // ERC721 + EthAddrContract6f = "6fd712e3a5b556654044608f9129040a4839e36c" // ERC1155 + // non contract + // EthAddr3e -> EthAddr55, value 1999622000000000000 EthTxidB1T1 = "cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b" EthTx1Packed = "08e8dd870210a6a6f0db051a6908ece40212050430e234001888a40122081bc0159d530e60003220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b3a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f42143e3a3d69dc66ba10737f531ed088954a9ec89d97480a22070a025208120101" EthTx1FailedPacked = "08e8dd870210a6a6f0db051a6908ece40212050430e234001888a40122081bc0159d530e60003220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b3a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f42143e3a3d69dc66ba10737f531ed088954a9ec89d97480a22040a025208" EthTx1NoStatusPacked = "08e8dd870210a6a6f0db051a6908ece40212050430e234001888a40122081bc0159d530e60003220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b3a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f42143e3a3d69dc66ba10737f531ed088954a9ec89d97480a22070a025208120155" - EthTxidB1T2 = "a9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101" - EthTx2Packed = "08e8dd870210a6a6f0db051aa20108d001120509502f900018d5e1042a44a9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab24000003220a9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b1013a144af4114f73d1c1c903ac9e0361b379d1291808a2421420cd153de35d469ba46127a0c8f18626b59a256a22a8010a02cb391201011a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a2122000000000000000000000000000000000000000000000021e19e0c9bab24000001a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a2000000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f" - EthTxidB2T1 = "c2c3dd1ecb00e8a6d81f793d24387cf2947a313e94ab03b1fb22cd63320f6c91" - EthTx3Packed = "08e9dd870210d4b5f0db051a6708c20112050218711a001888a401220710bc3578bd37d83220c2c3dd1ecb00e8a6d81f793d24387cf2947a313e94ab03b1fb22cd63320f6c913a149f4981531fda132e83c44680787dfa7ee31e4f8d4214555ee11fbddc0e49a9bab358a8941ad95ffdb48f480722070a025208120101" - EthTxidB2T2 = "c92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2" - EthTx4Packed = "08e9dd870210d4b5f0db051aa50b08f6be0712043b9aca001890a10f2ac40a4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f80000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c73843220c92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf23a14479cc461fecd078f766ecc58533d6f69580cf3ac42144bda106325c335df99eab7fe363cac8a0ba2a24d482422d40b0a03034d301201011a9e010a140d0f936ee4c93e25944694d6c121de94d9760f1112200000000000000000000000000000000000000000000000006a8313d60b1f606b1a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a21220000000000000000000000000000000000000000000000000000308fd0e798ac01a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1aa1030a14479cc461fecd078f766ecc58533d6f69580cf3ac1280020000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f606b000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac1a200d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb31a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1a2000000000000000000000000000000000000000000000000000000000000000001a205af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f1a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a2122000000000000000000000000000000000000000000000000000031855667df7a81a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a9e010a140d0f936ee4c93e25944694d6c121de94d9760f1112200000000000000000000000000000000000000000000000006a8313d60b1f80001a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1aa1030a14479cc461fecd078f766ecc58533d6f69580cf3ac1280020000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f481a200d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb31a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1a2000000000000000000000000000000000000000000000000000000000000000001a20b0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa" + + // ERC20 + // EthAddr20 -> EthAddrContract4a, value 0 + // ERC20 EthAddrContract4a: EthAddr20 -> EthAddr55, value 10000000000000000000000 + EthTxidB1T2 = "a9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101" + EthTx2Packed = "08e8dd870210a6a6f0db051aa20108d001120509502f900018d5e1042a44a9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab24000003220a9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b1013a144af4114f73d1c1c903ac9e0361b379d1291808a2421420cd153de35d469ba46127a0c8f18626b59a256a22a8010a02cb391201011a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a2122000000000000000000000000000000000000000000000021e19e0c9bab24000001a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a2000000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f" + + // non contract + // EthAddr55 -> EthAddr9f, value 4710537472325592 + EthTxidB2T1 = "c2c3dd1ecb00e8a6d81f793d24387cf2947a313e94ab03b1fb22cd63320f6c91" + EthTx3Packed = "08e9dd870210d4b5f0db051a6708c20112050218711a001888a401220710bc3578bd37d83220c2c3dd1ecb00e8a6d81f793d24387cf2947a313e94ab03b1fb22cd63320f6c913a149f4981531fda132e83c44680787dfa7ee31e4f8d4214555ee11fbddc0e49a9bab358a8941ad95ffdb48f480722070a025208120101" + + // ERC20 + // EthAddr4b -> EthAddrContract47, value 0 + // ERC20 EthAddrContract0d: EthAddr55 -> EthAddr4b, value 7675000000000000001 + // ERC20 EthAddrContract4a: EthAddr4b -> EthAddr55, value 854307892726464 + // ERC20 EthAddrContract4a: EthAddr7b -> EthAddr4b, value 871180000950184 + // ERC20 EthAddrContract0d: EthAddr4b -> EthAddr7b, value 7674999999999991915 + EthTxidB2T2 = "c92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2" + EthTx4Packed = "08e9dd870210d4b5f0db051aa50b08f6be0712043b9aca001890a10f2ac40a4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c73843220c92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf23a14479cc461fecd078f766ecc58533d6f69580cf3ac42144bda106325c335df99eab7fe363cac8a0ba2a24d482422d40b0a03034d301201011a9e010a140d0f936ee4c93e25944694d6c121de94d9760f1112200000000000000000000000000000000000000000000000006a8313d60b1f80011a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a21220000000000000000000000000000000000000000000000000000308fd0e798ac01a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1aa1030a14479cc461fecd078f766ecc58533d6f69580cf3ac1280020000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac1a200d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb31a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1a2000000000000000000000000000000000000000000000000000000000000000001a205af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f1a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a2122000000000000000000000000000000000000000000000000000031855667df7a81a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a9e010a140d0f936ee4c93e25944694d6c121de94d9760f1112200000000000000000000000000000000000000000000000006a8313d60b1f606b1a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a200000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d1a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1aa1030a14479cc461fecd078f766ecc58533d6f69580cf3ac1280020000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f481a200d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb31a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1a2000000000000000000000000000000000000000000000000000000000000000001a20b0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa" + + // ERC721 + // EthAddr83 -> EthAddrContractCd, value 0 + // ERC721 EthAddrContractCd: EthAddr83 -> EthAddr7b, value 1 + EthTxidB2T3 = "ca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a" + EthTx5Packed = "089ff7cc05109eaecd8e061ac2010802120459682f0718a9e7052a6423b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b00000000000000000000000000000000000000000000000000000000000000013220ca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a3a14cda9fc258358ecaa88845f19af595e908bb7efe94214837e3f699d85a4b0b99894567e9233dfb1dcb081480122c9020a02e5061201011a9e010a14cda9fc258358ecaa88845f19af595e908bb7efe91a208c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9251a20000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0811a2000000000000000000000000000000000000000000000000000000000000000001a2000000000000000000000000000000000000000000000000000000000000000011a9e010a14cda9fc258358ecaa88845f19af595e908bb7efe91a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a20000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0811a200000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b1a200000000000000000000000000000000000000000000000000000000000000001" + + // ERC1155 TransferSingle + // EthAddr3e -> EthAddr92, value 100000000000000000 + // ERC1155 EthAddrContract6f: EthAddrA3 -> EthAddr3e, values [(150,1)] + EthTxidB2T4 = "463a2a3f6303f88aec60fe7859081f80e8845b39495969a819c6bae9283aa12a" + EthTx6Packed = "08d2a6c80510ccfe8c8e061aad0108c1021204595faa4318f2dd0f2208016345785d8a00002a44d9bdda70000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000013220463a2a3f6303f88aec60fe7859081f80e8845b39495969a819c6bae9283aa12a3a149248a6048a58db9f0212dc7cd85ee8741128be7242143e3a3d69dc66ba10737f531ed088954a9ec89d97480822e7050a0302120d1201011abb020a146fd712e3a5b556654044608f9129040a4839e36c128002000000000000000000000000a3950b823cb063dd9afc0d27f35008b805b3ed530000000000000000000000003e3a3d69dc66ba10737f531ed088954a9ec89d97000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000011a205f9832c7244497a64c11c4a4f7597934bdf02b0361c54ad8e90091c2ce1f9e3c1ae0010a146fd712e3a5b556654044608f9129040a4839e36c1240000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000011a20c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f621a200000000000000000000000009248a6048a58db9f0212dc7cd85ee8741128be721a20000000000000000000000000a3950b823cb063dd9afc0d27f35008b805b3ed531a200000000000000000000000003e3a3d69dc66ba10737f531ed088954a9ec89d971abb010a149248a6048a58db9f0212dc7cd85ee8741128be721280010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000a3950b823cb063dd9afc0d27f35008b805b3ed530000000000000000000000003e3a3d69dc66ba10737f531ed088954a9ec89d9700000000000000000000000000000000000000000000000000000000000000011a200b7bef9468bee71526deef3cbbded0ec1a0aa3d5a3e81eaffb0e758552b33199" + + // ERC1155 TransferBatch + // EthAddr5d -> EthAddrContract6f, value 0 + // ERC1155 EthAddrContract6f: EthAddrZero -> EthAddr5d, values [(1776,1),(1898,10)] + EthTxidB2T5 = "6942c79c04ae981a2d194deb0ae5ae5e9d5d7a90fd9f52246b162fa645155e3a" + EthTx7Packed = "08a6c7d504108bb88f82061ae103085612044235839b18fbbf042a8403786279190000000000000000000000005dc6288b35e0807a3d6feb89b3a2ff4ab773168e000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000006f0000000000000000000000000000000000000000000000000000000000000076a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001010000000000000000000000000000000000000000000000000000000000000032206942c79c04ae981a2d194deb0ae5ae5e9d5d7a90fd9f52246b162fa645155e3a3a146fd712e3a5b556654044608f9129040a4839e36c42145dc6288b35e0807a3d6feb89b3a2ff4ab773168e22ac030a03011ffb1201011aa1030a146fd712e3a5b556654044608f9129040a4839e36c128002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000006f0000000000000000000000000000000000000000000000000000000000000076a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a1a204a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb1a200000000000000000000000005dc6288b35e0807a3d6feb89b3a2ff4ab773168e1a2000000000000000000000000000000000000000000000000000000000000000001a200000000000000000000000005dc6288b35e0807a3d6feb89b3a2ff4ab773168e" + + // ERC20 - special (not realistic) tx, all transfers from the same address to the same address + // EthAddr55 -> EthAddr55, value 0 + // ERC20 EthAddr55: EthAddr55 -> EthAddr55, value 10000000000000000000000 + EthTxidB2T6 = "e71e0d1dc1ac58b7a0c9fb14d0693af0764df07a72d882fffc020e464c91b63c" + EthTx8Packed = "08e8dd870210a6a6f0db051aa20108d001120509502f900018d5e1042a44a9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab24000003220e71e0d1dc1ac58b7a0c9fb14d0693af0764df07a72d882fffc020e464c91b63c3a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f4214555ee11fbddc0e49a9bab358a8941ad95ffdb48f22a8010a02cb391201011a9e010a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f122000000000000000000000000000000000000000000000021e19e0c9bab24000001a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f" ) var EthTx2InternalData = &bchain.EthereumInternalData{ @@ -138,6 +185,14 @@ func GetTestEthereumTypeBlock2(parser bchain.BlockChainParser) *bchain.Block { }, { packed: EthTx4Packed, internal: EthTx4InternalData, + }, { + packed: EthTx5Packed, + }, { + packed: EthTx6Packed, + }, { + packed: EthTx7Packed, + }, { + packed: EthTx8Packed, }}, parser), } } From 1b4ca7ad93a892150d3ef1401de8dcc5b3dbd92b Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 18 Jan 2022 22:31:50 +0100 Subject: [PATCH 045/530] =?UTF-8?q?eth=20archive=20(+testnet)=201.10.12=20?= =?UTF-8?q?=E2=86=92=201.10.15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum_archive.json | 6 +++--- configs/coins/ethereum_testnet_ropsten_archive.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 8d7fc12ffb..be4e4815e6 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -21,10 +21,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.12-6c4dc6c3", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.12-6c4dc6c3.tar.gz", + "version": "1.10.15-8be800ff", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.15-8be800ff.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.12-6c4dc6c3.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.15-8be800ff.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 --gcmode archive --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 8116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index f109bc02ff..d201f68821 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -20,10 +20,10 @@ "package_name": "backend-ethereum-testnet-ropsten-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.12-6c4dc6c3", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.12-6c4dc6c3.tar.gz", + "version": "1.10.15-8be800ff", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.15-8be800ff.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.12-6c4dc6c3.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.15-8be800ff.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From ae2d0e3958ea65a3814ad7e491b9a2daa86a194e Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 24 Jan 2022 01:08:32 +0100 Subject: [PATCH 046/530] Bump go-ethereum to v1.10.15 --- go.mod | 4 +--- go.sum | 40 ++++++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index f02c5d7c42..33b7b51d24 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/decred/dcrd/dcrutil/v3 v3.0.0 github.com/decred/dcrd/hdkeychain/v3 v3.0.0 github.com/decred/dcrd/txscript/v3 v3.0.0 - github.com/ethereum/go-ethereum v1.10.8 + github.com/ethereum/go-ethereum v1.10.15 github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect @@ -31,14 +31,12 @@ require ( github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde github.com/mr-tron/base58 v1.2.0 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pebbe/zmq4 v1.2.1 github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29 github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e github.com/prometheus/client_golang v1.8.0 github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect ) diff --git a/go.sum b/go.sum index 184844b5eb..458f52bd7a 100644 --- a/go.sum +++ b/go.sum @@ -125,6 +125,7 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -172,9 +173,10 @@ github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRk github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= @@ -185,8 +187,8 @@ github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaB github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.10.8 h1:0UP5WUR8hh46ffbjJV7PK499+uGEyasRIfffS0vy06o= -github.com/ethereum/go-ethereum v1.10.8/go.mod h1:pJNuIUYfX5+JKzSD/BTdNsvJSZ1TJqmz0dVyXMAbf6M= +github.com/ethereum/go-ethereum v1.10.15 h1:E9o0kMbD8HXhp7g6UwIwntY05WTDheCGziMhegcBsQw= +github.com/ethereum/go-ethereum v1.10.15/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -224,7 +226,7 @@ github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= @@ -259,9 +261,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -300,6 +302,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= @@ -371,8 +375,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= -github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw= -github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -387,9 +390,12 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= @@ -442,6 +448,10 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -460,8 +470,6 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= @@ -592,8 +600,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= -github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= @@ -879,8 +887,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= From 72e0ac23bc203fee69840a291490668e94dc508f Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 24 Jan 2022 01:11:18 +0100 Subject: [PATCH 047/530] Return internal data and ERC721 and ERC1155 tokens from API and explorer --- api/types.go | 26 +++- api/worker.go | 64 ++++++-- bchain/coins/eth/ethrpc.go | 29 ++-- server/public.go | 12 ++ server/public_ethereumtype_test.go | 2 +- static/templates/address.html | 4 + static/templates/txdetail_ethereumtype.html | 156 +++++++++++++++++++- 7 files changed, 254 insertions(+), 39 deletions(-) diff --git a/api/types.go b/api/types.go index 3e7ffed5c3..6a0bd165bd 100644 --- a/api/types.go +++ b/api/types.go @@ -187,16 +187,25 @@ type TokenTransfer struct { Values []TokenTransferValues `json:"values,omitempty"` } +type EthereumInternalTransfer struct { + Type bchain.EthereumInternalTransactionType `json:"type"` + From string `json:"from"` + To string `json:"to"` + Value *Amount `json:"value"` +} + // EthereumSpecific contains ethereum specific transaction data type EthereumSpecific struct { - TxType string `json:"txType,omitempty"` - Status eth.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending - Nonce uint64 `json:"nonce"` - GasLimit *big.Int `json:"gasLimit"` - GasUsed *big.Int `json:"gasUsed"` - GasPrice *Amount `json:"gasPrice"` - Data string `json:"data,omitempty"` - InternalTransfers []bchain.EthereumInternalTransfer `json:"internalTransfers,omitempty"` + Type bchain.EthereumInternalTransactionType `json:"type,omitempty"` + CreatedContract string `json:"createdContract,omitempty"` + Status eth.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending + Error string `json:"error,omitempty"` + Nonce uint64 `json:"nonce"` + GasLimit *big.Int `json:"gasLimit"` + GasUsed *big.Int `json:"gasUsed"` + GasPrice *Amount `json:"gasPrice"` + Data string `json:"data,omitempty"` + InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"` } // Tx holds information about a transaction @@ -279,6 +288,7 @@ type Address struct { UnconfirmedTxs int `json:"unconfirmedTxs"` Txs int `json:"txs"` NonTokenTxs int `json:"nonTokenTxs,omitempty"` + InternalTxs int `json:"internalTxs,omitempty"` Transactions []*Tx `json:"transactions,omitempty"` Txids []string `json:"txids,omitempty"` Nonce string `json:"nonce,omitempty"` diff --git a/api/worker.go b/api/worker.go index 19583aa97a..ac9cdb9069 100644 --- a/api/worker.go +++ b/api/worker.go @@ -261,6 +261,15 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe } tokens = w.getEthereumTokensTransfers(tokenTransfers) ethTxData := eth.GetEthereumTxData(bchainTx) + + var internalData *bchain.EthereumInternalData + if eth.ProcessInternalTransactions { + internalData, err = w.db.GetEthereumInternalData(bchainTx.Txid) + if err != nil { + return nil, err + } + } + // mempool txs do not have fees yet if ethTxData.GasUsed != nil { feesSat.Mul(ethTxData.GasPrice, ethTxData.GasUsed) @@ -276,6 +285,21 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe Status: ethTxData.Status, Data: ethTxData.Data, } + if internalData != nil { + ethSpecific.Type = internalData.Type + ethSpecific.CreatedContract = internalData.Contract + ethSpecific.Error = internalData.Error + ethSpecific.InternalTransfers = make([]EthereumInternalTransfer, len(internalData.Transfers)) + for i := range internalData.Transfers { + f := &internalData.Transfers[i] + t := ðSpecific.InternalTransfers[i] + t.From = f.From + t.To = f.To + t.Type = f.Type + t.Value = (*Amount)(&f.Value) + } + } + } // for now do not return size, we would have to compute vsize of segwit transactions // size:=len(bchainTx.Hex) / 2 @@ -427,13 +451,25 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers) []T if erc20c == nil { erc20c = &bchain.Erc20Contract{Name: t.Contract} } + var value *Amount + var values []TokenTransferValues + if t.Type == bchain.ERC1155 { + values = make([]TokenTransferValues, len(t.IdValues)) + for j := range values { + values[j].Id = (*Amount)(&t.IdValues[j].Id) + values[j].Value = (*Amount)(&t.IdValues[j].Value) + } + } else { + value = (*Amount)(&t.Value) + } tokens[i] = TokenTransfer{ Type: TokenTypeMap[t.Type], Token: t.Contract, From: t.From, To: t.To, + Value: value, + Values: values, Decimals: erc20c.Decimals, - Value: (*Amount)(&t.Value), Name: erc20c.Name, Symbol: erc20c.Symbol, } @@ -657,29 +693,30 @@ func (w *Worker) getEthereumToken(index int, addrDesc, contract bchain.AddressDe }, nil } -func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, error) { +func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, int, error) { var ( ba *db.AddrBalance tokens []Token ci *bchain.Erc20Contract n uint64 nonContractTxs int + internalTxs int ) // unknown number of results for paging totalResults := -1 ca, err := w.db.GetAddrDescContracts(addrDesc) if err != nil { - return nil, nil, nil, 0, 0, 0, NewAPIError(fmt.Sprintf("Address not found, %v", err), true) + return nil, nil, nil, 0, 0, 0, 0, NewAPIError(fmt.Sprintf("Address not found, %v", err), true) } b, err := w.chain.EthereumTypeGetBalance(addrDesc) if err != nil { - return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetBalance %v", addrDesc) + return nil, nil, nil, 0, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetBalance %v", addrDesc) } var filterDesc bchain.AddressDescriptor if filter.Contract != "" { filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract) if err != nil { - return nil, nil, nil, 0, 0, 0, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true) + return nil, nil, nil, 0, 0, 0, 0, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true) } } if ca != nil { @@ -691,7 +728,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto } n, err = w.chain.EthereumTypeGetNonce(addrDesc) if err != nil { - return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc) + return nil, nil, nil, 0, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc) } if details > AccountDetailsBasic { tokens = make([]Token, len(ca.Contracts)) @@ -706,7 +743,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto } t, err := w.getEthereumToken(i+db.ContractIndexOffset, addrDesc, c.Contract, details, int(c.Txs)) if err != nil { - return nil, nil, nil, 0, 0, 0, err + return nil, nil, nil, 0, 0, 0, 0, err } tokens[j] = *t j++ @@ -716,7 +753,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto if len(filterDesc) > 0 && j == 0 && details >= AccountDetailsTokens { t, err := w.getEthereumToken(0, addrDesc, filterDesc, details, 0) if err != nil { - return nil, nil, nil, 0, 0, 0, err + return nil, nil, nil, 0, 0, 0, 0, err } tokens = []Token{*t} // switch off query for transactions, there are no transactions @@ -727,7 +764,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto } ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc) if err != nil { - return nil, nil, nil, 0, 0, 0, err + return nil, nil, nil, 0, 0, 0, 0, err } if filter.FromHeight == 0 && filter.ToHeight == 0 { // compute total results for paging @@ -742,6 +779,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto } } nonContractTxs = int(ca.NonContractTxs) + internalTxs = int(ca.InternalTxs) } else { // addresses without any normal transactions can have internal transactions and therefore balance if b != nil { @@ -753,14 +791,14 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto if len(filterDesc) > 0 && details >= AccountDetailsTokens { t, err := w.getEthereumToken(0, addrDesc, filterDesc, details, 0) if err != nil { - return nil, nil, nil, 0, 0, 0, err + return nil, nil, nil, 0, 0, 0, 0, err } tokens = []Token{*t} // switch off query for transactions, there are no transactions filter.Vout = AddressFilterVoutQueryNotNecessary } } - return ba, tokens, ci, n, nonContractTxs, totalResults, nil + return ba, tokens, ci, n, nonContractTxs, internalTxs, totalResults, nil } func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetails, blockInfo *db.BlockInfo) (*Tx, error) { @@ -865,6 +903,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco nonce string unconfirmedTxs int nonTokenTxs int + internalTxs int totalResults int ) addrDesc, address, err := w.getAddrDescAndNormalizeAddress(address) @@ -873,7 +912,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco } if w.chainType == bchain.ChainEthereumType { var n uint64 - ba, tokens, erc20c, n, nonTokenTxs, totalResults, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter) + ba, tokens, erc20c, n, nonTokenTxs, internalTxs, totalResults, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter) if err != nil { return nil, err } @@ -977,6 +1016,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco TotalSentSat: (*Amount)(totalSent), Txs: int(ba.Txs), NonTokenTxs: nonTokenTxs, + InternalTxs: internalTxs, UnconfirmedBalanceSat: (*Amount)(&uBalSat), UnconfirmedTxs: unconfirmedTxs, Transactions: txs, diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 1b6f2803a2..8c4630c236 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -36,14 +36,15 @@ const ( // Configuration represents json config file type Configuration struct { - CoinName string `json:"coin_name"` - CoinShortcut string `json:"coin_shortcut"` - RPCURL string `json:"rpc_url"` - RPCTimeout int `json:"rpc_timeout"` - BlockAddressesToKeep int `json:"block_addresses_to_keep"` - MempoolTxTimeoutHours int `json:"mempoolTxTimeoutHours"` - QueryBackendOnMempoolResync bool `json:"queryBackendOnMempoolResync"` - ProcessInternalTransactions bool `json:"processInternalTransactions"` + CoinName string `json:"coin_name"` + CoinShortcut string `json:"coin_shortcut"` + RPCURL string `json:"rpc_url"` + RPCTimeout int `json:"rpc_timeout"` + BlockAddressesToKeep int `json:"block_addresses_to_keep"` + MempoolTxTimeoutHours int `json:"mempoolTxTimeoutHours"` + QueryBackendOnMempoolResync bool `json:"queryBackendOnMempoolResync"` + ProcessInternalTransactions bool `json:"processInternalTransactions"` + ProcessZeroInternalTransactions bool `json:"processZeroInternalTransactions"` } // EthereumRPC is an interface to JSON-RPC eth service. @@ -65,6 +66,9 @@ type EthereumRPC struct { ChainConfig *Configuration } +// ProcessInternalTransactions specifies if internal transactions are processed +var ProcessInternalTransactions bool + // NewEthereumRPC returns new EthRPC instance. func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { var err error @@ -90,6 +94,8 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification ChainConfig: &c, } + ProcessInternalTransactions = c.ProcessInternalTransactions + // always create parser s.Parser = NewEthereumParser(c.BlockAddressesToKeep) s.timeout = time.Duration(c.RPCTimeout) * time.Second @@ -498,7 +504,7 @@ func (b *EthereumRPC) getTokenTransferEventsForBlock(blockNumber string) (map[st err := b.rpc.CallContext(ctx, &logs, "eth_getLogs", map[string]interface{}{ "fromBlock": blockNumber, "toBlock": blockNumber, - "topics": []string{tokenTransferEventSignature, tokenERC1155TransferSingleEventSignature, tokenERC1155TransferBatchEventSignature}, + // "topics": []string{tokenTransferEventSignature, tokenERC1155TransferSingleEventSignature, tokenERC1155TransferBatchEventSignature}, }) if err != nil { return nil, errors.Annotatef(err, "blockNumber %v", blockNumber) @@ -543,7 +549,7 @@ func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInt From: call.From, To: call.To, }) - } else if err == nil && value.BitLen() > 0 { + } else if err == nil && (value.BitLen() > 0 || b.ChainConfig.ProcessZeroInternalTransactions) { d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ Value: *value, From: call.From, @@ -561,7 +567,7 @@ func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInt // getInternalDataForBlock fetches debug trace using callTracer, extracts internal transfers and creations and destructions of contracts func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []bchain.RpcTransaction) ([]bchain.EthereumInternalData, error) { data := make([]bchain.EthereumInternalData, len(transactions)) - if b.ChainConfig.ProcessInternalTransactions { + if ProcessInternalTransactions { ctx, cancel := context.WithTimeout(context.Background(), b.timeout) defer cancel() var trace []rpcTraceResult @@ -640,6 +646,7 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error internalData, err := b.getInternalDataForBlock(head.Hash, body.Transactions) if err != nil { blockSpecificData = &bchain.EthereumBlockSpecificData{InternalDataError: err.Error()} + glog.Info("InternalDataError ", bbh.Height, ": ", err.Error()) } btxs := make([]bchain.Tx, len(body.Transactions)) diff --git a/server/public.go b/server/public.go index 125c273a3b..e778306bc4 100644 --- a/server/public.go +++ b/server/public.go @@ -456,6 +456,7 @@ func (s *PublicServer) parseTemplates() []*template.Template { "setTxToTemplateData": setTxToTemplateData, "isOwnAddress": isOwnAddress, "toJSON": toJSON, + "tokenTransfersCount": tokenTransfersCount, } var createTemplate func(filenames ...string) *template.Template if s.debug { @@ -558,6 +559,17 @@ func isOwnAddress(td *TemplateData, a string) bool { return a == td.AddrStr } +// called from template, returns count of token transfers of given type +func tokenTransfersCount(tx *api.Tx, t api.TokenType) int { + count := 0 + for i := range tx.TokenTransfers { + if tx.TokenTransfers[i].Type == t { + count++ + } + } + return count +} + func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) { var tx *api.Tx var err error diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 4c9c4878dd..55ed9dc544 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -34,7 +34,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":18}],"erc20Contract":{"contract":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","name":"Contract 75","symbol":"S75","decimals":18}}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":18}],"erc20Contract":{"contract":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","name":"Contract 75","symbol":"S75","decimals":18}}`, }, }, } diff --git a/static/templates/address.html b/static/templates/address.html index 208a2b4483..b5b67d3d10 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -22,6 +22,10 @@

Confirmed

Non-contract Transactions {{$addr.NonTokenTxs}} + + Internal Transactions + {{$addr.InternalTxs}} + Nonce {{$addr.Nonce}} diff --git a/static/templates/txdetail_ethereumtype.html b/static/templates/txdetail_ethereumtype.html index c8c9f8bf56..36aed5488d 100644 --- a/static/templates/txdetail_ethereumtype.html +++ b/static/templates/txdetail_ethereumtype.html @@ -6,6 +6,7 @@ {{if eq $tx.EthereumSpecific.Status 1}}{{end}}{{if eq $tx.EthereumSpecific.Status 0}}{{end}} {{- if $tx.Blocktime}}
{{if $tx.Confirmations}}mined{{else}}first seen{{end}} {{formatUnixTime $tx.Blocktime}}
{{end -}} + {{if $tx.EthereumSpecific.Error}}
Error: {{$tx.EthereumSpecific.Error}}
{{end}}
@@ -67,19 +68,154 @@ {{formatAmount $tx.ValueOutSat}} {{$cs}}
- {{- if $tx.TokenTransfers -}} + + {{if $tx.EthereumSpecific.InternalTransfers}} +
+ Internal Transactions +
+ {{- range $tt := $tx.EthereumSpecific.InternalTransfers -}} +
+
+
+ + + + + + +
+ {{if ne $tt.From $addr}}{{$tt.From}}{{else}}{{$tt.From}}{{end}} +
+
+
+
+ + + +
+
+
+ + + + + + +
+ {{if ne $tt.To $addr}}{{$tt.To}}{{else}}{{$tt.To}}{{end}} +
+
+
+
{{formatAmount $tt.Value}} {{$cs}}
+
+ {{- end -}} +
+ {{- end -}} + + {{- if tokenTransfersCount $tx "ERC20" -}}
ERC20 Token Transfers
- {{- range $erc20 := $tx.TokenTransfers -}} + {{- range $tt := $tx.TokenTransfers -}} + {{if eq $tt.Type "ERC20"}} +
+
+
+ + + + + + +
+ {{if ne $tt.From $addr}}{{$tt.From}}{{else}}{{$tt.From}}{{end}} +
+
+
+
+ + + +
+
+
+ + + + + + +
+ {{if ne $tt.To $addr}}{{$tt.To}}{{else}}{{$tt.To}}{{end}} +
+
+
+
{{formatAmountWithDecimals $tt.Value $tt.Decimals}} {{$tt.Symbol}}
+
+ {{- end -}} + {{- end -}} +
+ {{- end -}} + + {{- if tokenTransfersCount $tx "ERC721" -}} +
+ ERC721 Token Transfers +
+ {{- range $tt := $tx.TokenTransfers -}} + {{if eq $tt.Type "ERC721"}} +
+
+
+ + + + + + +
+ {{if ne $tt.From $addr}}{{$tt.From}}{{else}}{{$tt.From}}{{end}} +
+
+
+
+ + + +
+
+
+ + + + + + +
+ {{if ne $tt.To $addr}}{{$tt.To}}{{else}}{{$tt.To}}{{end}} +
+
+
+
ID {{formatAmountWithDecimals $tt.Value 0}} {{$tt.Symbol}}
+
+ {{- end -}} + {{- end -}} +
+ {{- end -}} + + {{- if tokenTransfersCount $tx "ERC1155" -}} +
+ ERC1155 Token Transfers +
+ {{- range $tt := $tx.TokenTransfers -}} + {{if eq $tt.Type "ERC1155"}}
- + @@ -95,20 +231,26 @@
- {{if ne $erc20.From $addr}}{{$erc20.From}}{{else}}{{$erc20.From}}{{end}} + {{if ne $tt.From $addr}}{{$tt.From}}{{else}}{{$tt.From}}{{end}}
- +
- {{if ne $erc20.To $addr}}{{$erc20.To}}{{else}}{{$erc20.To}}{{end}} + {{if ne $tt.To $addr}}{{$tt.To}}{{else}}{{$tt.To}}{{end}}
-
{{formatAmountWithDecimals $erc20.Value $erc20.Decimals}} {{$erc20.Symbol}}
+
+ {{- range $iv := $tt.Values -}} + {{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value $tt.Decimals}} {{$tt.Symbol}} + {{- end -}} +
{{- end -}} + {{- end -}}
{{- end -}} +
{{- if $tx.FeesSat -}} From d93a58c423375e5547df27364b676c74925c3f3e Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 28 Jan 2022 10:05:23 +0100 Subject: [PATCH 048/530] Show contract creation/destruction in explorer --- static/templates/address.html | 1 + static/templates/txdetail_ethereumtype.html | 23 +++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/static/templates/address.html b/static/templates/address.html index b5b67d3d10..7c661b78ef 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -108,6 +108,7 @@

Transactions

{{- if $addr.Tokens -}} + {{- range $t := $addr.Tokens -}} {{- end -}} diff --git a/static/templates/txdetail_ethereumtype.html b/static/templates/txdetail_ethereumtype.html index 36aed5488d..aadb773e73 100644 --- a/static/templates/txdetail_ethereumtype.html +++ b/static/templates/txdetail_ethereumtype.html @@ -69,11 +69,34 @@
+ {{if eq $tx.EthereumSpecific.Type 1}} +
+ Contract creation +
+
+
+
+ + + + + + +
+ {{if ne $tx.EthereumSpecific.CreatedContract $addr}}{{$tx.EthereumSpecific.CreatedContract}}{{else}}{{$tx.EthereumSpecific.CreatedContract}}{{end}} +
+
+
+
+ {{end}} + {{if $tx.EthereumSpecific.InternalTransfers}}
Internal Transactions
{{- range $tt := $tx.EthereumSpecific.InternalTransfers -}} + {{if eq $tt.Type 1}}Contract creation{{end}} + {{if eq $tt.Type 2}}Contract destruction{{end}}
From ec510811cd3710bd466bad0af08c70a51459c17b Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 28 Jan 2022 10:06:30 +0100 Subject: [PATCH 049/530] Refactor storing Ethereum block specific data --- db/bulkconnect.go | 17 +++++++---------- db/rocksdb.go | 7 ++----- db/rocksdb_ethereumtype.go | 21 ++++++++++++++++++--- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/db/bulkconnect.go b/db/bulkconnect.go index f6bf4ba033..39adee3490 100644 --- a/db/bulkconnect.go +++ b/db/bulkconnect.go @@ -307,26 +307,23 @@ func (b *BulkConnect) connectBlockEthereumType(block *bchain.Block, storeBlockTx defer wb.Destroy() bac := b.bulkAddressesCount if sa || b.bulkAddressesCount > maxBulkAddresses { - if err := b.storeBulkAddresses(wb); err != nil { + if err = b.storeBulkAddresses(wb); err != nil { return err } } - if err := b.d.storeInternalDataEthereumType(wb, b.ethBlockTxs); err != nil { + if err = b.d.storeInternalDataEthereumType(wb, b.ethBlockTxs); err != nil { return err } b.ethBlockTxs = b.ethBlockTxs[:0] - blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) - if blockSpecificData != nil && blockSpecificData.InternalDataError != "" { - if err := b.d.storeBlockInternalDataErrorEthereumType(wb, block, blockSpecificData.InternalDataError); err != nil { - return err - } + if err = b.d.storeBlockSpecificDataEthereumType(wb, block); err != nil { + return err } if storeBlockTxs { - if err := b.d.storeAndCleanupBlockTxsEthereumType(wb, block, blockTxs); err != nil { + if err = b.d.storeAndCleanupBlockTxsEthereumType(wb, block, blockTxs); err != nil { return err } } - if err := b.d.db.Write(b.d.wo, wb); err != nil { + if err = b.d.db.Write(b.d.wo, wb); err != nil { return err } if bac > b.bulkAddressesCount { @@ -338,7 +335,7 @@ func (b *BulkConnect) connectBlockEthereumType(block *bchain.Block, storeBlockTx if blockSpecificData != nil && blockSpecificData.InternalDataError != "" { wb := gorocksdb.NewWriteBatch() defer wb.Destroy() - if err := b.d.storeBlockInternalDataErrorEthereumType(wb, block, blockSpecificData.InternalDataError); err != nil { + if err = b.d.storeBlockSpecificDataEthereumType(wb, block); err != nil { return err } } diff --git a/db/rocksdb.go b/db/rocksdb.go index 4438db1748..4ef396d2f4 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -484,11 +484,8 @@ func (d *RocksDB) ConnectBlock(block *bchain.Block) error { if err := d.storeInternalDataEthereumType(wb, blockTxs); err != nil { return err } - blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) - if blockSpecificData != nil && blockSpecificData.InternalDataError != "" { - if err := d.storeBlockInternalDataErrorEthereumType(wb, block, blockSpecificData.InternalDataError); err != nil { - return err - } + if err = d.storeBlockSpecificDataEthereumType(wb, block); err != nil { + return err } if err := d.storeAndCleanupBlockTxsEthereumType(wb, block, blockTxs); err != nil { return err diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index 03bbcf49e9..2e2eb4da71 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -215,8 +215,8 @@ func addToContract(c *AddrContract, contractIndex int, index int32, contract bch aggregate = func(s, v *big.Int) { s.Sub(s, v) if s.Sign() < 0 { - glog.Warningf("rocksdb: addToContracts: contract %s, from %s, negative aggregate", transfer.Contract, transfer.From) - s.SetInt64(0) + // glog.Warningf("rocksdb: addToContracts: contract %s, from %s, negative aggregate", transfer.Contract, transfer.From) + s.SetUint64(0) } } } else { @@ -254,8 +254,12 @@ func addToContract(c *AddrContract, contractIndex int, index int32, contract bch } } // if not found and transfer to, add to the list + // it is necessary to add a copy of the value so that subsequent calls to addToContract do not change the transfer value if index >= 0 { - c.IdValues = append(c.IdValues, t) + c.IdValues = append(c.IdValues, bchain.TokenTransferIdValue{ + Id: t.Id, + Value: *new(big.Int).Set(&t.Value), + }) } nextTransfer: } @@ -665,6 +669,17 @@ func (d *RocksDB) storeBlockInternalDataErrorEthereumType(wb *gorocksdb.WriteBat return nil } +func (d *RocksDB) storeBlockSpecificDataEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block) error { + blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) + if blockSpecificData != nil && blockSpecificData.InternalDataError != "" { + glog.Info("storeBlockSpecificDataEthereumType ", block.Height, ": ", blockSpecificData.InternalDataError) + if err := d.storeBlockInternalDataErrorEthereumType(wb, block, blockSpecificData.InternalDataError); err != nil { + return err + } + } + return nil +} + // unpackBlockTx unpacks ethBlockTx from buf, starting at position pos // the position is updated as the data is unpacked and returned to the caller func unpackBlockTx(buf []byte, pos int) (*ethBlockTx, int, error) { From d5e871818a7a32a2c65b5748873253f015b4e50a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 6 Feb 2022 21:57:54 +0100 Subject: [PATCH 050/530] Minor refactor --- blockbook.go | 74 ++++------------------ common/internalstate.go | 13 ++++ common/utils.go | 41 ++++++++++++ db/fiat.go | 135 ++++++++++++++++++++++++++++++++++++++++ db/fiat_test.go | 84 +++++++++++++++++++++++++ db/rocksdb.go | 127 ------------------------------------- db/rocksdb_test.go | 77 ----------------------- 7 files changed, 285 insertions(+), 266 deletions(-) create mode 100644 common/utils.go create mode 100644 db/fiat.go create mode 100644 db/fiat_test.go diff --git a/blockbook.go b/blockbook.go index be1184f2d9..4f8663bc39 100644 --- a/blockbook.go +++ b/blockbook.go @@ -13,7 +13,6 @@ import ( "os/signal" "runtime/debug" "strings" - "sync/atomic" "syscall" "time" @@ -42,7 +41,7 @@ const exitCodeOK = 0 const exitCodeFatal = 255 var ( - blockchain = flag.String("blockchaincfg", "", "path to blockchain RPC service configuration json file") + configFile = flag.String("blockchaincfg", "", "path to blockchain RPC service configuration json file") dbPath = flag.String("datadir", "./data", "path to database directory") dbCache = flag.Int("dbcache", 1<<29, "size of the rocksdb cache") @@ -105,7 +104,6 @@ var ( callbacksOnNewTx []bchain.OnNewTxFunc callbacksOnNewFiatRatesTicker []fiat.OnNewFiatRatesTicker chanOsSignal chan os.Signal - inShutdown int32 ) func init() { @@ -151,26 +149,24 @@ func mainWithExitCode() int { return exitCodeOK } - if *blockchain == "" { + if *configFile == "" { glog.Error("Missing blockchaincfg configuration parameter") return exitCodeFatal } - coin, coinShortcut, coinLabel, err := coins.GetCoinNameFromConfig(*blockchain) + coin, coinShortcut, coinLabel, err := coins.GetCoinNameFromConfig(*configFile) if err != nil { glog.Error("config: ", err) return exitCodeFatal } - // gspt.SetProcTitle("blockbook-" + normalizeName(coin)) - metrics, err = common.GetMetrics(coin) if err != nil { glog.Error("metrics: ", err) return exitCodeFatal } - if chain, mempool, err = getBlockChainWithRetry(coin, *blockchain, pushSynchronizationHandler, metrics, 120); err != nil { + if chain, mempool, err = getBlockChainWithRetry(coin, *configFile, pushSynchronizationHandler, metrics, 120); err != nil { glog.Error("rpc: ", err) return exitCodeFatal } @@ -347,7 +343,7 @@ func mainWithExitCode() int { if internalServer != nil || publicServer != nil || chain != nil { // start fiat rates downloader only if not shutting down immediately - initFiatRatesDownloader(index, *blockchain) + initFiatRatesDownloader(index, *configFile) waitForSignalAndShutdown(internalServer, publicServer, chain, 10*time.Second) } @@ -362,13 +358,13 @@ func mainWithExitCode() int { return exitCodeOK } -func getBlockChainWithRetry(coin string, configfile string, pushHandler func(bchain.NotificationType), metrics *common.Metrics, seconds int) (bchain.BlockChain, bchain.Mempool, error) { +func getBlockChainWithRetry(coin string, configFile string, pushHandler func(bchain.NotificationType), metrics *common.Metrics, seconds int) (bchain.BlockChain, bchain.Mempool, error) { var chain bchain.BlockChain var mempool bchain.Mempool var err error timer := time.NewTimer(time.Second) for i := 0; ; i++ { - if chain, mempool, err = coins.NewBlockChain(coin, configfile, pushHandler, metrics); err != nil { + if chain, mempool, err = coins.NewBlockChain(coin, configFile, pushHandler, metrics); err != nil { if i < seconds { glog.Error("rpc: ", err, " Retrying...") select { @@ -496,46 +492,11 @@ func newInternalState(coin, coinShortcut, coinLabel string, d *db.RocksDB) (*com return is, nil } -func tickAndDebounce(tickTime time.Duration, debounceTime time.Duration, input chan struct{}, f func()) { - timer := time.NewTimer(tickTime) - var firstDebounce time.Time -Loop: - for { - select { - case _, ok := <-input: - if !timer.Stop() { - <-timer.C - } - // exit loop on closed input channel - if !ok { - break Loop - } - if firstDebounce.IsZero() { - firstDebounce = time.Now() - } - // debounce for up to debounceTime period - // afterwards execute immediately - if firstDebounce.Add(debounceTime).After(time.Now()) { - timer.Reset(debounceTime) - } else { - timer.Reset(0) - } - case <-timer.C: - // do the action, if not in shutdown, then start the loop again - if atomic.LoadInt32(&inShutdown) == 0 { - f() - } - timer.Reset(tickTime) - firstDebounce = time.Time{} - } - } -} - func syncIndexLoop() { defer close(chanSyncIndexDone) glog.Info("syncIndexLoop starting") // resync index about every 15 minutes if there are no chanSyncIndex requests, with debounce 1 second - tickAndDebounce(time.Duration(*resyncIndexPeriodMs)*time.Millisecond, debounceResyncIndexMs*time.Millisecond, chanSyncIndex, func() { + common.TickAndDebounce(time.Duration(*resyncIndexPeriodMs)*time.Millisecond, debounceResyncIndexMs*time.Millisecond, chanSyncIndex, func() { if err := syncWorker.ResyncIndex(onNewBlockHash, false); err != nil { glog.Error("syncIndexLoop ", errors.ErrorStack(err), ", will retry...") // retry once in case of random network error, after a slight delay @@ -574,7 +535,7 @@ func syncMempoolLoop() { defer close(chanSyncMempoolDone) glog.Info("syncMempoolLoop starting") // resync mempool about every minute if there are no chanSyncMempool requests, with debounce 1 second - tickAndDebounce(time.Duration(*resyncMempoolPeriodMs)*time.Millisecond, debounceResyncMempoolMs*time.Millisecond, chanSyncMempool, func() { + common.TickAndDebounce(time.Duration(*resyncMempoolPeriodMs)*time.Millisecond, debounceResyncMempoolMs*time.Millisecond, chanSyncMempool, func() { internalState.StartedMempoolSync() if count, err := mempool.Resync(); err != nil { glog.Error("syncMempoolLoop ", errors.ErrorStack(err)) @@ -604,7 +565,7 @@ func storeInternalStateLoop() { } else { glog.Info("storeInternalStateLoop starting with db stats compute disabled") } - tickAndDebounce(storeInternalStatePeriodMs*time.Millisecond, (storeInternalStatePeriodMs-1)*time.Millisecond, chanStoreInternalState, func() { + common.TickAndDebounce(storeInternalStatePeriodMs*time.Millisecond, (storeInternalStatePeriodMs-1)*time.Millisecond, chanStoreInternalState, func() { if (*dbStatsPeriodHours) > 0 && !computeRunning && lastCompute.Add(computePeriod).Before(time.Now()) { computeRunning = true go func() { @@ -654,7 +615,7 @@ func onNewTx(tx *bchain.MempoolTx) { func pushSynchronizationHandler(nt bchain.NotificationType) { glog.V(1).Info("MQ: notification ", nt) - if atomic.LoadInt32(&inShutdown) != 0 { + if common.IsInShutdown() { return } if nt == bchain.NotificationNewBlock { @@ -668,7 +629,7 @@ func pushSynchronizationHandler(nt bchain.NotificationType) { func waitForSignalAndShutdown(internal *server.InternalServer, public *server.PublicServer, chain bchain.BlockChain, timeout time.Duration) { sig := <-chanOsSignal - atomic.StoreInt32(&inShutdown, 1) + common.SetInShutdown() glog.Infof("shutdown: %v", sig) ctx, cancel := context.WithTimeout(context.Background(), timeout) @@ -693,17 +654,6 @@ func waitForSignalAndShutdown(internal *server.InternalServer, public *server.Pu } } -func printResult(txid string, vout int32, isOutput bool) error { - glog.Info(txid, vout, isOutput) - return nil -} - -func normalizeName(s string) string { - s = strings.ToLower(s) - s = strings.Replace(s, " ", "-", -1) - return s -} - // computeFeeStats computes fee distribution in defined blocks func computeFeeStats(stopCompute chan os.Signal, blockFrom, blockTo int, db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is *common.InternalState, metrics *common.Metrics) error { start := time.Now() diff --git a/common/internalstate.go b/common/internalstate.go index bf8a46b5a3..3829094d5e 100644 --- a/common/internalstate.go +++ b/common/internalstate.go @@ -4,6 +4,7 @@ import ( "encoding/json" "sort" "sync" + "sync/atomic" "time" ) @@ -16,6 +17,8 @@ const ( DbStateInconsistent ) +var inShutdown int32 + // InternalStateColumn contains the data of a db column type InternalStateColumn struct { Name string `json:"name"` @@ -265,3 +268,13 @@ func UnpackInternalState(buf []byte) (*InternalState, error) { } return &is, nil } + +// SetInShutdown sets the internal state to in shutdown state +func SetInShutdown() { + atomic.StoreInt32(&inShutdown, 1) +} + +// IsInShutdown returns true if in application shutdown state +func IsInShutdown() bool { + return atomic.LoadInt32(&inShutdown) != 0 +} diff --git a/common/utils.go b/common/utils.go new file mode 100644 index 0000000000..bfe8980bf0 --- /dev/null +++ b/common/utils.go @@ -0,0 +1,41 @@ +package common + +import ( + "time" +) + +// TickAndDebounce calls function f on trigger channel or with tickTime period (whatever is sooner) with debounce +func TickAndDebounce(tickTime time.Duration, debounceTime time.Duration, trigger chan struct{}, f func()) { + timer := time.NewTimer(tickTime) + var firstDebounce time.Time +Loop: + for { + select { + case _, ok := <-trigger: + if !timer.Stop() { + <-timer.C + } + // exit loop on closed input channel + if !ok { + break Loop + } + if firstDebounce.IsZero() { + firstDebounce = time.Now() + } + // debounce for up to debounceTime period + // afterwards execute immediately + if firstDebounce.Add(debounceTime).After(time.Now()) { + timer.Reset(debounceTime) + } else { + timer.Reset(0) + } + case <-timer.C: + // do the action, if not in shutdown, then start the loop again + if !IsInShutdown() { + f() + } + timer.Reset(tickTime) + firstDebounce = time.Time{} + } + } +} diff --git a/db/fiat.go b/db/fiat.go new file mode 100644 index 0000000000..c2fa7325b3 --- /dev/null +++ b/db/fiat.go @@ -0,0 +1,135 @@ +package db + +import ( + "encoding/json" + "time" + + "github.com/golang/glog" + "github.com/juju/errors" +) + +// 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]float64 +} + +// ResultTickerAsString contains formatted CurrencyRatesTicker data +type ResultTickerAsString struct { + Timestamp int64 `json:"ts,omitempty"` + Rates map[string]float64 `json:"rates"` + 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 int64 `json:"ts,omitempty"` + Tickers []string `json:"available_currencies"` + Error string `json:"error,omitempty"` +} + +// 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 +} diff --git a/db/fiat_test.go b/db/fiat_test.go new file mode 100644 index 0000000000..95e83eab74 --- /dev/null +++ b/db/fiat_test.go @@ -0,0 +1,84 @@ +//go:build unittest + +package db + +import ( + "testing" + "time" +) + +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]float64{ + "usd": 20000, + }, + } + + ts2, _ := time.Parse(FiatRatesTimeFormat, "20190629000000") + ticker2 := &CurrencyRatesTicker{ + Timestamp: &ts2, + Rates: map[string]float64{ + "usd": 30000, + }, + } + err := d.FiatRatesStoreTicker(ticker1) + if err != nil { + t.Errorf("Error storing ticker! %v", err) + } + d.FiatRatesStoreTicker(ticker2) + if err != nil { + t.Errorf("Error storing ticker! %v", err) + } + + 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/db/rocksdb.go b/db/rocksdb.go index 4ef396d2f4..c2ef7cc7ad 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/binary" "encoding/hex" - "encoding/json" "fmt" "math/big" "os" @@ -31,34 +30,6 @@ 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]float64 -} - -// ResultTickerAsString contains formatted CurrencyRatesTicker data -type ResultTickerAsString struct { - Timestamp int64 `json:"ts,omitempty"` - Rates map[string]float64 `json:"rates"` - 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 int64 `json:"ts,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") @@ -183,104 +154,6 @@ 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 22912104e1..0f72c5a658 100644 --- a/db/rocksdb_test.go +++ b/db/rocksdb_test.go @@ -12,7 +12,6 @@ import ( "sort" "strings" "testing" - "time" vlq "github.com/bsm/go-vlq" "github.com/juju/errors" @@ -1482,79 +1481,3 @@ func Test_reorderUtxo(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]float64{ - "usd": 20000, - }, - } - - ts2, _ := time.Parse(FiatRatesTimeFormat, "20190629000000") - ticker2 := &CurrencyRatesTicker{ - Timestamp: &ts2, - Rates: map[string]float64{ - "usd": 30000, - }, - } - err := d.FiatRatesStoreTicker(ticker1) - if err != nil { - t.Errorf("Error storing ticker! %v", err) - } - d.FiatRatesStoreTicker(ticker2) - if err != nil { - t.Errorf("Error storing ticker! %v", err) - } - - 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.") - } -} From f57bd2e6c35b5bd1f3be3dc1f2e89ffb7de2a2a9 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 20 Feb 2022 17:59:06 +0100 Subject: [PATCH 051/530] Download ETH 4byte signatures --- bchain/coins/eth/dataparser.go | 14 ++ blockbook.go | 30 ++- configs/coins/ethereum_archive.json | 3 +- .../ethereum_testnet_ropsten_archive.json | 5 +- db/bulkconnect.go | 15 +- db/rocksdb.go | 31 ++- db/rocksdb_ethereumtype.go | 54 ++++- db/rocksdb_ethereumtype_test.go | 64 ++++++ db/rocksdb_test.go | 18 ++ fiat/fiat_rates.go | 2 +- fourbyte/fourbyte.go | 199 ++++++++++++++++++ fourbyte/fourbyte_test.go | 55 +++++ 12 files changed, 466 insertions(+), 24 deletions(-) create mode 100644 fourbyte/fourbyte.go create mode 100644 fourbyte/fourbyte_test.go diff --git a/bchain/coins/eth/dataparser.go b/bchain/coins/eth/dataparser.go index 399ef2d1ae..55e632c347 100644 --- a/bchain/coins/eth/dataparser.go +++ b/bchain/coins/eth/dataparser.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "math/big" + "unicode" "unicode/utf8" ) @@ -56,3 +57,16 @@ func parseSimpleStringProperty(data string) string { } return "" } + +func Decamel(s string) string { + var b bytes.Buffer + splittable := false + for _, v := range s { + if splittable && unicode.IsUpper(v) { + b.WriteByte(' ') + } + b.WriteRune(v) + splittable = unicode.IsLower(v) || unicode.IsNumber(v) + } + return b.String() +} diff --git a/blockbook.go b/blockbook.go index 4f8663bc39..85fab7771b 100644 --- a/blockbook.go +++ b/blockbook.go @@ -24,6 +24,7 @@ import ( "github.com/trezor/blockbook/common" "github.com/trezor/blockbook/db" "github.com/trezor/blockbook/fiat" + "github.com/trezor/blockbook/fourbyte" "github.com/trezor/blockbook/server" ) @@ -343,7 +344,7 @@ func mainWithExitCode() int { if internalServer != nil || publicServer != nil || chain != nil { // start fiat rates downloader only if not shutting down immediately - initFiatRatesDownloader(index, *configFile) + initDownloaders(index, chain, *configFile) waitForSignalAndShutdown(internalServer, publicServer, chain, 10*time.Second) } @@ -667,7 +668,7 @@ func computeFeeStats(stopCompute chan os.Signal, blockFrom, blockTo int, db *db. return err } -func initFiatRatesDownloader(db *db.RocksDB, configfile string) { +func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, configfile string) { data, err := ioutil.ReadFile(configfile) if err != nil { glog.Errorf("Error reading file %v, %v", configfile, err) @@ -675,8 +676,9 @@ func initFiatRatesDownloader(db *db.RocksDB, configfile string) { } var config struct { - FiatRates string `json:"fiat_rates"` - FiatRatesParams string `json:"fiat_rates_params"` + FiatRates string `json:"fiat_rates"` + FiatRatesParams string `json:"fiat_rates_params"` + FourByteSignatures string `json:"fourByteSignatures"` } err = json.Unmarshal(data, &config) @@ -686,14 +688,26 @@ func initFiatRatesDownloader(db *db.RocksDB, configfile string) { } if config.FiatRates == "" || config.FiatRatesParams == "" { - glog.Infof("FiatRates config (%v) is empty, so the functionality is disabled.", configfile) + glog.Infof("FiatRates config (%v) is empty, not downloading fiat rates.", configfile) } else { fiatRates, err := fiat.NewFiatRatesDownloader(db, config.FiatRates, config.FiatRatesParams, nil, onNewFiatRatesTicker) if err != nil { glog.Errorf("NewFiatRatesDownloader Init error: %v", err) - return + } else { + glog.Infof("Starting %v FiatRates downloader...", config.FiatRates) + go fiatRates.Run() + } + } + + if config.FourByteSignatures != "" && chain.GetChainParser().GetChainType() == bchain.ChainEthereumType { + fbsd, err := fourbyte.NewFourByteSignaturesDownloader(db, config.FourByteSignatures) + if err != nil { + glog.Errorf("NewFourByteSignaturesDownloader Init error: %v", err) + } else { + glog.Infof("Starting FourByteSignatures downloader...") + go fbsd.Run() } - glog.Infof("Starting %v FiatRates downloader...", config.FiatRates) - go fiatRates.Run() + } + } diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index be4e4815e6..16e320e75b 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -54,7 +54,8 @@ "processInternalTransactions": true, "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\", \"periodSeconds\": 60}", + "4byteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } }, diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index d201f68821..0e19291409 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -51,7 +51,8 @@ "additional_params": { "mempoolTxTimeoutHours": 12, "processInternalTransactions": true, - "queryBackendOnMempoolResync": false + "queryBackendOnMempoolResync": false, + "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } }, @@ -59,4 +60,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/db/bulkconnect.go b/db/bulkconnect.go index 39adee3490..0e1183a17a 100644 --- a/db/bulkconnect.go +++ b/db/bulkconnect.go @@ -108,7 +108,7 @@ func (b *BulkConnect) parallelStoreTxAddresses(c chan error, all bool) { c <- err return } - if err := b.d.db.Write(b.d.wo, wb); err != nil { + if err := b.d.WriteBatch(wb); err != nil { c <- err return } @@ -148,7 +148,7 @@ func (b *BulkConnect) parallelStoreBalances(c chan error, all bool) { c <- err return } - if err := b.d.db.Write(b.d.wo, wb); err != nil { + if err := b.d.WriteBatch(wb); err != nil { c <- err return } @@ -215,7 +215,7 @@ func (b *BulkConnect) connectBlockBitcoinType(block *bchain.Block, storeBlockTxs return err } } - if err := b.d.db.Write(b.d.wo, wb); err != nil { + if err := b.d.WriteBatch(wb); err != nil { return err } if bac > b.bulkAddressesCount { @@ -267,7 +267,7 @@ func (b *BulkConnect) parallelStoreAddressContracts(c chan error, all bool) { c <- err return } - if err := b.d.db.Write(b.d.wo, wb); err != nil { + if err := b.d.WriteBatch(wb); err != nil { c <- err return } @@ -323,7 +323,7 @@ func (b *BulkConnect) connectBlockEthereumType(block *bchain.Block, storeBlockTx return err } } - if err = b.d.db.Write(b.d.wo, wb); err != nil { + if err = b.d.WriteBatch(wb); err != nil { return err } if bac > b.bulkAddressesCount { @@ -338,6 +338,9 @@ func (b *BulkConnect) connectBlockEthereumType(block *bchain.Block, storeBlockTx if err = b.d.storeBlockSpecificDataEthereumType(wb, block); err != nil { return err } + if err := b.d.WriteBatch(wb); err != nil { + return err + } } } if storeAddrContracts != nil { @@ -381,7 +384,7 @@ func (b *BulkConnect) Close() error { if err := b.storeBulkAddresses(wb); err != nil { return err } - if err := b.d.db.Write(b.d.wo, wb); err != nil { + if err := b.d.WriteBatch(wb); err != nil { return err } glog.Info("rocksdb: height ", b.height, ", stored ", bac, " addresses, done in ", time.Since(start)) diff --git a/db/rocksdb.go b/db/rocksdb.go index c2ef7cc7ad..591570132e 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -196,6 +196,10 @@ func atoUint64(s string) uint64 { return uint64(i) } +func (d *RocksDB) WriteBatch(wb *gorocksdb.WriteBatch) error { + return d.db.Write(d.wo, wb) +} + // GetMemoryStats returns memory usage statistics as reported by RocksDB func (d *RocksDB) GetMemoryStats() string { var total, indexAndFilter, memtable uint64 @@ -369,7 +373,7 @@ func (d *RocksDB) ConnectBlock(block *bchain.Block) error { if err := d.storeAddresses(wb, block.Height, addresses); err != nil { return err } - if err := d.db.Write(d.wo, wb); err != nil { + if err := d.WriteBatch(wb); err != nil { return err } d.is.AppendBlockTime(uint32(block.Time)) @@ -1418,7 +1422,7 @@ func (d *RocksDB) disconnectBlock(height uint32, blockTxs []blockTxs) error { wb.DeleteCF(d.cfh[cfTransactions], b) wb.DeleteCF(d.cfh[cfTxAddresses], b) } - return d.db.Write(d.wo, wb) + return d.WriteBatch(wb) } // DisconnectBlockRangeBitcoinType removes all data belonging to blocks in range lower-higher @@ -1535,7 +1539,7 @@ func (d *RocksDB) DeleteTx(txid string) error { wb := gorocksdb.NewWriteBatch() defer wb.Destroy() d.internalDeleteTx(wb, key) - return d.db.Write(d.wo, wb) + return d.WriteBatch(wb) } // internalDeleteTx checks if tx is cached and updates internal state accordingly @@ -1832,7 +1836,7 @@ func (d *RocksDB) fixUtxo(addrDesc bchain.AddressDescriptor, ba *AddrBalance) (b wb := gorocksdb.NewWriteBatch() err = d.storeBalances(wb, map[string]*AddrBalance{string(addrDesc): ba}) if err == nil { - err = d.db.Write(d.wo, wb) + err = d.WriteBatch(wb) } wb.Destroy() if err != nil { @@ -1845,7 +1849,7 @@ func (d *RocksDB) fixUtxo(addrDesc bchain.AddressDescriptor, ba *AddrBalance) (b wb := gorocksdb.NewWriteBatch() err := d.storeBalances(wb, map[string]*AddrBalance{string(addrDesc): ba}) if err == nil { - err = d.db.Write(d.wo, wb) + err = d.WriteBatch(wb) } wb.Destroy() if err != nil { @@ -1977,6 +1981,23 @@ func unpackVaruint(buf []byte) (uint, int) { return uint(i), ofs } +func packString(s string) []byte { + varBuf := make([]byte, vlq.MaxLen64) + l := len(s) + i := packVaruint(uint(l), varBuf) + buf := make([]byte, 0, i+l) + buf = append(buf, varBuf[:i]...) + buf = append(buf, s...) + return buf +} + +func unpackString(buf []byte) (string, int) { + sl, l := unpackVaruint(buf) + so := l + int(sl) + s := string(buf[l:so]) + return s, so +} + const ( // number of bits in a big.Word wordBits = 32 << (uint64(^big.Word(0)) >> 63) diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index 2e2eb4da71..5bbc1c10f4 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -578,6 +578,58 @@ func (d *RocksDB) unpackEthInternalData(buf []byte) (*bchain.EthereumInternalDat return &id, nil } +type FourByteSignature struct { + Name string + Parameters []string +} + +func packFourByteKey(fourBytes uint32, id uint32) []byte { + key := make([]byte, 0, 8) + key = append(key, packUint(fourBytes)...) + key = append(key, packUint(id)...) + return key +} + +func packFourByteSignature(signature *FourByteSignature) []byte { + buf := packString(signature.Name) + for i := range signature.Parameters { + buf = append(buf, packString(signature.Parameters[i])...) + } + return buf +} + +func unpackFourByteSignature(buf []byte) (*FourByteSignature, error) { + var signature FourByteSignature + var l int + signature.Name, l = unpackString(buf) + for l < len(buf) { + s, ll := unpackString(buf[l:]) + signature.Parameters = append(signature.Parameters, s) + l += ll + } + return &signature, nil +} + +func (d *RocksDB) GetFourByteSignature(fourBytes uint32, id uint32) (*FourByteSignature, error) { + key := packFourByteKey(fourBytes, id) + val, err := d.db.GetCF(d.ro, d.cfh[cfFunctionSignatures], key) + if err != nil { + return nil, err + } + defer val.Free() + buf := val.Data() + if len(buf) == 0 { + return nil, nil + } + return unpackFourByteSignature(buf) +} + +func (d *RocksDB) StoreFourByteSignature(wb *gorocksdb.WriteBatch, fourBytes uint32, id uint32, signature *FourByteSignature) error { + key := packFourByteKey(fourBytes, id) + wb.PutCF(d.cfh[cfFunctionSignatures], key, packFourByteSignature(signature)) + return nil +} + func (d *RocksDB) GetEthereumInternalData(txid string) (*bchain.EthereumInternalData, error) { btxID, err := d.chainParser.PackTxid(txid) if err != nil { @@ -968,7 +1020,7 @@ func (d *RocksDB) DisconnectBlockRangeEthereumType(lower uint32, higher uint32) wb.DeleteCF(d.cfh[cfBlockInternalDataErrors], key) } d.storeAddressContracts(wb, contracts) - err := d.db.Write(d.wo, wb) + err := d.WriteBatch(wb) if err == nil { d.is.RemoveLastBlockTimes(int(higher-lower) + 1) glog.Infof("rocksdb: blocks %d-%d disconnected", lower, higher) diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index 6f9ed711b7..9d257c70e4 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -8,6 +8,7 @@ import ( "reflect" "testing" + "github.com/flier/gorocksdb" "github.com/juju/errors" "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain/coins/eth" @@ -297,6 +298,30 @@ func formatInternalData(in *bchain.EthereumInternalData) *bchain.EthereumInterna return &out } +func testFourByteSignature(t *testing.T, d *RocksDB) { + fourBytes := uint32(1234123) + id := uint32(42313) + signature := FourByteSignature{ + Name: "xyz", + Parameters: []string{"address", "(bytes,uint256[],uint256)", "uint16"}, + } + wb := gorocksdb.NewWriteBatch() + defer wb.Destroy() + if err := d.StoreFourByteSignature(wb, fourBytes, id, &signature); err != nil { + t.Fatal(err) + } + if err := d.WriteBatch(wb); err != nil { + t.Fatal(err) + } + got, err := d.GetFourByteSignature(fourBytes, id) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(*got, signature) { + t.Errorf("testFourByteSignature: got %+v, want %+v", got, signature) + } +} + // TestRocksDB_Index_EthereumType is an integration test probing the whole indexing functionality for EthereumType chains // It does the following: // 1) Connect two blocks (inputs from 2nd block are spending some outputs from the 1st block) @@ -417,6 +442,9 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { t.Errorf("GetBlockInfo() = %+v, want %+v", info, iw) } + // Test to store and get FourByteSignature + testFourByteSignature(t, d) + // Test tx caching functionality, leave one tx in db to test cleanup in DisconnectBlock testTxCache(t, d, block1, &block1.Txs[0]) // InternalData are not packed and stored in DB, remove them so that the test does not fail @@ -1133,3 +1161,39 @@ func Test_packUnpackBlockTx(t *testing.T) { }) } } + +func Test_packUnpackFourByteSignature(t *testing.T) { + tests := []struct { + name string + signature FourByteSignature + }{ + { + name: "no params", + signature: FourByteSignature{ + Name: "abcdef", + }, + }, + { + name: "one param", + signature: FourByteSignature{ + Name: "opqr", + Parameters: []string{"uint16"}, + }, + }, + { + name: "multiple params", + signature: FourByteSignature{ + Name: "xyz", + Parameters: []string{"address", "(bytes,uint256[],uint256)", "uint16"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := packFourByteSignature(&tt.signature) + if got, err := unpackFourByteSignature(buf); !reflect.DeepEqual(*got, tt.signature) || err != nil { + t.Errorf("packUnpackFourByteSignature() = %v, want %v, error %v", *got, tt.signature, err) + } + }) + } +} diff --git a/db/rocksdb_test.go b/db/rocksdb_test.go index 0f72c5a658..8a287403fd 100644 --- a/db/rocksdb_test.go +++ b/db/rocksdb_test.go @@ -1481,3 +1481,21 @@ func Test_reorderUtxo(t *testing.T) { }) } } + +func Test_packUnpackString(t *testing.T) { + tests := []struct { + name string + }{ + {name: "ahoj"}, + {name: ""}, + {name: "very long long very long long very long long very long long very long long very long long very long long very long long very long long very long long very long long very long long very long long"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := packString(tt.name) + if got, l := unpackString(buf); !reflect.DeepEqual(got, tt.name) || l != len(buf) { + t.Errorf("Test_packUnpackString() = %v, want %v, len %d, want len %d", got, tt.name, l, len(buf)) + } + }) + } +} diff --git a/fiat/fiat_rates.go b/fiat/fiat_rates.go index dcc0c85ac5..5f6df96d88 100644 --- a/fiat/fiat_rates.go +++ b/fiat/fiat_rates.go @@ -30,7 +30,7 @@ type RatesDownloader struct { downloader RatesDownloaderInterface } -// NewFiatRatesDownloader initiallizes the downloader for FiatRates API. +// NewFiatRatesDownloader initializes 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{} diff --git a/fourbyte/fourbyte.go b/fourbyte/fourbyte.go new file mode 100644 index 0000000000..76b56b3c35 --- /dev/null +++ b/fourbyte/fourbyte.go @@ -0,0 +1,199 @@ +package fourbyte + +import ( + "encoding/json" + "errors" + "io/ioutil" + "net/http" + "strconv" + "strings" + "time" + + "github.com/flier/gorocksdb" + "github.com/golang/glog" + "github.com/trezor/blockbook/db" +) + +// Coingecko is a structure that implements RatesDownloaderInterface +type FourByteSignaturesDownloader struct { + url string + httpTimeoutSeconds time.Duration + db *db.RocksDB +} + +// NewFourByteSignaturesDownloader initializes the downloader for FourByteSignatures API. +func NewFourByteSignaturesDownloader(db *db.RocksDB, url string) (*FourByteSignaturesDownloader, error) { + return &FourByteSignaturesDownloader{ + url: url, + httpTimeoutSeconds: 15 * time.Second, + db: db, + }, nil +} + +// Run starts the FourByteSignatures downloader +func (fd *FourByteSignaturesDownloader) Run() { + period := time.Hour * 24 + timer := time.NewTimer(period) + for { + fd.downloadSignatures() + <-timer.C + timer.Reset(period) + } +} + +type signatureData struct { + Id int `json:"id"` + TextSignature string `json:"text_signature"` + HexSignature string `json:"hex_signature"` +} + +type signaturesPage struct { + Count int `json:"count"` + Next string `json:"next"` + Results []signatureData `json:"results"` +} + +func (fd *FourByteSignaturesDownloader) getPage(url string) (*signaturesPage, error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + glog.Errorf("Error creating a new request for %v: %v", url, err) + return nil, err + } + req.Close = true + req.Header.Set("Content-Type", "application/json") + client := &http.Client{ + Timeout: fd.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 + } + var data signaturesPage + err = json.Unmarshal(bodyBytes, &data) + if err != nil { + glog.Errorf("Error parsing 4byte signatures response from %s: %v", url, err) + return nil, err + } + return &data, nil +} + +func (fd *FourByteSignaturesDownloader) getPageWithRetry(url string) (*signaturesPage, error) { + for retry := 1; retry <= 16; retry++ { + page, err := fd.getPage(url) + if err == nil && page != nil { + return page, err + } + glog.Errorf("Error getting 4byte signatures from %s: %v, retry count %d", url, err, retry) + timer := time.NewTimer(time.Second * time.Duration(retry)) + <-timer.C + } + return nil, errors.New("Too many retries to 4byte signatures") +} + +func parseSignatureFromText(t string) *db.FourByteSignature { + s := strings.Index(t, "(") + e := strings.LastIndex(t, ")") + if s < 0 || e < 0 { + return nil + } + var signature db.FourByteSignature + signature.Name = t[:s] + params := t[s+1 : e] + if len(params) > 0 { + s = 0 + tupleDepth := 0 + // parse params as comma separated list + // tuple is regarded as one parameter and not parsed further + for i, c := range params { + if c == ',' && tupleDepth == 0 { + signature.Parameters = append(signature.Parameters, params[s:i]) + s = i + 1 + } else if c == '(' { + tupleDepth++ + } else if c == ')' { + tupleDepth-- + } + } + signature.Parameters = append(signature.Parameters, params[s:]) + } + return &signature +} + +func (fd *FourByteSignaturesDownloader) downloadSignatures() { + period := time.Millisecond * 100 + timer := time.NewTimer(period) + url := fd.url + results := make([]signatureData, 0) + glog.Info("FourByteSignaturesDownloader starting download") + for { + page, err := fd.getPageWithRetry(url) + if err != nil { + glog.Errorf("Error getting 4byte signatures from %s: %v", url, err) + return + } + if page == nil { + glog.Errorf("Empty page from 4byte signatures from %s: %v", url, err) + return + } + glog.Infof("FourByteSignaturesDownloader downloaded %s with %d results", url, len(page.Results)) + if len(page.Results) > 0 { + fourBytes, err := strconv.ParseUint(page.Results[0].HexSignature, 0, 0) + if err != nil { + glog.Errorf("Invalid 4byte signature %+v on page %s: %v", page.Results[0], url, err) + return + } + sig, err := fd.db.GetFourByteSignature(uint32(fourBytes), uint32(page.Results[0].Id)) + if err != nil { + glog.Errorf("db.GetFourByteSignature error %+v on page %s: %v", page.Results[0], url, err) + return + } + // signature is already stored in db, break + if sig != nil { + break + } + results = append(results, page.Results...) + } + if page.Next == "" { + // at the end + break + } + url = page.Next + // wait a bit to not to flood the server + <-timer.C + timer.Reset(period) + } + if len(results) > 0 { + glog.Infof("FourByteSignaturesDownloader storing %d new signatures", len(results)) + wb := gorocksdb.NewWriteBatch() + defer wb.Destroy() + + for i := range results { + r := &results[i] + fourBytes, err := strconv.ParseUint(r.HexSignature, 0, 0) + if err != nil { + glog.Errorf("Invalid 4byte signature %+v: %v", r, err) + return + } + fbs := parseSignatureFromText(r.TextSignature) + if fbs != nil { + fd.db.StoreFourByteSignature(wb, uint32(fourBytes), uint32(r.Id), fbs) + } else { + glog.Errorf("FourByteSignaturesDownloader invalid signature %s", r.TextSignature) + } + } + + if err := fd.db.WriteBatch(wb); err != nil { + glog.Errorf("FourByteSignaturesDownloader failed to store signatures, %v", err) + } + + } + glog.Infof("FourByteSignaturesDownloader finished") +} diff --git a/fourbyte/fourbyte_test.go b/fourbyte/fourbyte_test.go new file mode 100644 index 0000000000..95c7b3af70 --- /dev/null +++ b/fourbyte/fourbyte_test.go @@ -0,0 +1,55 @@ +package fourbyte + +import ( + "reflect" + "testing" + + "github.com/trezor/blockbook/db" +) + +func Test_parseSignatureFromText(t *testing.T) { + tests := []struct { + name string + signature string + want db.FourByteSignature + }{ + { + name: "_gonsPerFragment", + signature: "_gonsPerFragment()", + want: db.FourByteSignature{ + Name: "_gonsPerFragment", + }, + }, + { + name: "vestingDeposits", + signature: "vestingDeposits(address)", + want: db.FourByteSignature{ + Name: "vestingDeposits", + Parameters: []string{"address"}, + }, + }, + { + name: "batchTransferTokenB", + signature: "batchTransferTokenB(address[],uint256)", + want: db.FourByteSignature{ + Name: "batchTransferTokenB", + Parameters: []string{"address[]", "uint256"}, + }, + }, + { + name: "transmitAndSellTokenForEth", + signature: "transmitAndSellTokenForEth(address,uint256,uint256,uint256,address,(uint8,bytes32,bytes32),bytes)", + want: db.FourByteSignature{ + Name: "transmitAndSellTokenForEth", + Parameters: []string{"address", "uint256", "uint256", "uint256", "address", "(uint8,bytes32,bytes32)", "bytes"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := parseSignatureFromText(tt.signature); !reflect.DeepEqual(*got, tt.want) { + t.Errorf("parseSignatureFromText() = %v, want %v", *got, tt.want) + } + }) + } +} From 74ef087d4b9596a562865ff7cc414f8c996f7c27 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 26 Mar 2022 19:16:35 +0100 Subject: [PATCH 052/530] Return token balances from API --- api/types.go | 32 ++++---- api/worker.go | 92 +++++++++++++++++----- server/public_ethereumtype_test.go | 2 +- tests/dbtestdata/fakechain_ethereumtype.go | 5 ++ 4 files changed, 94 insertions(+), 37 deletions(-) diff --git a/api/types.go b/api/types.go index 6a0bd165bd..dc6644d009 100644 --- a/api/types.go +++ b/api/types.go @@ -153,27 +153,29 @@ const ( // the map must match all bchain.TokenTransferTypes to avoid index out of range panic var TokenTypeMap []TokenType = []TokenType{ERC20TokenType, ERC771TokenType, ERC1155TokenType} -// Token contains info about tokens held by an address -type Token struct { - Type TokenType `json:"type"` - Name string `json:"name"` - Path string `json:"path,omitempty"` - Contract string `json:"contract,omitempty"` - Transfers int `json:"transfers"` - Symbol string `json:"symbol,omitempty"` - Decimals int `json:"decimals,omitempty"` - BalanceSat *Amount `json:"balance,omitempty"` - TotalReceivedSat *Amount `json:"totalReceived,omitempty"` - TotalSentSat *Amount `json:"totalSent,omitempty"` - ContractIndex string `json:"-"` -} - // TokenTransferValues contains values for ERC1155 contract type TokenTransferValues struct { Id *Amount `json:"id,omitempty"` Value *Amount `json:"value,omitempty"` } +// Token contains info about tokens held by an address +type Token struct { + Type TokenType `json:"type"` + Name string `json:"name"` + Path string `json:"path,omitempty"` + Contract string `json:"contract,omitempty"` + Transfers int `json:"transfers"` + Symbol string `json:"symbol,omitempty"` + Decimals int `json:"decimals,omitempty"` + BalanceSat *Amount `json:"balance,omitempty"` + Ids []Amount `json:"ids,omitempty"` // multiple ERC721 tokens + IdValues []TokenTransferValues `json:"idValues,omitempty"` // multiple ERC1155 tokens + TotalReceivedSat *Amount `json:"totalReceived,omitempty"` + TotalSentSat *Amount `json:"totalSent,omitempty"` + ContractIndex string `json:"-"` +} + // TokenTransfer contains info about a token transfer done in a transaction type TokenTransfer struct { Type TokenType `json:"type"` diff --git a/api/worker.go b/api/worker.go index ac9cdb9069..c5f2e054b0 100644 --- a/api/worker.go +++ b/api/worker.go @@ -655,7 +655,68 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) { }, from, to, page } -func (w *Worker) getEthereumToken(index int, addrDesc, contract bchain.AddressDescriptor, details AccountDetails, txs int) (*Token, error) { +func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails) (*Token, error) { + // TODO use db.contracts + validContract := true + + ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract) + if err != nil { + return nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract) + } + if ci == nil { + ci = &bchain.Erc20Contract{} + addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(c.Contract) + if len(addresses) > 0 { + ci.Contract = addresses[0] + ci.Name = addresses[0] + } + validContract = false + } + + t := Token{ + Type: ERC20TokenType, + Contract: ci.Contract, + Name: ci.Name, + Symbol: ci.Symbol, + Transfers: int(c.Txs), + Decimals: ci.Decimals, + ContractIndex: strconv.Itoa(index), + } + // return contract balances/values only at or above AccountDetailsTokenBalances + if details >= AccountDetailsTokenBalances && validContract { + if c.Type == bchain.ERC20 { + // get Erc20 Contract Balance from blockchain, balance obtained from adding and subtracting transfers is not correct + b, err := w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, c.Contract) + if err != nil { + // return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract) + glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, c.Contract, err) + } else { + t.BalanceSat = (*Amount)(b) + } + } else { + if len(t.Ids) > 0 { + ids := make([]Amount, len(t.Ids)) + for j := range ids { + ids[j] = (Amount)(c.Ids[j]) + } + t.Ids = ids + } + if len(t.IdValues) > 0 { + idValues := make([]TokenTransferValues, len(t.IdValues)) + for j := range idValues { + idValues[j].Id = (*Amount)(&c.IdValues[j].Id) + idValues[j].Value = (*Amount)(&c.IdValues[j].Value) + } + t.IdValues = idValues + } + } + } + + return &t, nil +} + +// a fallback method in case internal transactions are not processed and there is no indexed info about contract balance for an address +func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bchain.AddressDescriptor, details AccountDetails) (*Token, error) { var b *big.Int validContract := true ci, err := w.chain.EthereumTypeGetErc20ContractInfo(contract) @@ -687,9 +748,9 @@ func (w *Worker) getEthereumToken(index int, addrDesc, contract bchain.AddressDe Contract: ci.Contract, Name: ci.Name, Symbol: ci.Symbol, - Transfers: txs, + Transfers: 0, Decimals: ci.Decimals, - ContractIndex: strconv.Itoa(index), + ContractIndex: "0", }, nil } @@ -733,7 +794,8 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto if details > AccountDetailsBasic { tokens = make([]Token, len(ca.Contracts)) var j int - for i, c := range ca.Contracts { + for i := range ca.Contracts { + c := &ca.Contracts[i] if len(filterDesc) > 0 { if !bytes.Equal(filterDesc, c.Contract) { continue @@ -741,26 +803,14 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto // filter only transactions of this contract filter.Vout = i + db.ContractIndexOffset } - t, err := w.getEthereumToken(i+db.ContractIndexOffset, addrDesc, c.Contract, details, int(c.Txs)) + t, err := w.getEthereumContractBalance(addrDesc, i+db.ContractIndexOffset, c, details) if err != nil { return nil, nil, nil, 0, 0, 0, 0, err } tokens[j] = *t j++ } - // special handling if filter has contract - // if the address has no transactions with given contract, check the balance, the address may have some balance even without transactions - if len(filterDesc) > 0 && j == 0 && details >= AccountDetailsTokens { - t, err := w.getEthereumToken(0, addrDesc, filterDesc, details, 0) - if err != nil { - return nil, nil, nil, 0, 0, 0, 0, err - } - tokens = []Token{*t} - // switch off query for transactions, there are no transactions - filter.Vout = AddressFilterVoutQueryNotNecessary - } else { - tokens = tokens[:j] - } + tokens = tokens[:j] } ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc) if err != nil { @@ -781,15 +831,15 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto nonContractTxs = int(ca.NonContractTxs) internalTxs = int(ca.InternalTxs) } else { - // addresses without any normal transactions can have internal transactions and therefore balance + // addresses without any normal transactions can have internal transactions that were not processed and therefore balance if b != nil { ba = &db.AddrBalance{ BalanceSat: *b, } } - // special handling if filtering for a contract, check the ballance of it + // special handling if filtering for a contract, check the ballance of it in the blockchain if len(filterDesc) > 0 && details >= AccountDetailsTokens { - t, err := w.getEthereumToken(0, addrDesc, filterDesc, details, 0) + t, err := w.getEthereumContractBalanceFromBlockchain(addrDesc, filterDesc, details) if err != nil { return nil, nil, nil, 0, 0, 0, 0, err } diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 55ed9dc544..c6a020554a 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -34,7 +34,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":18}],"erc20Contract":{"contract":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","name":"Contract 75","symbol":"S75","decimals":18}}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":18,"balance":"1000075074"}],"erc20Contract":{"contract":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","name":"Contract 75","symbol":"S75","decimals":18}}`, }, }, } diff --git a/tests/dbtestdata/fakechain_ethereumtype.go b/tests/dbtestdata/fakechain_ethereumtype.go index 800ff03f18..b19276162c 100644 --- a/tests/dbtestdata/fakechain_ethereumtype.go +++ b/tests/dbtestdata/fakechain_ethereumtype.go @@ -126,3 +126,8 @@ func (c *fakeBlockChainEthereumType) EthereumTypeGetErc20ContractInfo(contractDe Decimals: 18, }, nil } + +// EthereumTypeGetErc20ContractBalance is not supported +func (c *fakeBlockChainEthereumType) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) { + return big.NewInt(1000000000 + int64(addrDesc[0])*1000 + int64(contractDesc[0])), nil +} From 0ccb9b37b4df61f0b7c56d4540c72497beec5370 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 4 Apr 2022 17:24:53 +0200 Subject: [PATCH 053/530] =?UTF-8?q?Bump=20eth=20archive=20(+testnet)=201.1?= =?UTF-8?q?0.15=20=E2=86=92=201.10.17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum_archive.json | 6 +++--- configs/coins/ethereum_testnet_ropsten_archive.json | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 16e320e75b..4f1d3cfcaf 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -21,10 +21,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.15-8be800ff", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.15-8be800ff.tar.gz", + "version": "1.10.17-25c9b49f", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.15-8be800ff.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.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 --gcmode archive --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 8116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index 0e19291409..0e3692bb39 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -20,10 +20,10 @@ "package_name": "backend-ethereum-testnet-ropsten-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.15-8be800ff", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.15-8be800ff.tar.gz", + "version": "1.10.17-25c9b49f", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.15-8be800ff.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -60,4 +60,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} From 6fdf6e297c8a511cc65bec11aa834873e2fa8a20 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 14 Apr 2022 16:24:26 +0200 Subject: [PATCH 054/530] Parse ethereum input data --- api/types.go | 1 + api/worker.go | 47 ++- bchain/coins/eth/dataparser.go | 234 +++++++++++++- bchain/coins/eth/dataparser_test.go | 318 +++++++++++++++++++- bchain/types_ethereum_type.go | 27 +- db/rocksdb_ethereumtype.go | 62 +++- db/rocksdb_ethereumtype_test.go | 17 +- fourbyte/fourbyte.go | 5 +- fourbyte/fourbyte_test.go | 12 +- server/public.go | 15 +- server/public_ethereumtype_test.go | 61 ++++ server/public_test.go | 13 +- static/templates/address.html | 68 ++++- static/templates/tx.html | 40 +++ static/templates/txdetail_ethereumtype.html | 5 +- 15 files changed, 873 insertions(+), 52 deletions(-) diff --git a/api/types.go b/api/types.go index dc6644d009..545e58caab 100644 --- a/api/types.go +++ b/api/types.go @@ -207,6 +207,7 @@ type EthereumSpecific struct { GasUsed *big.Int `json:"gasUsed"` GasPrice *Amount `json:"gasPrice"` Data string `json:"data,omitempty"` + ParsedData *bchain.EthereumParsedInputData `json:"parsedData,omitempty"` InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"` } diff --git a/api/worker.go b/api/worker.go index c5f2e054b0..a2ec91ee6c 100644 --- a/api/worker.go +++ b/api/worker.go @@ -127,6 +127,23 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool return w.GetTransactionFromBchainTx(bchainTx, height, spendingTxs, specificJSON) } +func (w *Worker) getParsedEthereumInputData(data string) *bchain.EthereumParsedInputData { + var err error + var signatures *[]bchain.FourByteSignature + fourBytes := eth.GetSignatureFromData(data) + if fourBytes != 0 { + signatures, err = w.db.GetFourByteSignatures(fourBytes) + if err != nil { + glog.Errorf("GetFourByteSignatures(%v) error %v", fourBytes, err) + return nil + } + if signatures == nil { + return nil + } + } + return eth.ParseInputData(signatures, data) +} + // GetTransactionFromBchainTx reads transaction data from txid func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spendingTxs bool, specificJSON bool) (*Tx, error) { var err error @@ -270,6 +287,8 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe } } + parsedInputData := w.getParsedEthereumInputData(ethTxData.Data) + // mempool txs do not have fees yet if ethTxData.GasUsed != nil { feesSat.Mul(ethTxData.GasPrice, ethTxData.GasUsed) @@ -278,12 +297,13 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe valOutSat = bchainTx.Vout[0].ValueSat } ethSpecific = &EthereumSpecific{ - GasLimit: ethTxData.GasLimit, - GasPrice: (*Amount)(ethTxData.GasPrice), - GasUsed: ethTxData.GasUsed, - Nonce: ethTxData.Nonce, - Status: ethTxData.Status, - Data: ethTxData.Data, + GasLimit: ethTxData.GasLimit, + GasPrice: (*Amount)(ethTxData.GasPrice), + GasUsed: ethTxData.GasUsed, + Nonce: ethTxData.Nonce, + Status: ethTxData.Status, + Data: ethTxData.Data, + ParsedData: parsedInputData, } if internalData != nil { ethSpecific.Type = internalData.Type @@ -674,7 +694,6 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i } t := Token{ - Type: ERC20TokenType, Contract: ci.Contract, Name: ci.Name, Symbol: ci.Symbol, @@ -685,6 +704,7 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i // return contract balances/values only at or above AccountDetailsTokenBalances if details >= AccountDetailsTokenBalances && validContract { if c.Type == bchain.ERC20 { + t.Type = ERC20TokenType // get Erc20 Contract Balance from blockchain, balance obtained from adding and subtracting transfers is not correct b, err := w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, c.Contract) if err != nil { @@ -694,15 +714,20 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i t.BalanceSat = (*Amount)(b) } } else { - if len(t.Ids) > 0 { - ids := make([]Amount, len(t.Ids)) + if c.Type == bchain.ERC721 { + t.Type = ERC771TokenType + } else { + t.Type = ERC1155TokenType + } + if len(c.Ids) > 0 { + ids := make([]Amount, len(c.Ids)) for j := range ids { ids[j] = (Amount)(c.Ids[j]) } t.Ids = ids } - if len(t.IdValues) > 0 { - idValues := make([]TokenTransferValues, len(t.IdValues)) + if len(c.IdValues) > 0 { + idValues := make([]TokenTransferValues, len(c.IdValues)) for j := range idValues { idValues[j].Id = (*Amount)(&c.IdValues[j].Id) idValues[j].Value = (*Amount)(&c.IdValues[j].Value) diff --git a/bchain/coins/eth/dataparser.go b/bchain/coins/eth/dataparser.go index 55e632c347..88043a9655 100644 --- a/bchain/coins/eth/dataparser.go +++ b/bchain/coins/eth/dataparser.go @@ -4,8 +4,15 @@ import ( "bytes" "encoding/hex" "math/big" + "runtime/debug" + "strconv" + "strings" "unicode" "unicode/utf8" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/golang/glog" + "github.com/trezor/blockbook/bchain" ) func parseSimpleNumericProperty(data string) *big.Int { @@ -58,15 +65,230 @@ func parseSimpleStringProperty(data string) string { return "" } -func Decamel(s string) string { +func decamel(s string) string { var b bytes.Buffer splittable := false - for _, v := range s { - if splittable && unicode.IsUpper(v) { - b.WriteByte(' ') + for i, v := range s { + if i == 0 { + b.WriteRune(unicode.ToUpper(v)) + } else { + if splittable && unicode.IsUpper(v) { + b.WriteByte(' ') + } + b.WriteRune(v) + splittable = unicode.IsLower(v) || unicode.IsNumber(v) } - b.WriteRune(v) - splittable = unicode.IsLower(v) || unicode.IsNumber(v) } return b.String() } + +func GetSignatureFromData(data string) uint32 { + if has0xPrefix(data) { + data = data[2:] + } + if len(data) < 8 { + return 0 + } + sig, err := strconv.ParseUint(data[:8], 16, 32) + if err != nil { + return 0 + } + return uint32(sig) +} + +const ErrorTy byte = 255 + +func processParam(data string, index int, t *abi.Type, processed []bool) ([]string, int, bool) { + var retval []string + d := index << 6 + if d+64 > len(data) { + return nil, 0, false + } + block := data[d : d+64] + switch t.T { + // static types + case abi.IntTy, abi.UintTy, abi.BoolTy: + var n big.Int + _, ok := n.SetString(block, 16) + if !ok { + return nil, 0, false + } + if t.T == abi.BoolTy { + if n.Int64() != 0 { + retval = []string{"true"} + } else { + retval = []string{"false"} + } + } else { + retval = []string{n.String()} + } + processed[index] = true + index++ + case abi.AddressTy: + b, err := hex.DecodeString(block[24:]) + if err != nil { + return nil, 0, false + } + retval = []string{EIP55Address(b)} + processed[index] = true + index++ + case abi.FixedBytesTy: + retval = []string{"0x" + block[:t.Size<<1]} + processed[index] = true + index++ + case abi.ArrayTy: + for i := 0; i < t.Size; i++ { + var r []string + var ok bool + r, index, ok = processParam(data, index, t.Elem, processed) + if !ok { + return nil, 0, false + } + retval = append(retval, r...) + } + // dynamic types + case abi.StringTy, abi.BytesTy, abi.SliceTy: + // get offset of dynamic type + offset, err := strconv.ParseInt(block, 16, 64) + if err != nil { + return nil, 0, false + } + processed[index] = true + index++ + offset <<= 1 + d = int(offset) + dynIndex := d >> 6 + if d+64 > len(data) || d < 0 { + return nil, 0, false + } + // get element count of dynamic type + c, err := strconv.ParseInt(data[d:d+64], 16, 64) + count := int(c) + if err != nil { + return nil, 0, false + } + processed[dynIndex] = true + dynIndex++ + if t.T == abi.StringTy || t.T == abi.BytesTy { + d += 64 + de := d + (count << 1) + if de > len(data) { + return nil, 0, false + } + if count == 0 { + retval = []string{""} + } else { + block = data[d:de] + if t.T == abi.StringTy { + b, err := hex.DecodeString(block) + if err != nil { + return nil, 0, false + } + retval = []string{string(b)} + } else { + retval = []string{"0x" + block} + } + count = ((count - 1) >> 5) + 1 + for i := 0; i < count; i++ { + processed[dynIndex] = true + dynIndex++ + } + } + } else { + for i := 0; i < count; i++ { + var r []string + var ok bool + r, dynIndex, ok = processParam(data, dynIndex, t.Elem, processed) + if !ok { + return nil, 0, false + } + retval = append(retval, r...) + } + } + // types not processed + case abi.HashTy, abi.FixedPointTy, abi.FunctionTy, abi.TupleTy: + fallthrough + default: + return nil, 0, false + } + return retval, index, true +} + +func tryParseParams(data string, params []string, parsedParams []abi.Type) []bchain.EthereumParsedInputParam { + processed := make([]bool, len(data)/64) + parsed := make([]bchain.EthereumParsedInputParam, len(params)) + index := 0 + var values []string + var ok bool + for i := range params { + t := &parsedParams[i] + values, index, ok = processParam(data, index, t, processed) + if !ok { + return nil + } + parsed[i] = bchain.EthereumParsedInputParam{Type: params[i], Values: values} + } + // all data must be processed, otherwise wrong signature + for _, p := range processed { + if !p { + return nil + } + } + return parsed +} + +// ParseInputData tries to parse transaction input data from known FourByteSignatures +// as there may be multiple signatures for the same four bytes, it tries to match the input to the known parameters +// it does not parse tuples for now +func ParseInputData(signatures *[]bchain.FourByteSignature, data string) *bchain.EthereumParsedInputData { + if len(data) <= 2 { // data is empty or 0x + return &bchain.EthereumParsedInputData{Name: "Transfer"} + } + if len(data) < 10 || (len(data)-10)%64 != 0 { + return nil + } + parsed := bchain.EthereumParsedInputData{ + MethodId: data[:10], + } + defer func() { + if r := recover(); r != nil { + glog.Error("ParseInputData recovered from panic: ", r, ", ", data, ",signatures ", signatures) + debug.PrintStack() + } + }() + if signatures != nil { + data = data[10:] + for i := range *signatures { + s := &(*signatures)[i] + // if not yet done, set DecamelName and Function and parse parameter types from string to abi.Type + // the signatures are stored in cache + if s.DecamelName == "" { + s.DecamelName = decamel(s.Name) + s.Function = s.Name + "(" + strings.Join(s.Parameters, ", ") + ")" + s.ParsedParameters = make([]abi.Type, len(s.Parameters)) + for j := range s.Parameters { + var t abi.Type + if len(s.Parameters[j]) > 0 && s.Parameters[j][0] == '(' { + // Tuple type is not supported for now + t = abi.Type{T: abi.TupleTy} + } else { + var err error + t, err = abi.NewType(s.Parameters[j], "", nil) + if err != nil { + t = abi.Type{T: ErrorTy} + } + } + s.ParsedParameters[j] = t + } + } + parsedParams := tryParseParams(data, s.Parameters, s.ParsedParameters) + if parsedParams != nil { + parsed.Name = s.DecamelName + parsed.Function = s.Function + parsed.Params = parsedParams + break + } + } + } + return &parsed +} diff --git a/bchain/coins/eth/dataparser_test.go b/bchain/coins/eth/dataparser_test.go index 9af84c1f4d..234dd8cd10 100644 --- a/bchain/coins/eth/dataparser_test.go +++ b/bchain/coins/eth/dataparser_test.go @@ -2,7 +2,12 @@ package eth -import "testing" +import ( + "reflect" + "testing" + + "github.com/trezor/blockbook/bchain" +) func Test_parseSimpleStringProperty(t *testing.T) { tests := []struct { @@ -51,3 +56,314 @@ func Test_parseSimpleStringProperty(t *testing.T) { }) } } + +func TestGetSignatureFromData(t *testing.T) { + tests := []struct { + name string + data string + want uint32 + }{ + { + name: "0x9e53a69a", + data: "0x9e53a69a000000000000000000000000000000000000000000000", + want: 2656282266, + }, + { + name: "9e53a69b", + data: "9e53a69b000000000000000000000000000000000000000000000", + want: 2656282267, + }, + { + name: "0x9e53 short", + data: "0x9e53", + want: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetSignatureFromData(tt.data); got != tt.want { + t.Errorf("GetSignatureFromData() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestParseInputData(t *testing.T) { + signatures := []bchain.FourByteSignature{ + { + Name: "mintFighter", + Parameters: []string{}, + }, + { + Name: "cancelMultipleMakerOrders", + Parameters: []string{"uint256[]"}, + }, + { + Name: "mockRegisterFact", + Parameters: []string{"bytes32"}, + }, + { + Name: "vestingDeposits", + Parameters: []string{"address"}, + }, + { + Name: "addLiquidityETH", + Parameters: []string{"address", "uint256", "uint256", "uint256", "address", "uint256"}, + }, + { + Name: "spread", + Parameters: []string{"uint256", "address[]"}, + }, + { + Name: "registerWithConfig", + Parameters: []string{"string", "address", "uint256", "bytes32", "address", "address"}, + }, + { + Name: "atomicMatch_", + Parameters: []string{"address[14]", "uint256[18]", "uint8[8]", "bytes", "bytes", "bytes", "bytes", "bytes", "bytes", "uint8[2]", "bytes32[5]"}, + }, + { + Name: "transmitAndSellTokenForEth", + Parameters: []string{"address", "uint256", "uint256", "uint256", "address", "(uint8,bytes32,bytes32)", "bytes"}, + }, + } + tests := []struct { + name string + signatures *[]bchain.FourByteSignature + data string + want *bchain.EthereumParsedInputData + wantErr bool + }{ + { + name: "transfer", + signatures: &signatures, + data: "", + want: &bchain.EthereumParsedInputData{ + Name: "Transfer", + }, + }, + { + name: "mintFighter", + signatures: &signatures, + data: "0xa19b9082", + want: &bchain.EthereumParsedInputData{ + MethodId: "0xa19b9082", + Name: "Mint Fighter", + Function: "mintFighter()", + Params: []bchain.EthereumParsedInputParam{}, + }, + }, + { + name: "mockRegisterFact", + signatures: &signatures, + data: "0xf69507abdc8fa8fe57a22de66a1d5898496c524068cb04c31f72497b3ac9f3b449e58725", + want: &bchain.EthereumParsedInputData{ + MethodId: "0xf69507ab", + Name: "Mock Register Fact", + Function: "mockRegisterFact(bytes32)", + Params: []bchain.EthereumParsedInputParam{ + { + Type: "bytes32", + Values: []string{"0xdc8fa8fe57a22de66a1d5898496c524068cb04c31f72497b3ac9f3b449e58725"}, + }, + }, + }, + }, + { + name: "cancelMultipleMakerOrders", + signatures: &signatures, + data: "0x9e53a69a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000017f62f8db30", + want: &bchain.EthereumParsedInputData{ + MethodId: "0x9e53a69a", + Name: "Cancel Multiple Maker Orders", + Function: "cancelMultipleMakerOrders(uint256[])", + Params: []bchain.EthereumParsedInputParam{ + { + Type: "uint256[]", + Values: []string{"1646632950576"}, + }, + }, + }, + }, + { + name: "addLiquidityETH", + signatures: &signatures, + data: "0xf305d719000000000000000000000000b80e5aaa2131c07568128f68b8538ed3c8951234000000000000000000000000000000000000007e37be2022c0914b2680000000000000000000000000000000000000000000007e37be2022c0914b26800000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000009f64b014ca26f2def573246543dd1115b229e4f400000000000000000000000000000000000000000000000000000000623f56f8", + want: &bchain.EthereumParsedInputData{ + MethodId: "0xf305d719", + Name: "Add Liquidity ETH", + Function: "addLiquidityETH(address, uint256, uint256, uint256, address, uint256)", + Params: []bchain.EthereumParsedInputParam{ + { + Type: "address", + Values: []string{"0xB80e5AaA2131c07568128f68b8538eD3C8951234"}, + }, + { + Type: "uint256", + Values: []string{"10000000000000000000000000000000"}, + }, + { + Type: "uint256", + Values: []string{"10000000000000000000000000000000"}, + }, + { + Type: "uint256", + Values: []string{"1000000000000000000"}, + }, + { + Type: "address", + Values: []string{"0x9f64B014CA26F2DeF573246543DD1115b229e4F4"}, + }, + { + Type: "uint256", + Values: []string{"1648318200"}, + }, + }, + }, + }, + { + name: "addLiquidityETH data don't match - too long", + signatures: &signatures, + data: "0xf305d719000000000000000000000000b80e5aaa2131c07568128f68b8538ed3c8951234000000000000000000000000000000000000007e37be2022c0914b2680000000000000000000000000000000000000000000007e37be2022c0914b26800000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000009f64b014ca26f2def573246543dd1115b229e4f400000000000000000000000000000000000000000000000000000000623f56f800000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: &bchain.EthereumParsedInputData{ + MethodId: "0xf305d719", + }, + }, + { + name: "addLiquidityETH data don't match - too short", + signatures: &signatures, + data: "0xf305d719000000000000000000000000b80e5aaa2131c07568128f68b8538ed3c8951234000000000000000000000000000000000000007e37be2022c0914b2680000000000000000000000000000000000000000000007e37be2022c0914b26800000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000009f64b014ca26f2def573246543dd1115b229e4f4", + want: &bchain.EthereumParsedInputData{ + MethodId: "0xf305d719", + }, + }, + { + name: "spread", + signatures: &signatures, + data: "0xcd51b093000000000000000000000000000000000000000000000000016345785d8a00000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000048c999d9206fcf2a0ecde10049de6dc2d1704bb2000000000000000000000000d2dae6b2309ada5d4c983b4c7d2c942452adc759", + want: &bchain.EthereumParsedInputData{ + MethodId: "0xcd51b093", + Name: "Spread", + Function: "spread(uint256, address[])", + Params: []bchain.EthereumParsedInputParam{ + { + Type: "uint256", + Values: []string{"100000000000000000"}, + }, + { + Type: "address[]", + Values: []string{"0x48c999d9206fcf2A0ecdE10049de6Dc2d1704Bb2", "0xD2DAE6B2309aDa5d4c983B4c7D2c942452aDC759"}, + }, + }, + }, + }, + { + name: "atomicMatch_", // mainnet tx 0x57aff22b0f812e05467fb73caec8ac0364a535382496e5f64eb9df9fb32bd85f + signatures: &signatures, + data: "0xab834bab0000000000000000000000007f268357a8c2552623316e2562d90e642bb538e50000000000000000000000001676b0ab0aeb83122c58abc3d6a50b6c4a9d376300000000000000000000000024c57fbb5c260edf158583818177cfd5c2dec4700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000baf2127b49fc93cbca6269fade0f7f31df4c88a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f268357a8c2552623316e2562d90e642bb538e500000000000000000000000024c57fbb5c260edf158583818177cfd5c2dec47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005b3256965e7c3cf26e11fcaf296dfc8807c01073000000000000000000000000baf2127b49fc93cbca6269fade0f7f31df4c88a70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000062531f6400000000000000000000000000000000000000000000000000000000000000000227db897c05fe6409bc72c6bee932b99a92ca45e155cf85e763424e7a3ee61500000000000000000000000000000000000000000000000000000000000002ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000625313f800000000000000000000000000000000000000000000000000000000627aa14b79166058af7dd96e2190730f926c56d6131af9d72b4dd2138b58c30e268c7f300000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000007c000000000000000000000000000000000000000000000000000000000000008e00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000b20000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c77e6196859305642ea4751b9597a9507472acb04b9f1f4759aa0f27af41edd8960513f1649f58782cacce26b1341575b584594f940bba0614aff302d25b4b10477e6196859305642ea4751b9597a9507472acb04b9f1f4759aa0f27af41edd8960513f1649f58782cacce26b1341575b584594f940bba0614aff302d25b4b104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4fb16a59500000000000000000000000000000000000000000000000000000000000000000000000000000000000000001676b0ab0aeb83122c58abc3d6a50b6c4a9d3763000000000000000000000000f25f4f4f6517101dc947d1c0370571ebdd25f14a00000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4fb16a59500000000000000000000000024c57fbb5c260edf158583818177cfd5c2dec4700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f25f4f4f6517101dc947d1c0370571ebdd25f14a00000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e400000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + want: &bchain.EthereumParsedInputData{ + MethodId: "0xab834bab", + Name: "Atomic Match_", + Function: "atomicMatch_(address[14], uint256[18], uint8[8], bytes, bytes, bytes, bytes, bytes, bytes, uint8[2], bytes32[5])", + Params: []bchain.EthereumParsedInputParam{ + { + Type: "address[14]", + Values: []string{ + "0x7f268357A8c2552623316e2562D90e642bB538E5", "0x1676b0AB0Aeb83122C58ABC3d6a50B6c4A9d3763", "0x24C57FBB5c260EDf158583818177Cfd5C2dec470", "0x0000000000000000000000000000000000000000", + "0xBAf2127B49fC93CbcA6269FAdE0F7F31dF4c88a7", "0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", "0x7f268357A8c2552623316e2562D90e642bB538E5", + "0x24C57FBB5c260EDf158583818177Cfd5C2dec470", "0x0000000000000000000000000000000000000000", "0x5b3256965e7C3cF26E11FCAf296DfC8807C01073", "0xBAf2127B49fC93CbcA6269FAdE0F7F31dF4c88a7", + "0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000"}, + }, + { + Type: "uint256[18]", + Values: []string{ + "750", "0", "0", "0", "10000000000000000", "0", "1649614692", "0", "975047921716720136517384107537725863826800092678142650456874303300963329557", + "750", "0", "0", "0", "10000000000000000", "0", "1649611768", "1652203851", "54769390272606378508076535204478407261307419838517394120712398796227861053232"}, + }, + { + Type: "uint8[8]", + Values: []string{"1", "0", "0", "1", "1", "1", "0", "1"}, + }, + { + Type: "bytes", + Values: []string{"0xfb16a59500000000000000000000000000000000000000000000000000000000000000000000000000000000000000001676b0ab0aeb83122c58abc3d6a50b6c4a9d3763000000000000000000000000f25f4f4f6517101dc947d1c0370571ebdd25f14a00000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000"}, + }, + { + Type: "bytes", + Values: []string{"0xfb16a59500000000000000000000000024c57fbb5c260edf158583818177cfd5c2dec4700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f25f4f4f6517101dc947d1c0370571ebdd25f14a00000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000"}, + }, + { + Type: "bytes", + Values: []string{"0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, + }, + { + Type: "bytes", + Values: []string{"0x000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, + }, + { + Type: "bytes", + Values: []string{""}, + }, + { + Type: "bytes", + Values: []string{""}, + }, + { + Type: "uint8[2]", + Values: []string{"28", "28"}, + }, + { + Type: "bytes32[5]", + Values: []string{"0x77e6196859305642ea4751b9597a9507472acb04b9f1f4759aa0f27af41edd89", "0x60513f1649f58782cacce26b1341575b584594f940bba0614aff302d25b4b104", + "0x77e6196859305642ea4751b9597a9507472acb04b9f1f4759aa0f27af41edd89", "0x60513f1649f58782cacce26b1341575b584594f940bba0614aff302d25b4b104", + "0x0000000000000000000000000000000000000000000000000000000000000000"}, + }, + }, + }, + }, + { + name: "registerWithConfig", + signatures: &signatures, + data: "0xf7a1696300000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000769cbf44073741ccb4c39c945402130b46fa8a70000000000000000000000000000000000000000000000000000000012cf35707a8c22626793047f41a428e815e2bb12ced6d5db4246a8b0bda488c541647bef0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba410000000000000000000000000769cbf44073741ccb4c39c945402130b46fa8a700000000000000000000000000000000000000000000000000000000000000076d6f6e7369746100000000000000000000000000000000000000000000000000", + want: &bchain.EthereumParsedInputData{ + MethodId: "0xf7a16963", + Name: "Register With Config", + Function: "registerWithConfig(string, address, uint256, bytes32, address, address)", + Params: []bchain.EthereumParsedInputParam{ + { + Type: "string", + Values: []string{"monsita"}, + }, + { + Type: "address", + Values: []string{"0x0769cBf44073741cCb4C39c945402130B46fa8A7"}, + }, + { + Type: "uint256", + Values: []string{"315569520"}, + }, + { + Type: "bytes32", + Values: []string{"0x7a8c22626793047f41a428e815e2bb12ced6d5db4246a8b0bda488c541647bef"}, + }, + { + Type: "address", + Values: []string{"0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41"}, + }, + { + Type: "address", + Values: []string{"0x0769cBf44073741cCb4C39c945402130B46fa8A7"}, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ParseInputData(tt.signatures, tt.data) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ParseInputData() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index 4061eea7a8..a631532494 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -1,6 +1,10 @@ package bchain -import "math/big" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" +) // EthereumType specific @@ -12,6 +16,27 @@ type EthereumInternalTransfer struct { Value big.Int `json:"value"` } +type FourByteSignature struct { + // stored in DB + Name string + Parameters []string + // processed from DB data and stored only in cache + DecamelName string + Function string + ParsedParameters []abi.Type +} + +type EthereumParsedInputParam struct { + Type string `json:"type"` + Values []string `json:"values,omitempty"` +} +type EthereumParsedInputData struct { + MethodId string `json:"methodId"` + Name string `json:"name"` + Function string `json:"function,omitempty"` + Params []EthereumParsedInputParam `json:"params,omitempty"` +} + // EthereumInternalTransactionType - type of ethereum transaction from internal data type EthereumInternalTransactionType int diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index 5bbc1c10f4..3310afe347 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "math/big" + "sync" "github.com/flier/gorocksdb" "github.com/golang/glog" @@ -578,11 +579,8 @@ func (d *RocksDB) unpackEthInternalData(buf []byte) (*bchain.EthereumInternalDat return &id, nil } -type FourByteSignature struct { - Name string - Parameters []string -} - +// FourByteSignature contains 4byte signature of transaction value with parameters +// and parsed parameters (that are not stored in DB) func packFourByteKey(fourBytes uint32, id uint32) []byte { key := make([]byte, 0, 8) key = append(key, packUint(fourBytes)...) @@ -590,7 +588,7 @@ func packFourByteKey(fourBytes uint32, id uint32) []byte { return key } -func packFourByteSignature(signature *FourByteSignature) []byte { +func packFourByteSignature(signature *bchain.FourByteSignature) []byte { buf := packString(signature.Name) for i := range signature.Parameters { buf = append(buf, packString(signature.Parameters[i])...) @@ -598,8 +596,8 @@ func packFourByteSignature(signature *FourByteSignature) []byte { return buf } -func unpackFourByteSignature(buf []byte) (*FourByteSignature, error) { - var signature FourByteSignature +func unpackFourByteSignature(buf []byte) (*bchain.FourByteSignature, error) { + var signature bchain.FourByteSignature var l int signature.Name, l = unpackString(buf) for l < len(buf) { @@ -610,7 +608,8 @@ func unpackFourByteSignature(buf []byte) (*FourByteSignature, error) { return &signature, nil } -func (d *RocksDB) GetFourByteSignature(fourBytes uint32, id uint32) (*FourByteSignature, error) { +// GetFourByteSignature gets all 4byte signature of given fourBytes and id +func (d *RocksDB) GetFourByteSignature(fourBytes uint32, id uint32) (*bchain.FourByteSignature, error) { key := packFourByteKey(fourBytes, id) val, err := d.db.GetCF(d.ro, d.cfh[cfFunctionSignatures], key) if err != nil { @@ -624,12 +623,51 @@ func (d *RocksDB) GetFourByteSignature(fourBytes uint32, id uint32) (*FourByteSi return unpackFourByteSignature(buf) } -func (d *RocksDB) StoreFourByteSignature(wb *gorocksdb.WriteBatch, fourBytes uint32, id uint32, signature *FourByteSignature) error { +var cachedByteSignatures = make(map[uint32]*[]bchain.FourByteSignature) +var cachedByteSignaturesMux sync.Mutex + +// GetFourByteSignatures gets all 4byte signatures of given fourBytes +// (there may be more than one signature starting with the same four bytes) +func (d *RocksDB) GetFourByteSignatures(fourBytes uint32) (*[]bchain.FourByteSignature, error) { + cachedByteSignaturesMux.Lock() + signatures, found := cachedByteSignatures[fourBytes] + cachedByteSignaturesMux.Unlock() + if !found { + retval := []bchain.FourByteSignature{} + key := packUint(fourBytes) + it := d.db.NewIteratorCF(d.ro, d.cfh[cfFunctionSignatures]) + defer it.Close() + for it.Seek(key); it.Valid(); it.Next() { + current := it.Key().Data() + if bytes.Compare(current[:4], key) > 0 { + break + } + val := it.Value().Data() + signature, err := unpackFourByteSignature(val) + if err != nil { + return nil, err + } + retval = append(retval, *signature) + } + cachedByteSignaturesMux.Lock() + cachedByteSignatures[fourBytes] = &retval + cachedByteSignaturesMux.Unlock() + return &retval, nil + } + return signatures, nil +} + +// StoreFourByteSignature stores 4byte signature in DB +func (d *RocksDB) StoreFourByteSignature(wb *gorocksdb.WriteBatch, fourBytes uint32, id uint32, signature *bchain.FourByteSignature) error { key := packFourByteKey(fourBytes, id) wb.PutCF(d.cfh[cfFunctionSignatures], key, packFourByteSignature(signature)) + cachedByteSignaturesMux.Lock() + delete(cachedByteSignatures, fourBytes) + cachedByteSignaturesMux.Unlock() return nil } +// GetEthereumInternalData gets transaction internal data from DB func (d *RocksDB) GetEthereumInternalData(txid string) (*bchain.EthereumInternalData, error) { btxID, err := d.chainParser.PackTxid(txid) if err != nil { @@ -902,7 +940,9 @@ func (d *RocksDB) disconnectAddress(btxID []byte, internal bool, addrDesc bchain glog.Warning("AddressContracts ", addrDesc, ", contract ", contractIndex, " Txs would be negative, tx ", hex.EncodeToString(btxID)) } } else { - glog.Warning("AddressContracts ", addrDesc, ", contract ", btxContract.contract, " not found, tx ", hex.EncodeToString(btxID)) + if !isZeroAddress(addrDesc) { + glog.Warning("AddressContracts ", addrDesc, ", contract ", btxContract.contract, " not found, tx ", hex.EncodeToString(btxID)) + } } } } else { diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index 9d257c70e4..a61fc958a0 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -301,7 +301,7 @@ func formatInternalData(in *bchain.EthereumInternalData) *bchain.EthereumInterna func testFourByteSignature(t *testing.T, d *RocksDB) { fourBytes := uint32(1234123) id := uint32(42313) - signature := FourByteSignature{ + signature := bchain.FourByteSignature{ Name: "xyz", Parameters: []string{"address", "(bytes,uint256[],uint256)", "uint16"}, } @@ -320,6 +320,13 @@ func testFourByteSignature(t *testing.T, d *RocksDB) { if !reflect.DeepEqual(*got, signature) { t.Errorf("testFourByteSignature: got %+v, want %+v", got, signature) } + gotSlice, err := d.GetFourByteSignatures(fourBytes) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(*gotSlice, []bchain.FourByteSignature{signature}) { + t.Errorf("testFourByteSignature: got %+v, want %+v", *gotSlice, []bchain.FourByteSignature{signature}) + } } // TestRocksDB_Index_EthereumType is an integration test probing the whole indexing functionality for EthereumType chains @@ -1165,24 +1172,24 @@ func Test_packUnpackBlockTx(t *testing.T) { func Test_packUnpackFourByteSignature(t *testing.T) { tests := []struct { name string - signature FourByteSignature + signature bchain.FourByteSignature }{ { name: "no params", - signature: FourByteSignature{ + signature: bchain.FourByteSignature{ Name: "abcdef", }, }, { name: "one param", - signature: FourByteSignature{ + signature: bchain.FourByteSignature{ Name: "opqr", Parameters: []string{"uint16"}, }, }, { name: "multiple params", - signature: FourByteSignature{ + signature: bchain.FourByteSignature{ Name: "xyz", Parameters: []string{"address", "(bytes,uint256[],uint256)", "uint16"}, }, diff --git a/fourbyte/fourbyte.go b/fourbyte/fourbyte.go index 76b56b3c35..21d6fe12a1 100644 --- a/fourbyte/fourbyte.go +++ b/fourbyte/fourbyte.go @@ -11,6 +11,7 @@ import ( "github.com/flier/gorocksdb" "github.com/golang/glog" + "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/db" ) @@ -98,13 +99,13 @@ func (fd *FourByteSignaturesDownloader) getPageWithRetry(url string) (*signature return nil, errors.New("Too many retries to 4byte signatures") } -func parseSignatureFromText(t string) *db.FourByteSignature { +func parseSignatureFromText(t string) *bchain.FourByteSignature { s := strings.Index(t, "(") e := strings.LastIndex(t, ")") if s < 0 || e < 0 { return nil } - var signature db.FourByteSignature + var signature bchain.FourByteSignature signature.Name = t[:s] params := t[s+1 : e] if len(params) > 0 { diff --git a/fourbyte/fourbyte_test.go b/fourbyte/fourbyte_test.go index 95c7b3af70..c64ddad5ce 100644 --- a/fourbyte/fourbyte_test.go +++ b/fourbyte/fourbyte_test.go @@ -4,26 +4,26 @@ import ( "reflect" "testing" - "github.com/trezor/blockbook/db" + "github.com/trezor/blockbook/bchain" ) func Test_parseSignatureFromText(t *testing.T) { tests := []struct { name string signature string - want db.FourByteSignature + want bchain.FourByteSignature }{ { name: "_gonsPerFragment", signature: "_gonsPerFragment()", - want: db.FourByteSignature{ + want: bchain.FourByteSignature{ Name: "_gonsPerFragment", }, }, { name: "vestingDeposits", signature: "vestingDeposits(address)", - want: db.FourByteSignature{ + want: bchain.FourByteSignature{ Name: "vestingDeposits", Parameters: []string{"address"}, }, @@ -31,7 +31,7 @@ func Test_parseSignatureFromText(t *testing.T) { { name: "batchTransferTokenB", signature: "batchTransferTokenB(address[],uint256)", - want: db.FourByteSignature{ + want: bchain.FourByteSignature{ Name: "batchTransferTokenB", Parameters: []string{"address[]", "uint256"}, }, @@ -39,7 +39,7 @@ func Test_parseSignatureFromText(t *testing.T) { { name: "transmitAndSellTokenForEth", signature: "transmitAndSellTokenForEth(address,uint256,uint256,uint256,address,(uint8,bytes32,bytes32),bytes)", - want: db.FourByteSignature{ + want: bchain.FourByteSignature{ Name: "transmitAndSellTokenForEth", Parameters: []string{"address", "uint256", "uint256", "uint256", "address", "(uint8,bytes32,bytes32)", "bytes"}, }, diff --git a/server/public.go b/server/public.go index e778306bc4..be0dc4027e 100644 --- a/server/public.go +++ b/server/public.go @@ -457,6 +457,8 @@ func (s *PublicServer) parseTemplates() []*template.Template { "isOwnAddress": isOwnAddress, "toJSON": toJSON, "tokenTransfersCount": tokenTransfersCount, + "tokenCount": tokenCount, + "hasPrefix": strings.HasPrefix, } var createTemplate func(filenames ...string) *template.Template if s.debug { @@ -559,7 +561,7 @@ func isOwnAddress(td *TemplateData, a string) bool { return a == td.AddrStr } -// called from template, returns count of token transfers of given type +// called from template, returns count of token transfers of given type in a tx func tokenTransfersCount(tx *api.Tx, t api.TokenType) int { count := 0 for i := range tx.TokenTransfers { @@ -570,6 +572,17 @@ func tokenTransfersCount(tx *api.Tx, t api.TokenType) int { return count } +// called from template, returns count of tokens in array of given type +func tokenCount(tokens []api.Token, t api.TokenType) int { + count := 0 + for i := range tokens { + if tokens[i].Type == t { + count++ + } + } + return count +} + func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) { var tx *api.Tx var err error diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index c6a020554a..27a6009da2 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -8,13 +8,43 @@ import ( "net/http/httptest" "testing" + "github.com/flier/gorocksdb" "github.com/golang/glog" + "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain/coins/eth" + "github.com/trezor/blockbook/db" "github.com/trezor/blockbook/tests/dbtestdata" ) func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { tests := []httpTests{ + { + name: "explorerAddress " + dbtestdata.EthAddr7b, + r: newGetRequest(ts.URL + "/address/" + dbtestdata.EthAddr7b), + status: http.StatusOK, + contentType: "text/html; charset=utf-8", + body: []string{ + `Trezor Fake Coin Explorer

Contract Contract 123 (S123) 0.000000000123450123 FAKE

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

Confirmed

Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ERC20 Tokens
ContractTokensTransfers
Contract 740.000000001000123074 S741
Contract 130.000000001000123013 S131
ERC721 Tokens
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
ID 1 S205
Fee: 0.00008794500041041 FAKE
Unconfirmed Transaction!0 FAKE
ERC20 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
0.000871180000950184 S74
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
7.674999999999991915 S13
Fee: 0.000216368 FAKE
Unconfirmed Transaction!0 FAKE
`, + }, + }, + { + name: "explorerAddress " + dbtestdata.EthAddr5d, + r: newGetRequest(ts.URL + "/address/" + dbtestdata.EthAddr5d), + status: http.StatusOK, + contentType: "text/html; charset=utf-8", + body: []string{ + `Trezor Fake Coin Explorer

Contract Contract 93 (S93) 0.000000000123450093 FAKE

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

Confirmed

Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ERC1155 Tokens
ContractTokensTransfers
Contract 1111776:1 S111, 1898:10 S1111

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0 FAKE
ERC1155 Token Transfers
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1776:1 S1111898:10 S111
Fee: 0.000081891755740665 FAKE
Unconfirmed Transaction!0 FAKE
`, + }, + }, + { + name: "explorerTx " + dbtestdata.EthTxidB1T2, + r: newGetRequest(ts.URL + "/tx/0x" + dbtestdata.EthTxidB1T2), + status: http.StatusOK, + contentType: "text/html; charset=utf-8", + body: []string{ + `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101

Summary

In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE
Fees0.002081 FAKE
RBFON

Details

Input Data
Transfer
Method ID: 0xa9059cbb
Function: transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, + }, + }, { name: "apiIndex", r: newGetRequest(ts.URL + "/api"), @@ -37,11 +67,42 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":18,"balance":"1000075074"}],"erc20Contract":{"contract":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","name":"Contract 75","symbol":"S75","decimals":18}}`, }, }, + { + name: "apiAddress EthAddr7b details=txs", + r: newGetRequest(ts.URL + "/api/v2/address/" + dbtestdata.EthAddr7b + "?details=txs"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":18,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"erc20Contract":{"contract":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","name":"Contract 123","symbol":"S123","decimals":18}}`, + }, + }, + { + name: "apiTx EthTxidB1T2", + r: newGetRequest(ts.URL + "/api/v2/tx/0x" + dbtestdata.EthTxidB1T2), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}}}`, + }, + }, } performHttpTests(tests, t, ts) } +func initEthereumTypeDB(d *db.RocksDB) error { + // add 0xa9059cbb transfer(address,uint256) signature + wb := gorocksdb.NewWriteBatch() + defer wb.Destroy() + if err := d.StoreFourByteSignature(wb, 2835717307, 145, &bchain.FourByteSignature{ + Name: "transfer", + Parameters: []string{"address", "uint256"}, + }); err != nil { + return err + } + return d.WriteBatch(wb) +} + func Test_PublicServer_EthereumType(t *testing.T) { parser := eth.NewEthereumParser(1) chain, err := dbtestdata.NewFakeBlockChainEthereumType(parser) diff --git a/server/public_test.go b/server/public_test.go index f06d55d2e1..341a670de3 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -74,10 +74,15 @@ func setupRocksDB(parser bchain.BlockChainParser, chain bchain.BlockChain, t *te if err := d.ConnectBlock(block2); err != nil { t.Fatal(err) } - if err := InitTestFiatRates(d); err != nil { + if err := initTestFiatRates(d); err != nil { t.Fatal(err) } is.FinishedSync(block2.Height) + if parser.GetChainType() == bchain.ChainEthereumType { + if err := initEthereumTypeDB(d); err != nil { + t.Fatal(err) + } + } return d, is, tmp } @@ -168,8 +173,8 @@ func insertFiatRate(date string, rates map[string]float64, d *db.RocksDB) error return d.FiatRatesStoreTicker(ticker) } -// InitTestFiatRates initializes test data for /api/v2/tickers endpoint -func InitTestFiatRates(d *db.RocksDB) error { +// initTestFiatRates initializes test data for /api/v2/tickers endpoint +func initTestFiatRates(d *db.RocksDB) error { if err := insertFiatRate("20180320020000", map[string]float64{ "usd": 2000.0, "eur": 1300.0, @@ -235,7 +240,7 @@ func performHttpTests(tests []httpTests, t *testing.T, ts *httptest.Server) { b := string(bb) for _, c := range tt.body { if !strings.Contains(b, c) { - t.Errorf("got %v, want to contain %v", b, c) + t.Errorf("got\n%v\nwant to contain %v", b, c) break } } diff --git a/static/templates/address.html b/static/templates/address.html index 7c661b78ef..730355aebb 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -30,7 +30,7 @@

Confirmed

Nonce {{$addr.Nonce}} - {{- if $addr.Tokens -}} + {{if tokenCount $addr.Tokens "ERC20"}} ERC20 Tokens @@ -41,13 +41,75 @@

Confirmed

Tokens Transfers - {{- range $t := $addr.Tokens -}} + {{range $t := $addr.Tokens}} + {{if eq $t.Type "ERC20"}} {{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} {{formatAmountWithDecimals $t.BalanceSat $t.Decimals}} {{$t.Symbol}} {{$t.Transfers}} - {{- end -}} + {{end}} + {{end}} + + + + + {{- end -}} + {{if tokenCount $addr.Tokens "ERC721"}} + + ERC721 Tokens + + + + + + + + + {{range $t := $addr.Tokens}} + {{if eq $t.Type "ERC721"}} + + + + + + {{end}} + {{end}} + +
ContractTokensTransfers
{{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} + {{range $i, $iv := $t.Ids}} + {{if $i}}, {{end}} + {{formatAmountWithDecimals $iv 0}} + {{end}} + {{$t.Transfers}}
+ + + {{- end -}} + {{if tokenCount $addr.Tokens "ERC1155"}} + + ERC1155 Tokens + + + + + + + + + {{range $t := $addr.Tokens}} + {{if eq $t.Type "ERC1155"}} + + + + + + {{end}} + {{end}}
ContractTokensTransfers
{{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} + {{range $i, $iv := $t.IdValues}} + {{if $i}}, {{end}} + {{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$t.Symbol}} + {{end}} + {{$t.Transfers}}
diff --git a/static/templates/tx.html b/static/templates/tx.html index 3f3f21d1f2..6a260a793b 100644 --- a/static/templates/tx.html +++ b/static/templates/tx.html @@ -84,6 +84,46 @@

Details

{{template "txdetail" .}}
+{{if eq .ChainType 1}} +{{if $tx.EthereumSpecific.ParsedData}} +{{if $tx.EthereumSpecific.ParsedData.Function }} +
+
Input Data
+
+ {{if $tx.EthereumSpecific.ParsedData.Name}}
{{$tx.EthereumSpecific.ParsedData.Name}}
{{end}}{{if $tx.EthereumSpecific.ParsedData.MethodId}}
Method ID: {{$tx.EthereumSpecific.ParsedData.MethodId}}
{{end}} + {{if $tx.EthereumSpecific.ParsedData.Function}}
Function: {{$tx.EthereumSpecific.ParsedData.Function}}
{{end}} + {{if $tx.EthereumSpecific.ParsedData.Params}} +
+ + + + + + + + + + {{range $i,$p := $tx.EthereumSpecific.ParsedData.Params}} + + + + + + {{end}} + +
#TypeData
{{$i}}{{$p.Type}} + {{range $j,$v := $p.Values}} + {{if $j}}
{{end}} + {{if hasPrefix $p.Type "address"}}{{$v}}{{else}}{{$v}}{{end}} + {{end}} +
+
+ {{end}} +
+
+{{end}} +{{end}} +{{end}}
Raw Transaction
diff --git a/static/templates/txdetail_ethereumtype.html b/static/templates/txdetail_ethereumtype.html index aadb773e73..d47d7f0c47 100644 --- a/static/templates/txdetail_ethereumtype.html +++ b/static/templates/txdetail_ethereumtype.html @@ -6,6 +6,9 @@ {{if eq $tx.EthereumSpecific.Status 1}}{{end}}{{if eq $tx.EthereumSpecific.Status 0}}{{end}}
{{- if $tx.Blocktime}}
{{if $tx.Confirmations}}mined{{else}}first seen{{end}} {{formatUnixTime $tx.Blocktime}}
{{end -}} + {{if $tx.EthereumSpecific.ParsedData}} + {{if $tx.EthereumSpecific.ParsedData.Name}}
{{$tx.EthereumSpecific.ParsedData.Name}}
{{end}}{{if $tx.EthereumSpecific.ParsedData.MethodId}}
Method ID: {{$tx.EthereumSpecific.ParsedData.MethodId}}
{{end}} + {{end}} {{if $tx.EthereumSpecific.Error}}
Error: {{$tx.EthereumSpecific.Error}}
{{end}}
@@ -265,7 +268,7 @@
{{- range $iv := $tt.Values -}} - {{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value $tt.Decimals}} {{$tt.Symbol}} + {{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$tt.Symbol}} {{- end -}}
From 77561e3567376e51fd4688f59c1c74676b3f2314 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 16 Apr 2022 01:33:22 +0200 Subject: [PATCH 055/530] Process ENS records --- bchain/coins/eth/contract.go | 2 + bchain/coins/eth/dataparser.go | 28 ++++++++++- bchain/coins/eth/dataparser_test.go | 76 +++++++++++++++++++++++++++++ bchain/coins/eth/ethrpc.go | 29 +++++++---- bchain/types_ethereum_type.go | 11 ++++- 5 files changed, 134 insertions(+), 12 deletions(-) diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go index 11fe89d042..ddc992949c 100644 --- a/bchain/coins/eth/contract.go +++ b/bchain/coins/eth/contract.go @@ -21,6 +21,8 @@ const tokenTransferEventSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f16 const tokenERC1155TransferSingleEventSignature = "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62" const tokenERC1155TransferBatchEventSignature = "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb" +const nameRegisteredEventSignature = "0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621f71ce8f02b9f2a230097404f" + const contractNameSignature = "0x06fdde03" const contractSymbolSignature = "0x95d89b41" const contractDecimalsSignature = "0x313ce567" diff --git a/bchain/coins/eth/dataparser.go b/bchain/coins/eth/dataparser.go index 88043a9655..a67c947e01 100644 --- a/bchain/coins/eth/dataparser.go +++ b/bchain/coins/eth/dataparser.go @@ -163,16 +163,16 @@ func processParam(data string, index int, t *abi.Type, processed []bool) ([]stri } // get element count of dynamic type c, err := strconv.ParseInt(data[d:d+64], 16, 64) - count := int(c) if err != nil { return nil, 0, false } + count := int(c) processed[dynIndex] = true dynIndex++ if t.T == abi.StringTy || t.T == abi.BytesTy { d += 64 de := d + (count << 1) - if de > len(data) { + if de > len(data) || de < 0 { return nil, 0, false } if count == 0 { @@ -292,3 +292,27 @@ func ParseInputData(signatures *[]bchain.FourByteSignature, data string) *bchain } return &parsed } + +// getEnsRecord processes transaction log entry and tries to parse ENS record from it +func getEnsRecord(l *rpcLogWithTxHash) *bchain.AddressAliasRecord { + if len(l.Topics) == 3 && l.Topics[0] == nameRegisteredEventSignature && len(l.Data) >= 322 { + address, err := addressFromPaddedHex(l.Topics[2]) + if err != nil { + return nil + } + c, err := strconv.ParseInt(l.Data[194:194+64], 16, 64) + if err != nil { + return nil + } + de := 194 + 64 + (int(c) << 1) + if de > len(l.Data) || de < 0 { + return nil + } + b, err := hex.DecodeString(l.Data[194+64 : de]) + if err != nil { + return nil + } + return &bchain.AddressAliasRecord{Address: address, Name: string(b)} + } + return nil +} diff --git a/bchain/coins/eth/dataparser_test.go b/bchain/coins/eth/dataparser_test.go index 234dd8cd10..5aca77ec14 100644 --- a/bchain/coins/eth/dataparser_test.go +++ b/bchain/coins/eth/dataparser_test.go @@ -367,3 +367,79 @@ func TestParseInputData(t *testing.T) { }) } } + +func Test_getEnsRecord(t *testing.T) { + tests := []struct { + name string + log rpcLogWithTxHash + want *bchain.AddressAliasRecord + }{ + { + name: "unraveled", + log: rpcLogWithTxHash{ + RpcLog: bchain.RpcLog{ + Address: "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5", + Topics: []string{ + "0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621f71ce8f02b9f2a230097404f", + "0x40ce2aa8cd9ee9fef4bf3a68abab7fbcceb6bac89370518caf6a602cefe836bd", + "0x0000000000000000000000002c630b16aa53ae0189880e15c23323688acb607c", + }, + Data: "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000017629245f5a86f0000000000000000000000000000000000000000000000000000000069dbb21d0000000000000000000000000000000000000000000000000000000000000009756e726176656c65640000000000000000000000000000000000000000000000", + }, + }, + want: &bchain.AddressAliasRecord{Address: "0x2C630b16Aa53ae0189880e15C23323688acb607c", Name: "unraveled"}, + }, + { + name: "4x unraveled", + log: rpcLogWithTxHash{ + RpcLog: bchain.RpcLog{ + Address: "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5", + Topics: []string{ + "0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621f71ce8f02b9f2a230097404f", + "0x40ce2aa8cd9ee9fef4bf3a68abab7fbcceb6bac89370518caf6a602cefe836bd", + "0x0000000000000000000000002c630b16aa53ae0189880e15c23323688acb607c", + }, + Data: "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000017629245f5a86f0000000000000000000000000000000000000000000000000000000069dbb21d0000000000000000000000000000000000000000000000000000000000000024756e726176656c6564756e726176656c6564756e726176656c6564756e726176656c656400000000000000000000000000000000000000000000000000000000", + }, + }, + want: &bchain.AddressAliasRecord{Address: "0x2C630b16Aa53ae0189880e15C23323688acb607c", Name: "unraveledunraveledunraveledunraveled"}, + }, + { + name: "no signature", + log: rpcLogWithTxHash{ + RpcLog: bchain.RpcLog{ + Address: "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5", + Topics: []string{ + "0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621f71ce8f02b9f2a230097404e", + "0x40ce2aa8cd9ee9fef4bf3a68abab7fbcceb6bac89370518caf6a602cefe836bd", + "0x0000000000000000000000002c630b16aa53ae0189880e15c23323688acb607c", + }, + Data: "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000017629245f5a86f0000000000000000000000000000000000000000000000000000000069dbb21d0000000000000000000000000000000000000000000000000000000000000009756e726176656c65640000000000000000000000000000000000000000000000", + }, + }, + want: nil, + }, + { + name: "name length does not match", + log: rpcLogWithTxHash{ + RpcLog: bchain.RpcLog{ + Address: "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5", + Topics: []string{ + "0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621f71ce8f02b9f2a230097404f", + "0x40ce2aa8cd9ee9fef4bf3a68abab7fbcceb6bac89370518caf6a602cefe836bd", + "0x0000000000000000000000002c630b16aa53ae0189880e15c23323688acb607c", + }, + Data: "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000017629245f5a86f0000000000000000000000000000000000000000000000000000000069dbb21d0000000000000000000000000000000000000000000000000000000000000ff9756e726176656c65640000000000000000000000000000000000000000000000", + }, + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getEnsRecord(&tt.log); !reflect.DeepEqual(got, tt.want) { + t.Errorf("getEnsRecord() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 8c4630c236..7d5b6770bd 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -497,24 +497,28 @@ func (b *EthereumRPC) getBlockRaw(hash string, height uint32, fullTxs bool) (jso return raw, nil } -func (b *EthereumRPC) getTokenTransferEventsForBlock(blockNumber string) (map[string][]*bchain.RpcLog, error) { +func (b *EthereumRPC) processEventsForBlock(blockNumber string) (map[string][]*bchain.RpcLog, []bchain.AddressAliasRecord, error) { ctx, cancel := context.WithTimeout(context.Background(), b.timeout) defer cancel() var logs []rpcLogWithTxHash + var ensRecords []bchain.AddressAliasRecord err := b.rpc.CallContext(ctx, &logs, "eth_getLogs", map[string]interface{}{ "fromBlock": blockNumber, "toBlock": blockNumber, - // "topics": []string{tokenTransferEventSignature, tokenERC1155TransferSingleEventSignature, tokenERC1155TransferBatchEventSignature}, }) if err != nil { - return nil, errors.Annotatef(err, "blockNumber %v", blockNumber) + return nil, nil, errors.Annotatef(err, "blockNumber %v", blockNumber) } r := make(map[string][]*bchain.RpcLog) for i := range logs { l := &logs[i] r[l.Hash] = append(r[l.Hash], &l.RpcLog) + ens := getEnsRecord(l) + if ens != nil { + ensRecords = append(ensRecords, *ens) + } } - return r, nil + return r, ensRecords, nil } type rpcCallTrace struct { @@ -636,17 +640,24 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error if err != nil { return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) } - // get contract transfers events - logs, err := b.getTokenTransferEventsForBlock(head.Number) + // get block events + logs, ens, err := b.processEventsForBlock(head.Number) if err != nil { return nil, err } // error fetching internal data does not stop the block processing var blockSpecificData *bchain.EthereumBlockSpecificData internalData, err := b.getInternalDataForBlock(head.Hash, body.Transactions) - if err != nil { - blockSpecificData = &bchain.EthereumBlockSpecificData{InternalDataError: err.Error()} - glog.Info("InternalDataError ", bbh.Height, ": ", err.Error()) + if err != nil || len(ens) > 0 { + blockSpecificData = &bchain.EthereumBlockSpecificData{} + if err != nil { + blockSpecificData.InternalDataError = err.Error() + glog.Info("InternalDataError ", bbh.Height, ": ", err.Error()) + } + if len(ens) > 0 { + blockSpecificData.AddressAliasRecords = ens + glog.Info("ENS", ens) + } } btxs := make([]bchain.Tx, len(body.Transactions)) diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index a631532494..357f1667f4 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -121,12 +121,21 @@ type RpcReceipt struct { Logs []*RpcLog `json:"logs"` } +// EthereumSpecificData contains data specific to Ethereum transactions type EthereumSpecificData struct { Tx *RpcTransaction `json:"tx"` InternalData *EthereumInternalData `json:"internalData,omitempty"` Receipt *RpcReceipt `json:"receipt,omitempty"` } +// AddressAliasRecord maps address to ENS name +type AddressAliasRecord struct { + Address string + Name string +} + +// EthereumBlockSpecificData contain data specific for Ethereum block type EthereumBlockSpecificData struct { - InternalDataError string + InternalDataError string + AddressAliasRecords []AddressAliasRecord } From 8bdc3da694558732bbdf4fc9c2445b9a793de90a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 25 Apr 2022 00:16:04 +0200 Subject: [PATCH 056/530] Return address aliases from API --- api/types.go | 13 +- api/worker.go | 151 +++++++++++++----- api/xpub.go | 6 +- bchain/baseparser.go | 11 ++ bchain/coins/btc/bitcoinlikeparser.go | 1 + bchain/coins/btc/bitcoinrpc.go | 1 + bchain/coins/eth/contract_test.go | 2 +- bchain/coins/eth/ethparser.go | 8 +- bchain/coins/eth/ethparser_test.go | 6 +- bchain/coins/eth/ethrpc.go | 4 +- bchain/types.go | 4 + configs/coins/ethereum_archive.json | 1 + .../ethereum_testnet_ropsten_archive.json | 1 + db/bulkconnect.go | 4 +- db/rocksdb.go | 59 ++++++- db/rocksdb_ethereumtype.go | 15 +- db/rocksdb_ethereumtype_test.go | 26 ++- server/public_ethereumtype_test.go | 6 +- tests/dbtestdata/dbtestdata_ethereumtype.go | 17 ++ 19 files changed, 276 insertions(+), 60 deletions(-) diff --git a/api/types.go b/api/types.go index 545e58caab..f801d9f4ac 100644 --- a/api/types.go +++ b/api/types.go @@ -211,6 +211,12 @@ type EthereumSpecific struct { InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"` } +type AddressAlias struct { + Type string + Alias string +} +type AddressAliasesMap map[string]AddressAlias + // Tx holds information about a transaction type Tx struct { Txid string `json:"txid"` @@ -231,6 +237,7 @@ type Tx struct { CoinSpecificData json.RawMessage `json:"coinSpecificData,omitempty"` TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty"` EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty"` + AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` } // FeeStats contains detailed block fee statistics @@ -298,6 +305,7 @@ type Address struct { UsedTokens int `json:"usedTokens,omitempty"` Tokens []Token `json:"tokens,omitempty"` Erc20Contract *bchain.Erc20Contract `json:"erc20Contract,omitempty"` + AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` // helpers for explorer Filter string `json:"-"` XPubAddresses map[string]struct{} `json:"-"` @@ -428,8 +436,9 @@ type BlockInfo struct { type Block struct { Paging BlockInfo - TxCount int `json:"txCount"` - Transactions []*Tx `json:"txs,omitempty"` + TxCount int `json:"txCount"` + Transactions []*Tx `json:"txs,omitempty"` + AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` } // BlockRaw contains raw block in hex diff --git a/api/worker.go b/api/worker.go index a2ec91ee6c..4445d12c25 100644 --- a/api/worker.go +++ b/api/worker.go @@ -23,27 +23,29 @@ import ( // Worker is handle to api worker type Worker struct { - db *db.RocksDB - txCache *db.TxCache - chain bchain.BlockChain - chainParser bchain.BlockChainParser - chainType bchain.ChainType - mempool bchain.Mempool - is *common.InternalState - metrics *common.Metrics + db *db.RocksDB + txCache *db.TxCache + chain bchain.BlockChain + chainParser bchain.BlockChainParser + chainType bchain.ChainType + useAddressAliases bool + mempool bchain.Mempool + is *common.InternalState + metrics *common.Metrics } // NewWorker creates new api worker func NewWorker(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState) (*Worker, error) { w := &Worker{ - db: db, - txCache: txCache, - chain: chain, - chainParser: chain.GetChainParser(), - chainType: chain.GetChainParser().GetChainType(), - mempool: mempool, - is: is, - metrics: metrics, + db: db, + txCache: txCache, + chain: chain, + chainParser: chain.GetChainParser(), + chainType: chain.GetChainParser().GetChainType(), + useAddressAliases: chain.GetChainParser().UseAddressAliases(), + mempool: mempool, + is: is, + metrics: metrics, } if w.chainType == bchain.ChainBitcoinType { w.initXpubCache() @@ -100,7 +102,7 @@ func (w *Worker) setSpendingTxToVout(vout *Vout, txid string, height uint32) err // GetSpendingTxid returns transaction id of transaction that spent given output func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) { start := time.Now() - tx, err := w.GetTransaction(txid, false, false) + tx, err := w.getTransaction(txid, false, false, nil) if err != nil { return "", err } @@ -115,8 +117,65 @@ func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) { return tx.Vout[n].SpentTxID, nil } +func aggregateAddress(m map[string]struct{}, a string) { + if m != nil && len(a) > 0 { + m[a] = struct{}{} + } +} + +func aggregateAddresses(m map[string]struct{}, addresses []string, isAddress bool) { + if m != nil && isAddress { + for _, a := range addresses { + if len(a) > 0 { + m[a] = struct{}{} + } + } + } +} + +func (w *Worker) newAddressesMapForAliases() map[string]struct{} { + if w.useAddressAliases { + return make(map[string]struct{}) + } + return nil +} + +func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliasesMap { + if len(addresses) > 0 { + aliases := make(AddressAliasesMap) + var t string + if w.chainType == bchain.ChainEthereumType { + t = "ENS" + } else { + t = "Alias" + } + for a := range addresses { + if w.chainType == bchain.ChainEthereumType { + // TODO get contract name + } + n := w.db.GetAddressAlias(a) + if len(n) > 0 { + aliases[a] = AddressAlias{Type: t, Alias: n} + } + } + return aliases + } + return nil +} + // GetTransaction reads transaction data from txid func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) { + addresses := w.newAddressesMapForAliases() + tx, err := w.getTransaction(txid, spendingTxs, specificJSON, addresses) + if err != nil { + return nil, err + } + tx.AddressAliases = w.getAddressAliases(addresses) + return tx, nil +} + +// getTransaction reads transaction data from txid +func (w *Worker) getTransaction(txid string, spendingTxs bool, specificJSON bool, addresses map[string]struct{}) (*Tx, error) { bchainTx, height, err := w.txCache.GetTransaction(txid) if err != nil { if err == bchain.ErrTxNotFound { @@ -124,7 +183,7 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool } return nil, NewAPIError(fmt.Sprintf("Transaction '%v' not found (%v)", txid, err), true) } - return w.GetTransactionFromBchainTx(bchainTx, height, spendingTxs, specificJSON) + return w.getTransactionFromBchainTx(bchainTx, height, spendingTxs, specificJSON, addresses) } func (w *Worker) getParsedEthereumInputData(data string) *bchain.EthereumParsedInputData { @@ -144,8 +203,8 @@ func (w *Worker) getParsedEthereumInputData(data string) *bchain.EthereumParsedI return eth.ParseInputData(signatures, data) } -// GetTransactionFromBchainTx reads transaction data from txid -func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spendingTxs bool, specificJSON bool) (*Tx, error) { +// getTransactionFromBchainTx reads transaction data from txid +func (w *Worker) getTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spendingTxs bool, specificJSON bool, addresses map[string]struct{}) (*Tx, error) { var err error var ta *db.TxAddresses var tokens []TokenTransfer @@ -199,6 +258,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe if err != nil { glog.Warning("GetAddressesFromAddrDesc tx ", bchainVin.Txid, ", addrDesc ", vin.AddrDesc, ": ", err) } + aggregateAddresses(addresses, vin.Addresses, vin.IsAddress) continue } return nil, errors.Annotatef(err, "txCache.GetTransaction %v", bchainVin.Txid) @@ -215,6 +275,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe if err != nil { glog.Errorf("getAddressesFromVout error %v, vout %+v", err, vout) } + aggregateAddresses(addresses, vin.Addresses, vin.IsAddress) } } else { if len(tas.Outputs) > int(vin.Vout) { @@ -225,6 +286,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe if err != nil { glog.Errorf("output.Addresses error %v, tx %v, output %v", err, bchainVin.Txid, i) } + aggregateAddresses(addresses, vin.Addresses, vin.IsAddress) } } if vin.ValueSat != nil { @@ -239,6 +301,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe } vin.Addresses = bchainVin.Addresses vin.IsAddress = true + aggregateAddresses(addresses, vin.Addresses, vin.IsAddress) } } } @@ -254,6 +317,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe if err != nil { glog.V(2).Infof("getAddressesFromVout error %v, %v, output %v", err, bchainTx.Txid, bchainVout.N) } + aggregateAddresses(addresses, vout.Addresses, vout.IsAddress) if ta != nil { vout.Spent = ta.Outputs[i].Spent if spendingTxs && vout.Spent { @@ -276,7 +340,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe if err != nil { glog.Errorf("GetTokenTransfersFromTx error %v, %v", err, bchainTx) } - tokens = w.getEthereumTokensTransfers(tokenTransfers) + tokens = w.getEthereumTokensTransfers(tokenTransfers, addresses) ethTxData := eth.GetEthereumTxData(bchainTx) var internalData *bchain.EthereumInternalData @@ -314,7 +378,9 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe f := &internalData.Transfers[i] t := ðSpecific.InternalTransfers[i] t.From = f.From + aggregateAddress(addresses, t.From) t.To = f.To + aggregateAddress(addresses, t.To) t.Type = f.Type t.Value = (*Amount)(&f.Value) } @@ -365,6 +431,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, var pValInSat *big.Int var tokens []TokenTransfer var ethSpecific *EthereumSpecific + addresses := w.newAddressesMapForAliases() vins := make([]Vin, len(mempoolTx.Vin)) rbf := false for i := range mempoolTx.Vin { @@ -389,6 +456,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, if vin.ValueSat != nil { valInSat.Add(&valInSat, (*big.Int)(vin.ValueSat)) } + aggregateAddresses(addresses, vin.Addresses, vin.IsAddress) } } else if w.chainType == bchain.ChainEthereumType { if len(bchainVin.Addresses) > 0 { @@ -398,6 +466,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, } vin.Addresses = bchainVin.Addresses vin.IsAddress = true + aggregateAddresses(addresses, vin.Addresses, vin.IsAddress) } } } @@ -413,6 +482,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, if err != nil { glog.V(2).Infof("getAddressesFromVout error %v, %v, output %v", err, mempoolTx.Txid, bchainVout.N) } + aggregateAddresses(addresses, vout.Addresses, vout.IsAddress) } if w.chainType == bchain.ChainBitcoinType { // for coinbase transactions valIn is 0 @@ -425,7 +495,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, if len(mempoolTx.Vout) > 0 { valOutSat = mempoolTx.Vout[0].ValueSat } - tokens = w.getEthereumTokensTransfers(mempoolTx.TokenTransfers) + tokens = w.getEthereumTokensTransfers(mempoolTx.TokenTransfers, addresses) ethTxData := eth.GetEthereumTxDataFromSpecificData(mempoolTx.CoinSpecificData) ethSpecific = &EthereumSpecific{ GasLimit: ethTxData.GasLimit, @@ -450,11 +520,12 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, Vout: vouts, TokenTransfers: tokens, EthereumSpecific: ethSpecific, + AddressAliases: w.getAddressAliases(addresses), } return r, nil } -func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers) []TokenTransfer { +func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, addresses map[string]struct{}) []TokenTransfer { sort.Sort(transfers) tokens := make([]TokenTransfer, len(transfers)) for i := range transfers { @@ -482,6 +553,8 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers) []T } else { value = (*Amount)(&t.Value) } + aggregateAddress(addresses, t.From) + aggregateAddress(addresses, t.To) tokens[i] = TokenTransfer{ Type: TokenTypeMap[t.Type], Token: t.Contract, @@ -606,7 +679,7 @@ func GetUniqueTxids(txids []string) []string { return ut[0:i] } -func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockInfo, bestheight uint32) *Tx { +func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockInfo, bestheight uint32, addresses map[string]struct{}) *Tx { var err error var valInSat, valOutSat, feesSat big.Int vins := make([]Vin, len(ta.Inputs)) @@ -620,6 +693,7 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn if err != nil { glog.Errorf("tai.Addresses error %v, tx %v, input %v, tai %+v", err, txid, i, tai) } + aggregateAddresses(addresses, vin.Addresses, vin.IsAddress) } vouts := make([]Vout, len(ta.Outputs)) for i := range ta.Outputs { @@ -633,6 +707,7 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn glog.Errorf("tai.Addresses error %v, tx %v, output %v, tao %+v", err, txid, i, tao) } vout.Spent = tao.Spent + aggregateAddresses(addresses, vout.Addresses, vout.IsAddress) } // for coinbase transactions valIn is 0 feesSat.Sub(&valInSat, &valOutSat) @@ -876,7 +951,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto return ba, tokens, ci, n, nonContractTxs, internalTxs, totalResults, nil } -func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetails, blockInfo *db.BlockInfo) (*Tx, error) { +func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetails, blockInfo *db.BlockInfo, addresses map[string]struct{}) (*Tx, error) { var tx *Tx var err error // only ChainBitcoinType supports TxHistoryLight @@ -888,9 +963,9 @@ func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetail if ta == nil { glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses") // as fallback, get tx from backend - tx, err = w.GetTransaction(txid, false, false) + tx, err = w.getTransaction(txid, false, false, addresses) if err != nil { - return nil, errors.Annotatef(err, "GetTransaction %v", txid) + return nil, errors.Annotatef(err, "getTransaction %v", txid) } } else { if blockInfo == nil { @@ -904,12 +979,12 @@ func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetail blockInfo = &db.BlockInfo{} } } - tx = w.txFromTxAddress(txid, ta, blockInfo, bestheight) + tx = w.txFromTxAddress(txid, ta, blockInfo, bestheight, addresses) } } else { - tx, err = w.GetTransaction(txid, false, false) + tx, err = w.getTransaction(txid, false, false, addresses) if err != nil { - return nil, errors.Annotatef(err, "GetTransaction %v", txid) + return nil, errors.Annotatef(err, "getTransaction %v", txid) } } return tx, nil @@ -1012,6 +1087,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco ba = &db.AddrBalance{} page = 0 } + addresses := w.newAddressesMapForAliases() // process mempool, only if toHeight is not specified if filter.ToHeight == 0 && !filter.OnlyConfirmed { txm, err = w.getAddressTxids(addrDesc, true, filter, maxInt) @@ -1019,7 +1095,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco return nil, errors.Annotatef(err, "getAddressTxids %v true", addrDesc) } for _, txid := range txm { - tx, err := w.GetTransaction(txid, false, true) + tx, err := w.getTransaction(txid, false, true, addresses) // mempool transaction may fail if err != nil || tx == nil { glog.Warning("GetTransaction in mempool: ", err) @@ -1070,7 +1146,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco if option == AccountDetailsTxidHistory { txids = append(txids, txid) } else { - tx, err := w.txFromTxid(txid, bestheight, option, nil) + tx, err := w.txFromTxid(txid, bestheight, option, nil, addresses) if err != nil { return nil, err } @@ -1099,6 +1175,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco Tokens: tokens, Erc20Contract: erc20c, Nonce: nonce, + AddressAliases: w.getAddressAliases(addresses), } glog.Info("GetAddress ", address, ", ", time.Since(start)) return r, nil @@ -1786,8 +1863,9 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) { pg, from, to, page := computePaging(txCount, page, txsOnPage) txs := make([]*Tx, to-from) txi := 0 + addresses := w.newAddressesMapForAliases() for i := from; i < to; i++ { - txs[txi], err = w.txFromTxid(bi.Txids[i], bestheight, AccountDetailsTxHistoryLight, dbi) + txs[txi], err = w.txFromTxid(bi.Txids[i], bestheight, AccountDetailsTxHistoryLight, dbi, addresses) if err != nil { return nil, err } @@ -1819,8 +1897,9 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) { Txids: bi.Txids, Version: bi.Version, }, - TxCount: txCount, - Transactions: txs, + TxCount: txCount, + Transactions: txs, + AddressAliases: w.getAddressAliases(addresses), }, nil } @@ -1875,7 +1954,7 @@ func (w *Worker) ComputeFeeStats(blockFrom, blockTo int, stopCompute chan os.Sig glog.Info("ComputeFeeStats interrupted at height ", block) return db.ErrOperationInterrupted default: - tx, err := w.txFromTxid(txid, bestheight, AccountDetailsTxHistoryLight, dbi) + tx, err := w.txFromTxid(txid, bestheight, AccountDetailsTxHistoryLight, dbi, nil) if err != nil { return err } diff --git a/api/xpub.go b/api/xpub.go index b5af25d3a1..8737373bd6 100644 --- a/api/xpub.go +++ b/api/xpub.go @@ -437,6 +437,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc } filtered = true } + addresses := w.newAddressesMapForAliases() // process mempool, only if ToHeight is not specified if filter.ToHeight == 0 && !filter.OnlyConfirmed { txmMap = make(map[string]*Tx) @@ -452,7 +453,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc // the same tx can have multiple addresses from the same xpub, get it from backend it only once tx, foundTx := txmMap[txid.txid] if !foundTx { - tx, err = w.GetTransaction(txid.txid, false, true) + tx, err = w.getTransaction(txid.txid, false, true, addresses) // mempool transaction may fail if err != nil || tx == nil { glog.Warning("GetTransaction in mempool: ", err) @@ -529,7 +530,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc if option == AccountDetailsTxidHistory { txids = append(txids, xpubTxid.txid) } else { - tx, err := w.txFromTxid(xpubTxid.txid, bestheight, option, nil) + tx, err := w.txFromTxid(xpubTxid.txid, bestheight, option, nil, addresses) if err != nil { return nil, err } @@ -580,6 +581,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc UsedTokens: usedTokens, Tokens: tokens, XPubAddresses: xpubAddresses, + AddressAliases: w.getAddressAliases(addresses), } glog.Info("GetXpubAddress ", xpub[:xpubLogPrefix], ", cache ", inCache, ", ", txCount, " txs, ", time.Since(start)) return &addr, nil diff --git a/bchain/baseparser.go b/bchain/baseparser.go index 3f342a5327..e45c8f9ec4 100644 --- a/bchain/baseparser.go +++ b/bchain/baseparser.go @@ -16,6 +16,7 @@ import ( type BaseParser struct { BlockAddressesToKeep int AmountDecimalPoint int + AddressAliases bool } // ParseBlock parses raw block to our Block struct - currently not implemented @@ -103,6 +104,11 @@ func (p *BaseParser) AmountDecimals() int { return p.AmountDecimalPoint } +// UseAddressAliases returns true if address aliases are enabled +func (p *BaseParser) UseAddressAliases() bool { + return p.AddressAliases +} + // ParseTxFromJson parses JSON message containing transaction and returns Tx struct func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) { var tx Tx @@ -304,3 +310,8 @@ func (p *BaseParser) DeriveAddressDescriptorsFromTo(descriptor *XpubDescriptor, func (p *BaseParser) EthereumTypeGetTokenTransfersFromTx(tx *Tx) (TokenTransfers, error) { return nil, errors.New("Not supported") } + +// FormatAddressAlias makes possible to do coin specific formatting to an address alias +func (p *BaseParser) FormatAddressAlias(address string, name string) string { + return name +} diff --git a/bchain/coins/btc/bitcoinlikeparser.go b/bchain/coins/btc/bitcoinlikeparser.go index 3f168239b9..716877ccc7 100644 --- a/bchain/coins/btc/bitcoinlikeparser.go +++ b/bchain/coins/btc/bitcoinlikeparser.go @@ -44,6 +44,7 @@ func NewBitcoinLikeParser(params *chaincfg.Params, c *Configuration) *BitcoinLik BaseParser: &bchain.BaseParser{ BlockAddressesToKeep: c.BlockAddressesToKeep, AmountDecimalPoint: 8, + AddressAliases: c.AddressAliases, }, Params: params, XPubMagic: c.XPubMagic, diff --git a/bchain/coins/btc/bitcoinrpc.go b/bchain/coins/btc/bitcoinrpc.go index faac252877..b2618a0364 100644 --- a/bchain/coins/btc/bitcoinrpc.go +++ b/bchain/coins/btc/bitcoinrpc.go @@ -43,6 +43,7 @@ type Configuration struct { RPCUser string `json:"rpc_user"` RPCPass string `json:"rpc_pass"` RPCTimeout int `json:"rpc_timeout"` + AddressAliases bool `json:"address_aliases,omitempty"` Parse bool `json:"parse"` MessageQueueBinding string `json:"message_queue_binding"` Subversion string `json:"subversion"` diff --git a/bchain/coins/eth/contract_test.go b/bchain/coins/eth/contract_test.go index 2efa1eaef8..7d609a0a95 100644 --- a/bchain/coins/eth/contract_test.go +++ b/bchain/coins/eth/contract_test.go @@ -228,7 +228,7 @@ func Test_contractGetTransfersFromLog(t *testing.T) { } func Test_contractGetTransfersFromTx(t *testing.T) { - p := NewEthereumParser(1) + p := NewEthereumParser(1, false) b1 := dbtestdata.GetTestEthereumTypeBlock1(p) b2 := dbtestdata.GetTestEthereumTypeBlock2(p) bn, _ := new(big.Int).SetString("21e19e0c9bab2400000", 16) diff --git a/bchain/coins/eth/ethparser.go b/bchain/coins/eth/ethparser.go index 92ed0054ba..975b837494 100644 --- a/bchain/coins/eth/ethparser.go +++ b/bchain/coins/eth/ethparser.go @@ -28,10 +28,11 @@ type EthereumParser struct { } // NewEthereumParser returns new EthereumParser instance -func NewEthereumParser(b int) *EthereumParser { +func NewEthereumParser(b int, addressAliases bool) *EthereumParser { return &EthereumParser{&bchain.BaseParser{ BlockAddressesToKeep: b, AmountDecimalPoint: EtherAmountDecimalPoint, + AddressAliases: addressAliases, }} } @@ -458,6 +459,11 @@ func (p *EthereumParser) EthereumTypeGetTokenTransfersFromTx(tx *bchain.Tx) (bch return r, nil } +// FormatAddressAlias adds .eth to a name alias +func (p *EthereumParser) FormatAddressAlias(address string, name string) string { + return name + ".eth" +} + // TxStatus is status of transaction type TxStatus int diff --git a/bchain/coins/eth/ethparser_test.go b/bchain/coins/eth/ethparser_test.go index ac54e6b3a4..aaee177ae6 100644 --- a/bchain/coins/eth/ethparser_test.go +++ b/bchain/coins/eth/ethparser_test.go @@ -54,7 +54,7 @@ func TestEthParser_GetAddrDescFromAddress(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - p := NewEthereumParser(1) + p := NewEthereumParser(1, false) got, err := p.GetAddrDescFromAddress(tt.args.address) if (err != nil) != tt.wantErr { t.Errorf("EthParser.GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr) @@ -285,7 +285,7 @@ func TestEthereumParser_PackTx(t *testing.T) { want: dbtestdata.EthTx1NoStatusPacked, }, } - p := NewEthereumParser(1) + p := NewEthereumParser(1, false) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := p.PackTx(tt.args.tx, tt.args.height, tt.args.blockTime) @@ -338,7 +338,7 @@ func TestEthereumParser_UnpackTx(t *testing.T) { want1: 4321000, }, } - p := NewEthereumParser(1) + p := NewEthereumParser(1, false) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { b, err := hex.DecodeString(tt.args.hex) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 7d5b6770bd..092c835ba8 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -41,6 +41,7 @@ type Configuration struct { RPCURL string `json:"rpc_url"` RPCTimeout int `json:"rpc_timeout"` BlockAddressesToKeep int `json:"block_addresses_to_keep"` + AddressAliases bool `json:"address_aliases,omitempty"` MempoolTxTimeoutHours int `json:"mempoolTxTimeoutHours"` QueryBackendOnMempoolResync bool `json:"queryBackendOnMempoolResync"` ProcessInternalTransactions bool `json:"processInternalTransactions"` @@ -97,7 +98,7 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification ProcessInternalTransactions = c.ProcessInternalTransactions // always create parser - s.Parser = NewEthereumParser(c.BlockAddressesToKeep) + s.Parser = NewEthereumParser(c.BlockAddressesToKeep, c.AddressAliases) s.timeout = time.Duration(c.RPCTimeout) * time.Second // new blocks notifications handling @@ -648,6 +649,7 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error // error fetching internal data does not stop the block processing var blockSpecificData *bchain.EthereumBlockSpecificData internalData, err := b.getInternalDataForBlock(head.Hash, body.Transactions) + // pass internalData error and ENS records in blockSpecificData to be stored if err != nil || len(ens) > 0 { blockSpecificData = &bchain.EthereumBlockSpecificData{} if err != nil { diff --git a/bchain/types.go b/bchain/types.go index d2fe44c484..2f40dfe446 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -305,6 +305,8 @@ type BlockChainParser interface { KeepBlockAddresses() int // AmountDecimals returns number of decimal places in coin amounts AmountDecimals() int + // UseAddressAliases returns true if address aliases are enabled + UseAddressAliases() bool // MinimumCoinbaseConfirmations returns minimum number of confirmations a coinbase transaction must have before it can be spent MinimumCoinbaseConfirmations() int // AmountToDecimalString converts amount in big.Int to string with decimal point in the correct place @@ -338,6 +340,8 @@ type BlockChainParser interface { DeriveAddressDescriptorsFromTo(descriptor *XpubDescriptor, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error) // EthereumType specific EthereumTypeGetTokenTransfersFromTx(tx *Tx) (TokenTransfers, error) + // AddressAlias + FormatAddressAlias(address string, name string) string } // Mempool defines common interface to mempool diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 4f1d3cfcaf..313d800d17 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -50,6 +50,7 @@ "mempool_sub_workers": 2, "block_addresses_to_keep": 300, "additional_params": { + "address_aliases": true, "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index 0e3692bb39..35bf48aa65 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -49,6 +49,7 @@ "mempool_sub_workers": 2, "block_addresses_to_keep": 3000, "additional_params": { + "address_aliases": true, "mempoolTxTimeoutHours": 12, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, diff --git a/db/bulkconnect.go b/db/bulkconnect.go index 0e1183a17a..0bd7ac6221 100644 --- a/db/bulkconnect.go +++ b/db/bulkconnect.go @@ -330,9 +330,9 @@ func (b *BulkConnect) connectBlockEthereumType(block *bchain.Block, storeBlockTx glog.Info("rocksdb: height ", b.height, ", stored ", bac, " addresses, done in ", time.Since(start)) } } else { - // if there is InternalDataError, store it + // if there are blockSpecificData, store them blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) - if blockSpecificData != nil && blockSpecificData.InternalDataError != "" { + if blockSpecificData != nil { wb := gorocksdb.NewWriteBatch() defer wb.Destroy() if err = b.d.storeBlockSpecificDataEthereumType(wb, block); err != nil { diff --git a/db/rocksdb.go b/db/rocksdb.go index 591570132e..ad02a0de9b 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -10,6 +10,7 @@ import ( "path/filepath" "sort" "strconv" + "sync" "time" "unsafe" @@ -90,6 +91,9 @@ const ( cfContracts cfFunctionSignatures cfBlockInternalDataErrors + + // TODO move to common section + cfAddressAliases ) // common columns @@ -98,7 +102,7 @@ var cfBaseNames = []string{"default", "height", "addresses", "blockTxs", "transa // type specific columns var cfNamesBitcoinType = []string{"addressBalance", "txAddresses"} -var cfNamesEthereumType = []string{"addressContracts", "internalData", "contracts", "functionSignatures", "blockInternalDataErrors"} +var cfNamesEthereumType = []string{"addressContracts", "internalData", "contracts", "functionSignatures", "blockInternalDataErrors", "addressAliases"} func openDB(path string, c *gorocksdb.Cache, openFiles int) (*gorocksdb.DB, []*gorocksdb.ColumnFamilyHandle, error) { // opts with bloom filter @@ -1248,6 +1252,50 @@ func (d *RocksDB) writeHeight(wb *gorocksdb.WriteBatch, height uint32, bi *Block return nil } +// address alias support +var cachedAddressAliasRecords = make(map[string]string) +var cachedAddressAliasRecordsMux sync.Mutex + +// InitAddressAliasRecords loads all records to cache +func (d *RocksDB) InitAddressAliasRecords() (int, error) { + count := 0 + cachedAddressAliasRecordsMux.Lock() + defer cachedAddressAliasRecordsMux.Unlock() + it := d.db.NewIteratorCF(d.ro, d.cfh[cfAddressAliases]) + defer it.Close() + for it.SeekToFirst(); it.Valid(); it.Next() { + address := string(it.Key().Data()) + name := string(it.Value().Data()) + if address != "" && name != "" { + cachedAddressAliasRecords[address] = d.chainParser.FormatAddressAlias(address, name) + count++ + } + } + return count, nil +} + +func (d *RocksDB) GetAddressAlias(address string) string { + cachedAddressAliasRecordsMux.Lock() + name := cachedAddressAliasRecords[address] + cachedAddressAliasRecordsMux.Unlock() + return name +} + +func (d *RocksDB) storeAddressAliasRecords(wb *gorocksdb.WriteBatch, records []bchain.AddressAliasRecord) error { + if d.chainParser.UseAddressAliases() { + for i := range records { + r := &records[i] + if len(r.Name) > 0 { + wb.PutCF(d.cfh[cfAddressAliases], []byte(r.Address), []byte(r.Name)) + cachedAddressAliasRecordsMux.Lock() + cachedAddressAliasRecords[r.Address] = d.chainParser.FormatAddressAlias(r.Address, r.Name) + cachedAddressAliasRecordsMux.Unlock() + } + } + } + return nil +} + // Disconnect blocks func (d *RocksDB) disconnectTxAddressesInputs(wb *gorocksdb.WriteBatch, btxID []byte, inputs []outpoint, txa *TxAddresses, txAddressesToUpdate map[string]*TxAddresses, @@ -1642,6 +1690,15 @@ func (d *RocksDB) LoadInternalState(rpcCoin string) (*common.InternalState, erro var t time.Time is.LastMempoolSync = t is.SyncMode = false + + if d.chainParser.UseAddressAliases() { + recordsCount, err := d.InitAddressAliasRecords() + if err != nil { + return nil, err + } + glog.Infof("loaded %d address alias records", recordsCount) + } + return is, nil } diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index 3310afe347..0f7f95f13f 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -761,10 +761,17 @@ func (d *RocksDB) storeBlockInternalDataErrorEthereumType(wb *gorocksdb.WriteBat func (d *RocksDB) storeBlockSpecificDataEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block) error { blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) - if blockSpecificData != nil && blockSpecificData.InternalDataError != "" { - glog.Info("storeBlockSpecificDataEthereumType ", block.Height, ": ", blockSpecificData.InternalDataError) - if err := d.storeBlockInternalDataErrorEthereumType(wb, block, blockSpecificData.InternalDataError); err != nil { - return err + if blockSpecificData != nil { + if blockSpecificData.InternalDataError != "" { + glog.Info("storeBlockSpecificDataEthereumType ", block.Height, ": ", blockSpecificData.InternalDataError) + if err := d.storeBlockInternalDataErrorEthereumType(wb, block, blockSpecificData.InternalDataError); err != nil { + return err + } + } + if len(blockSpecificData.AddressAliasRecords) > 0 { + if err := d.storeAddressAliasRecords(wb, blockSpecificData.AddressAliasRecords); err != nil { + return err + } } } return nil diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index a61fc958a0..6631df9f2d 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -21,7 +21,7 @@ type testEthereumParser struct { } func ethereumTestnetParser() *eth.EthereumParser { - return eth.NewEthereumParser(1) + return eth.NewEthereumParser(1, true) } func bigintFromStringToHex(s string) string { @@ -267,6 +267,25 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa } } + var addressAliases []keyPair + addressAliases = []keyPair{ + { + hex.EncodeToString([]byte(dbtestdata.EthAddr7bEIP55)), + hex.EncodeToString([]byte("address7b")), + nil, + }, + { + hex.EncodeToString([]byte(dbtestdata.EthAddr20EIP55)), + hex.EncodeToString([]byte("address20")), + nil, + }, + } + if err := checkColumn(d, cfAddressAliases, addressAliases); err != nil { + { + t.Fatal(err) + } + } + var internalDataError []keyPair if wantBlockInternalDataError { internalDataError = []keyPair{ @@ -282,6 +301,7 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa t.Fatal(err) } } + } func formatInternalData(in *bchain.EthereumInternalData) *bchain.EthereumInternalData { @@ -359,9 +379,8 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) } - // connect 2nd block, simulate InternalDataError + // connect 2nd block, simulate InternalDataError and AddressAlias block2 := dbtestdata.GetTestEthereumTypeBlock2(d.chainParser) - block2.CoinSpecificData = &bchain.EthereumBlockSpecificData{InternalDataError: "test error"} if err := d.ConnectBlock(block2); err != nil { t.Fatal(err) } @@ -544,7 +563,6 @@ func Test_BulkConnect_EthereumType(t *testing.T) { // connect 2nd block, simulate InternalDataError block2 := dbtestdata.GetTestEthereumTypeBlock2(d.chainParser) - block2.CoinSpecificData = &bchain.EthereumBlockSpecificData{InternalDataError: "test error"} if err := bc.ConnectBlock(block2, true); err != nil { t.Fatal(err) } diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 27a6009da2..bd4461c6a3 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -73,7 +73,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":18,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"erc20Contract":{"contract":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","name":"Contract 123","symbol":"S123","decimals":18}}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":18,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"erc20Contract":{"contract":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","name":"Contract 123","symbol":"S123","decimals":18},"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"}}}`, }, }, { @@ -82,7 +82,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}}}`, + `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}},"addressAliases":{"0x20cD153de35D469BA46127A0C8F18626b59a256A":{"Type":"ENS","Alias":"address20.eth"}}}`, }, }, } @@ -104,7 +104,7 @@ func initEthereumTypeDB(d *db.RocksDB) error { } func Test_PublicServer_EthereumType(t *testing.T) { - parser := eth.NewEthereumParser(1) + parser := eth.NewEthereumParser(1, true) chain, err := dbtestdata.NewFakeBlockChainEthereumType(parser) if err != nil { glog.Fatal("fakechain: ", err) diff --git a/tests/dbtestdata/dbtestdata_ethereumtype.go b/tests/dbtestdata/dbtestdata_ethereumtype.go index 03e72f56e7..e34012bd0d 100644 --- a/tests/dbtestdata/dbtestdata_ethereumtype.go +++ b/tests/dbtestdata/dbtestdata_ethereumtype.go @@ -13,9 +13,11 @@ const ( EthAddr3e = "3e3a3d69dc66ba10737f531ed088954a9ec89d97" EthAddr55 = "555ee11fbddc0e49a9bab358a8941ad95ffdb48f" EthAddr20 = "20cd153de35d469ba46127a0c8f18626b59a256a" + EthAddr20EIP55 = "0x20cD153de35D469BA46127A0C8F18626b59a256A" EthAddr9f = "9f4981531fda132e83c44680787dfa7ee31e4f8d" EthAddr4b = "4bda106325c335df99eab7fe363cac8a0ba2a24d" EthAddr7b = "7b62eb7fe80350dc7ec945c0b73242cb9877fb1b" + EthAddr7bEIP55 = "0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b" EthAddr83 = "837e3f699d85a4b0b99894567e9233dfb1dcb081" EthAddrA3 = "a3950b823cb063dd9afc0d27f35008b805b3ed53" EthAddr5d = "5dc6288b35e0807a3d6feb89b3a2ff4ab773168e" @@ -126,6 +128,20 @@ var EthTx4InternalData = &bchain.EthereumInternalData{ }, } +var Block2SpecificData = &bchain.EthereumBlockSpecificData{ + InternalDataError: "test error", + AddressAliasRecords: []bchain.AddressAliasRecord{ + { + Address: EthAddr7bEIP55, + Name: "address7b", + }, + { + Address: EthAddr20EIP55, + Name: "address20", + }, + }, +} + type packedAndInternal struct { packed string internal *bchain.EthereumInternalData @@ -194,5 +210,6 @@ func GetTestEthereumTypeBlock2(parser bchain.BlockChainParser) *bchain.Block { }, { packed: EthTx8Packed, }}, parser), + CoinSpecificData: Block2SpecificData, } } From 2507e12057934bc7e330d3e9aff38996da9d5103 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 26 Apr 2022 00:35:52 +0200 Subject: [PATCH 057/530] Fix issue in eth disconnect block --- db/rocksdb_ethereumtype.go | 6 ++++-- static/templates/address.html | 10 ++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index 0f7f95f13f..13457c7c06 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -1025,8 +1025,10 @@ func (d *RocksDB) disconnectBlockTxsEthereumType(wb *gorocksdb.WriteBatch, heigh if err := d.disconnectAddress(blockTx.btxID, false, c.from, c, addresses, contracts); err != nil { return err } - if err := d.disconnectAddress(blockTx.btxID, false, c.to, c, addresses, contracts); err != nil { - return err + if !bytes.Equal(c.from, c.to) { + if err := d.disconnectAddress(blockTx.btxID, false, c.to, c, addresses, contracts); err != nil { + return err + } } } wb.DeleteCF(d.cfh[cfTransactions], blockTx.btxID) diff --git a/static/templates/address.html b/static/templates/address.html index 730355aebb..c662ff12aa 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -71,10 +71,7 @@

Confirmed

{{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} - {{range $i, $iv := $t.Ids}} - {{if $i}}, {{end}} - {{formatAmountWithDecimals $iv 0}} - {{end}} + {{range $i, $iv := $t.Ids}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv 0}}{{end}} {{$t.Transfers}} @@ -101,10 +98,7 @@

Confirmed

{{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} - {{range $i, $iv := $t.IdValues}} - {{if $i}}, {{end}} - {{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$t.Symbol}} - {{end}} + {{range $i, $iv := $t.IdValues}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$t.Symbol}}{{end}} {{$t.Transfers}} From db91824dc36d0af1517c8553294cf33423d5b483 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 1 May 2022 02:41:56 +0200 Subject: [PATCH 058/530] Store contract info in DB --- api/types.go | 98 ++++------ api/worker.go | 131 +++++++------ api/xpub.go | 2 +- bchain/basechain.go | 4 +- bchain/coins/blockchain.go | 8 +- bchain/coins/eth/contract.go | 123 ++++++------ bchain/coins/eth/contract_test.go | 20 +- bchain/coins/eth/dataparser.go | 4 +- bchain/coins/eth/dataparser_test.go | 5 + bchain/coins/eth/ethrpc.go | 46 +++-- bchain/types.go | 23 ++- bchain/types_ethereum_type.go | 53 ++--- db/rocksdb_ethereumtype.go | 187 ++++++++++++++---- db/rocksdb_ethereumtype_test.go | 204 ++++++++++++++------ server/public.go | 4 +- server/public_ethereumtype_test.go | 12 +- server/websocket.go | 2 +- static/templates/address.html | 4 +- static/templates/txdetail_ethereumtype.html | 2 +- tests/dbtestdata/dbtestdata_ethereumtype.go | 20 ++ tests/dbtestdata/fakechain_ethereumtype.go | 14 +- 21 files changed, 611 insertions(+), 355 deletions(-) diff --git a/api/types.go b/api/types.go index f801d9f4ac..c8241e3679 100644 --- a/api/types.go +++ b/api/types.go @@ -135,58 +135,40 @@ type Vout struct { Type string `json:"type,omitempty"` } -// TokenType specifies type of token -type TokenType string - -// Token types -const ( - // Ethereum token types - ERC20TokenType TokenType = "ERC20" - ERC771TokenType TokenType = "ERC721" - ERC1155TokenType TokenType = "ERC1155" - - // XPUBAddressTokenType is address derived from xpub - XPUBAddressTokenType TokenType = "XPUBAddress" -) - -// TokenTypeMap maps bchain.TokenTransferType to TokenType -// the map must match all bchain.TokenTransferTypes to avoid index out of range panic -var TokenTypeMap []TokenType = []TokenType{ERC20TokenType, ERC771TokenType, ERC1155TokenType} - -// TokenTransferValues contains values for ERC1155 contract -type TokenTransferValues struct { +// MultiTokenValue contains values for contract with id and value (like ERC1155) +type MultiTokenValue struct { Id *Amount `json:"id,omitempty"` Value *Amount `json:"value,omitempty"` } // Token contains info about tokens held by an address type Token struct { - Type TokenType `json:"type"` - Name string `json:"name"` - Path string `json:"path,omitempty"` - Contract string `json:"contract,omitempty"` - Transfers int `json:"transfers"` - Symbol string `json:"symbol,omitempty"` - Decimals int `json:"decimals,omitempty"` - BalanceSat *Amount `json:"balance,omitempty"` - Ids []Amount `json:"ids,omitempty"` // multiple ERC721 tokens - IdValues []TokenTransferValues `json:"idValues,omitempty"` // multiple ERC1155 tokens - TotalReceivedSat *Amount `json:"totalReceived,omitempty"` - TotalSentSat *Amount `json:"totalSent,omitempty"` - ContractIndex string `json:"-"` + Type bchain.TokenTypeName `json:"type"` + Name string `json:"name"` + Path string `json:"path,omitempty"` + Contract string `json:"contract,omitempty"` + Transfers int `json:"transfers"` + Symbol string `json:"symbol,omitempty"` + Decimals int `json:"decimals,omitempty"` + BalanceSat *Amount `json:"balance,omitempty"` + Ids []Amount `json:"ids,omitempty"` // multiple ERC721 tokens + MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` // multiple ERC1155 tokens + TotalReceivedSat *Amount `json:"totalReceived,omitempty"` + TotalSentSat *Amount `json:"totalSent,omitempty"` + ContractIndex string `json:"-"` } // TokenTransfer contains info about a token transfer done in a transaction type TokenTransfer struct { - Type TokenType `json:"type"` - From string `json:"from"` - To string `json:"to"` - Token string `json:"token"` - Name string `json:"name"` - Symbol string `json:"symbol"` - Decimals int `json:"decimals"` - Value *Amount `json:"value,omitempty"` - Values []TokenTransferValues `json:"values,omitempty"` + Type bchain.TokenTypeName `json:"type"` + From string `json:"from"` + To string `json:"to"` + Token string `json:"token"` + Name string `json:"name"` + Symbol string `json:"symbol"` + Decimals int `json:"decimals"` + Value *Amount `json:"value,omitempty"` + MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` } type EthereumInternalTransfer struct { @@ -290,22 +272,22 @@ type AddressFilter struct { // Address holds information about address and its transactions type Address struct { Paging - AddrStr string `json:"address"` - BalanceSat *Amount `json:"balance"` - TotalReceivedSat *Amount `json:"totalReceived,omitempty"` - TotalSentSat *Amount `json:"totalSent,omitempty"` - UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance"` - UnconfirmedTxs int `json:"unconfirmedTxs"` - Txs int `json:"txs"` - NonTokenTxs int `json:"nonTokenTxs,omitempty"` - InternalTxs int `json:"internalTxs,omitempty"` - Transactions []*Tx `json:"transactions,omitempty"` - Txids []string `json:"txids,omitempty"` - Nonce string `json:"nonce,omitempty"` - UsedTokens int `json:"usedTokens,omitempty"` - Tokens []Token `json:"tokens,omitempty"` - Erc20Contract *bchain.Erc20Contract `json:"erc20Contract,omitempty"` - AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` + AddrStr string `json:"address"` + BalanceSat *Amount `json:"balance"` + TotalReceivedSat *Amount `json:"totalReceived,omitempty"` + TotalSentSat *Amount `json:"totalSent,omitempty"` + UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance"` + UnconfirmedTxs int `json:"unconfirmedTxs"` + Txs int `json:"txs"` + NonTokenTxs int `json:"nonTokenTxs,omitempty"` + InternalTxs int `json:"internalTxs,omitempty"` + Transactions []*Tx `json:"transactions,omitempty"` + Txids []string `json:"txids,omitempty"` + Nonce string `json:"nonce,omitempty"` + UsedTokens int `json:"usedTokens,omitempty"` + Tokens []Token `json:"tokens,omitempty"` + ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"` + AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` // helpers for explorer Filter string `json:"-"` XPubAddresses map[string]struct{} `json:"-"` diff --git a/api/worker.go b/api/worker.go index 4445d12c25..0fad5f2be9 100644 --- a/api/worker.go +++ b/api/worker.go @@ -151,7 +151,10 @@ func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliases } for a := range addresses { if w.chainType == bchain.ChainEthereumType { - // TODO get contract name + ci, err := w.db.GetContractInfoForAddress(a) + if err == nil && ci != nil && ci.Name != "" { + aliases[a] = AddressAlias{Type: "Contract", Alias: ci.Name} + } } n := w.db.GetAddressAlias(a) if len(n) > 0 { @@ -535,20 +538,28 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add glog.Errorf("GetAddrDescFromAddress error %v, contract %v", err, t.Contract) continue } - erc20c, err := w.chain.EthereumTypeGetErc20ContractInfo(cd) + typeName := bchain.EthereumTokenTypeMap[t.Type] + contractInfo, err := w.db.GetContractInfo(cd, typeName) if err != nil { - glog.Errorf("GetErc20ContractInfo error %v, contract %v", err, t.Contract) + glog.Errorf("GetContractInfo error %v, contract %v", err, t.Contract) } - if erc20c == nil { - erc20c = &bchain.Erc20Contract{Name: t.Contract} + if contractInfo == nil { + glog.Warningf("Contract %v %v not found in DB", t.Contract, typeName) + contractInfo, err = w.chain.GetContractInfo(cd) + if err != nil { + glog.Errorf("GetContractInfo from chain error %v, contract %v", err, t.Contract) + } + if contractInfo == nil { + contractInfo = &bchain.ContractInfo{Name: t.Contract, Type: bchain.UnknownTokenType} + } } var value *Amount - var values []TokenTransferValues - if t.Type == bchain.ERC1155 { - values = make([]TokenTransferValues, len(t.IdValues)) + var values []MultiTokenValue + if t.Type == bchain.MultiToken { + values = make([]MultiTokenValue, len(t.MultiTokenValues)) for j := range values { - values[j].Id = (*Amount)(&t.IdValues[j].Id) - values[j].Value = (*Amount)(&t.IdValues[j].Value) + values[j].Id = (*Amount)(&t.MultiTokenValues[j].Id) + values[j].Value = (*Amount)(&t.MultiTokenValues[j].Value) } } else { value = (*Amount)(&t.Value) @@ -556,15 +567,15 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add aggregateAddress(addresses, t.From) aggregateAddress(addresses, t.To) tokens[i] = TokenTransfer{ - Type: TokenTypeMap[t.Type], - Token: t.Contract, - From: t.From, - To: t.To, - Value: value, - Values: values, - Decimals: erc20c.Decimals, - Name: erc20c.Name, - Symbol: erc20c.Symbol, + Type: typeName, + Token: t.Contract, + From: t.From, + To: t.To, + Value: value, + MultiTokenValues: values, + Decimals: contractInfo.Decimals, + Name: contractInfo.Name, + Symbol: contractInfo.Symbol, } } return tokens @@ -751,35 +762,41 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) { } func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails) (*Token, error) { - // TODO use db.contracts validContract := true - - ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract) + typeName := bchain.EthereumTokenTypeMap[c.Type] + ci, err := w.db.GetContractInfo(c.Contract, typeName) if err != nil { - return nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract) + return nil, errors.Annotatef(err, "GetContractInfo %v", c.Contract) } if ci == nil { - ci = &bchain.Erc20Contract{} - addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(c.Contract) - if len(addresses) > 0 { - ci.Contract = addresses[0] - ci.Name = addresses[0] + glog.Warningf("Contract %v %v not found in DB", c.Contract, typeName) + ci, err = w.chain.GetContractInfo(c.Contract) + if err != nil { + glog.Errorf("GetContractInfo from chain error %v, contract %v", err, c.Contract) + } + if ci == nil { + ci = &bchain.ContractInfo{Type: bchain.UnknownTokenType} + addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(c.Contract) + if len(addresses) > 0 { + ci.Contract = addresses[0] + ci.Name = addresses[0] + } + validContract = false } - validContract = false } t := Token{ Contract: ci.Contract, Name: ci.Name, Symbol: ci.Symbol, + Type: typeName, Transfers: int(c.Txs), Decimals: ci.Decimals, ContractIndex: strconv.Itoa(index), } // return contract balances/values only at or above AccountDetailsTokenBalances if details >= AccountDetailsTokenBalances && validContract { - if c.Type == bchain.ERC20 { - t.Type = ERC20TokenType + if c.Type == bchain.FungibleToken { // get Erc20 Contract Balance from blockchain, balance obtained from adding and subtracting transfers is not correct b, err := w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, c.Contract) if err != nil { @@ -789,11 +806,6 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i t.BalanceSat = (*Amount)(b) } } else { - if c.Type == bchain.ERC721 { - t.Type = ERC771TokenType - } else { - t.Type = ERC1155TokenType - } if len(c.Ids) > 0 { ids := make([]Amount, len(c.Ids)) for j := range ids { @@ -801,13 +813,13 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i } t.Ids = ids } - if len(c.IdValues) > 0 { - idValues := make([]TokenTransferValues, len(c.IdValues)) + if len(c.MultiTokenValues) > 0 { + idValues := make([]MultiTokenValue, len(c.MultiTokenValues)) for j := range idValues { - idValues[j].Id = (*Amount)(&c.IdValues[j].Id) - idValues[j].Value = (*Amount)(&c.IdValues[j].Value) + idValues[j].Id = (*Amount)(&c.MultiTokenValues[j].Id) + idValues[j].Value = (*Amount)(&c.MultiTokenValues[j].Value) } - t.IdValues = idValues + t.MultiTokenValues = idValues } } } @@ -819,18 +831,25 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bchain.AddressDescriptor, details AccountDetails) (*Token, error) { var b *big.Int validContract := true - ci, err := w.chain.EthereumTypeGetErc20ContractInfo(contract) + ci, err := w.db.GetContractInfo(contract, "") if err != nil { - return nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", contract) + return nil, errors.Annotatef(err, "GetContractInfo %v", contract) } if ci == nil { - ci = &bchain.Erc20Contract{} - addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(contract) - if len(addresses) > 0 { - ci.Contract = addresses[0] - ci.Name = addresses[0] + glog.Warningf("Contract %v not found in DB", contract) + ci, err = w.chain.GetContractInfo(contract) + if err != nil { + glog.Errorf("GetContractInfo from chain error %v, contract %v", err, contract) + } + if ci == nil { + ci = &bchain.ContractInfo{Type: bchain.UnknownTokenType} + addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(contract) + if len(addresses) > 0 { + ci.Contract = addresses[0] + ci.Name = addresses[0] + } + validContract = false } - validContract = false } // do not read contract balances etc in case of Basic option if details >= AccountDetailsTokenBalances && validContract { @@ -843,7 +862,7 @@ func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bch b = nil } return &Token{ - Type: ERC20TokenType, + Type: ci.Type, BalanceSat: (*Amount)(b), Contract: ci.Contract, Name: ci.Name, @@ -854,11 +873,11 @@ func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bch }, nil } -func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, int, error) { +func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.ContractInfo, uint64, int, int, int, error) { var ( ba *db.AddrBalance tokens []Token - ci *bchain.Erc20Contract + ci *bchain.ContractInfo n uint64 nonContractTxs int internalTxs int @@ -912,7 +931,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto } tokens = tokens[:j] } - ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc) + ci, err = w.db.GetContractInfo(addrDesc, "") if err != nil { return nil, nil, nil, 0, 0, 0, 0, err } @@ -1043,7 +1062,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco var ( ba *db.AddrBalance tokens []Token - erc20c *bchain.Erc20Contract + contractInfo *bchain.ContractInfo txm []string txs []*Tx txids []string @@ -1062,7 +1081,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco } if w.chainType == bchain.ChainEthereumType { var n uint64 - ba, tokens, erc20c, n, nonTokenTxs, internalTxs, totalResults, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter) + ba, tokens, contractInfo, n, nonTokenTxs, internalTxs, totalResults, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter) if err != nil { return nil, err } @@ -1173,7 +1192,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco Transactions: txs, Txids: txids, Tokens: tokens, - Erc20Contract: erc20c, + ContractInfo: contractInfo, Nonce: nonce, AddressAliases: w.getAddressAliases(addresses), } diff --git a/api/xpub.go b/api/xpub.go index 8737373bd6..b555e379a5 100644 --- a/api/xpub.go +++ b/api/xpub.go @@ -266,7 +266,7 @@ func (w *Worker) tokenFromXpubAddress(data *xpubData, ad *xpubAddress, changeInd } } return Token{ - Type: XPUBAddressTokenType, + Type: bchain.XPUBAddressTokenType, Name: address, Decimals: w.chainParser.AmountDecimals(), BalanceSat: (*Amount)(balance), diff --git a/bchain/basechain.go b/bchain/basechain.go index 26ea6a5e1b..ee5d30e06f 100644 --- a/bchain/basechain.go +++ b/bchain/basechain.go @@ -54,8 +54,8 @@ func (b *BaseChain) EthereumTypeEstimateGas(params map[string]interface{}) (uint return 0, errors.New("Not supported") } -// EthereumTypeGetErc20ContractInfo is not supported -func (b *BaseChain) EthereumTypeGetErc20ContractInfo(contractDesc AddressDescriptor) (*Erc20Contract, error) { +// GetContractInfo is not supported +func (b *BaseChain) GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error) { return nil, errors.New("Not supported") } diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index c4008e8f13..33b996e9e3 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -321,13 +321,13 @@ func (c *blockChainWithMetrics) EthereumTypeEstimateGas(params map[string]interf return c.b.EthereumTypeEstimateGas(params) } -func (c *blockChainWithMetrics) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (v *bchain.Erc20Contract, err error) { - defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetErc20ContractInfo", s, err) }(time.Now()) - return c.b.EthereumTypeGetErc20ContractInfo(contractDesc) +func (c *blockChainWithMetrics) GetContractInfo(contractDesc bchain.AddressDescriptor) (v *bchain.ContractInfo, err error) { + defer func(s time.Time) { c.observeRPCLatency("GetContractInfo", s, err) }(time.Now()) + return c.b.GetContractInfo(contractDesc) } func (c *blockChainWithMetrics) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (v *big.Int, err error) { - defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetErc20ContractInfo", s, err) }(time.Now()) + defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetErc20ContractBalance", s, err) }(time.Now()) return c.b.EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc) } diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go index ddc992949c..76a84fb1ed 100644 --- a/bchain/coins/eth/contract.go +++ b/bchain/coins/eth/contract.go @@ -4,10 +4,8 @@ import ( "context" "math/big" "strings" - "sync" ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/golang/glog" "github.com/juju/errors" "github.com/trezor/blockbook/bchain" ) @@ -28,9 +26,6 @@ const contractSymbolSignature = "0x95d89b41" const contractDecimalsSignature = "0x313ce567" const contractBalanceOf = "0x70a08231" -var cachedContracts = make(map[string]*bchain.Erc20Contract) -var cachedContractsMux sync.Mutex - func addressFromPaddedHex(s string) (string, error) { var t big.Int var ok bool @@ -48,16 +43,16 @@ func addressFromPaddedHex(s string) (string, error) { func processTransferEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { tl := len(l.Topics) - var ttt bchain.TokenTransferType + var ttt bchain.TokenType var value big.Int if tl == 3 { - ttt = bchain.ERC20 + ttt = bchain.FungibleToken _, ok := value.SetString(l.Data, 0) if !ok { return nil, errors.New("ERC20 log Data is not a number") } } else if tl == 4 { - ttt = bchain.ERC721 + ttt = bchain.NonFungibleToken _, ok := value.SetString(l.Topics[3], 0) if !ok { return nil, errors.New("ERC721 log Topics[3] is not a number") @@ -105,11 +100,11 @@ func processERC1155TransferSingleEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, return nil, errors.New("ERC1155 log Data value is not a number") } return &bchain.TokenTransfer{ - Type: bchain.ERC1155, - Contract: EIP55AddressFromAddress(l.Address), - From: EIP55AddressFromAddress(from), - To: EIP55AddressFromAddress(to), - IdValues: []bchain.TokenTransferIdValue{{Id: id, Value: value}}, + Type: bchain.MultiToken, + Contract: EIP55AddressFromAddress(l.Address), + From: EIP55AddressFromAddress(from), + To: EIP55AddressFromAddress(to), + MultiTokenValues: []bchain.MultiTokenValue{{Id: id, Value: value}}, }, nil } @@ -150,7 +145,7 @@ func processERC1155TransferBatchEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, if countIds != countValues { return nil, errors.New("ERC1155 TransferBatch, count values and ids does not match") } - idValues := make([]bchain.TokenTransferIdValue, countValues) + idValues := make([]bchain.MultiTokenValue, countValues) for i := 0; i < countValues; i++ { var id, value big.Int o := offsetIds + 64 + 64*i @@ -163,14 +158,14 @@ func processERC1155TransferBatchEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, if !ok { return nil, errors.New("ERC1155 log Data value is not a number") } - idValues[i] = bchain.TokenTransferIdValue{Id: id, Value: value} + idValues[i] = bchain.MultiTokenValue{Id: id, Value: value} } return &bchain.TokenTransfer{ - Type: bchain.ERC1155, - Contract: EIP55AddressFromAddress(l.Address), - From: EIP55AddressFromAddress(from), - To: EIP55AddressFromAddress(to), - IdValues: idValues, + Type: bchain.MultiToken, + Contract: EIP55AddressFromAddress(l.Address), + From: EIP55AddressFromAddress(from), + To: EIP55AddressFromAddress(to), + MultiTokenValues: idValues, }, nil } func contractGetTransfersFromLog(logs []*bchain.RpcLog) (bchain.TokenTransfers, error) { @@ -214,7 +209,7 @@ func contractGetTransfersFromTx(tx *bchain.RpcTransaction) (bchain.TokenTransfer return nil, errors.New("Data is not a number") } r = append(r, &bchain.TokenTransfer{ - Type: bchain.ERC20, + Type: bchain.FungibleToken, Contract: EIP55AddressFromAddress(tx.To), From: EIP55AddressFromAddress(tx.From), To: EIP55AddressFromAddress(to), @@ -238,7 +233,7 @@ func contractGetTransfersFromTx(tx *bchain.RpcTransaction) (bchain.TokenTransfer return nil, errors.New("Data is not a number") } r = append(r, &bchain.TokenTransfer{ - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Contract: EIP55AddressFromAddress(tx.To), From: EIP55AddressFromAddress(from), To: EIP55AddressFromAddress(to), @@ -262,56 +257,52 @@ func (b *EthereumRPC) ethCall(data, to string) (string, error) { return r, nil } -// EthereumTypeGetErc20ContractInfo returns information about ERC20 contract -func (b *EthereumRPC) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.Erc20Contract, error) { - cds := string(contractDesc) - cachedContractsMux.Lock() - contract, found := cachedContracts[cds] - cachedContractsMux.Unlock() - if !found { - address := EIP55Address(contractDesc) - data, err := b.ethCall(contractNameSignature, address) +func (b *EthereumRPC) fetchContractInfo(address string) (*bchain.ContractInfo, error) { + var contract bchain.ContractInfo + data, err := b.ethCall(contractNameSignature, address) + if err != nil { + // ignore the error from the eth_call - since geth v1.9.15 they changed the behavior + // and returning error "execution reverted" for some non contract addresses + // https://github.com/ethereum/go-ethereum/issues/21249#issuecomment-648647672 + // glog.Warning(errors.Annotatef(err, "Contract NameSignature %v", address)) + return nil, nil + // return nil, errors.Annotatef(err, "erc20NameSignature %v", address) + } + name := parseSimpleStringProperty(data) + if name != "" { + data, err = b.ethCall(contractSymbolSignature, address) if err != nil { - // ignore the error from the eth_call - since geth v1.9.15 they changed the behavior - // and returning error "execution reverted" for some non contract addresses - // https://github.com/ethereum/go-ethereum/issues/21249#issuecomment-648647672 - glog.Warning(errors.Annotatef(err, "erc20NameSignature %v", address)) + // glog.Warning(errors.Annotatef(err, "Contract SymbolSignature %v", address)) return nil, nil - // return nil, errors.Annotatef(err, "erc20NameSignature %v", address) + // return nil, errors.Annotatef(err, "erc20SymbolSignature %v", address) } - name := parseSimpleStringProperty(data) - if name != "" { - data, err = b.ethCall(contractSymbolSignature, address) - if err != nil { - glog.Warning(errors.Annotatef(err, "erc20SymbolSignature %v", address)) - return nil, nil - // return nil, errors.Annotatef(err, "erc20SymbolSignature %v", address) - } - symbol := parseSimpleStringProperty(data) - data, err = b.ethCall(contractDecimalsSignature, address) - if err != nil { - glog.Warning(errors.Annotatef(err, "erc20DecimalsSignature %v", address)) - // return nil, errors.Annotatef(err, "erc20DecimalsSignature %v", address) - } - contract = &bchain.Erc20Contract{ - Contract: address, - Name: name, - Symbol: symbol, - } - d := parseSimpleNumericProperty(data) - if d != nil { - contract.Decimals = int(uint8(d.Uint64())) - } else { - contract.Decimals = EtherAmountDecimalPoint - } + symbol := parseSimpleStringProperty(data) + data, _ = b.ethCall(contractDecimalsSignature, address) + // if err != nil { + // glog.Warning(errors.Annotatef(err, "Contract DecimalsSignature %v", address)) + // // return nil, errors.Annotatef(err, "erc20DecimalsSignature %v", address) + // } + contract = bchain.ContractInfo{ + Contract: address, + Name: name, + Symbol: symbol, + } + d := parseSimpleNumericProperty(data) + if d != nil { + contract.Decimals = int(uint8(d.Uint64())) } else { - contract = nil + contract.Decimals = EtherAmountDecimalPoint } - cachedContractsMux.Lock() - cachedContracts[cds] = contract - cachedContractsMux.Unlock() + } else { + return nil, nil } - return contract, nil + return &contract, nil +} + +// GetContractInfo returns information about a contract +func (b *EthereumRPC) GetContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.ContractInfo, error) { + address := EIP55Address(contractDesc) + return b.fetchContractInfo(address) } // EthereumTypeGetErc20ContractBalance returns balance of ERC20 contract for given address diff --git a/bchain/coins/eth/contract_test.go b/bchain/coins/eth/contract_test.go index 7d609a0a95..587d98a774 100644 --- a/bchain/coins/eth/contract_test.go +++ b/bchain/coins/eth/contract_test.go @@ -133,7 +133,7 @@ func Test_contractGetTransfersFromLog(t *testing.T) { }, want: bchain.TokenTransfers{ { - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Contract: "0x5689b918D34C038901870105A6C7fc24744D31eB", From: "0x0a206d4d5ff79cb5069def7fe3598421cff09391", To: "0x6a016d7eec560549ffa0fbdb7f15c2b27302087f", @@ -171,11 +171,11 @@ func Test_contractGetTransfersFromLog(t *testing.T) { }, want: bchain.TokenTransfers{ { - Type: bchain.ERC1155, - Contract: "0x6Fd712E3A5B556654044608F9129040A4839E36c", - From: "0xa3950b823cb063dd9afc0d27f35008b805b3ed53", - To: "0x4392faf3bb96b5694ecc6ef64726f61cdd4bb0ec", - IdValues: []bchain.TokenTransferIdValue{{Id: *big.NewInt(150), Value: *big.NewInt(0x11)}}, + Type: bchain.MultiToken, + Contract: "0x6Fd712E3A5B556654044608F9129040A4839E36c", + From: "0xa3950b823cb063dd9afc0d27f35008b805b3ed53", + To: "0x4392faf3bb96b5694ecc6ef64726f61cdd4bb0ec", + MultiTokenValues: []bchain.MultiTokenValue{{Id: *big.NewInt(150), Value: *big.NewInt(0x11)}}, }, }, }, @@ -195,11 +195,11 @@ func Test_contractGetTransfersFromLog(t *testing.T) { }, want: bchain.TokenTransfers{ { - Type: bchain.ERC1155, + Type: bchain.MultiToken, Contract: "0x6c42c26a081c2f509f8bb68fb7ac3062311ccfb7", From: "0x0000000000000000000000000000000000000000", To: "0x5dc6288b35e0807a3d6feb89b3a2ff4ab773168e", - IdValues: []bchain.TokenTransferIdValue{ + MultiTokenValues: []bchain.MultiTokenValue{ {Id: *big.NewInt(1776), Value: *big.NewInt(1)}, {Id: *big.NewInt(1898), Value: *big.NewInt(10)}, }, @@ -247,7 +247,7 @@ func Test_contractGetTransfersFromTx(t *testing.T) { args: (b1.Txs[1].CoinSpecificData.(bchain.EthereumSpecificData)).Tx, want: bchain.TokenTransfers{ { - Type: bchain.ERC20, + Type: bchain.FungibleToken, Contract: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2", From: "0x20cd153de35d469ba46127a0c8f18626b59a256a", To: "0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f", @@ -260,7 +260,7 @@ func Test_contractGetTransfersFromTx(t *testing.T) { args: (b2.Txs[2].CoinSpecificData.(bchain.EthereumSpecificData)).Tx, want: bchain.TokenTransfers{ { - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Contract: "0xcda9fc258358ecaa88845f19af595e908bb7efe9", From: "0x837e3f699d85a4b0b99894567e9233dfb1dcb081", To: "0x7b62eb7fe80350dc7ec945c0b73242cb9877fb1b", diff --git a/bchain/coins/eth/dataparser.go b/bchain/coins/eth/dataparser.go index a67c947e01..060fcfca76 100644 --- a/bchain/coins/eth/dataparser.go +++ b/bchain/coins/eth/dataparser.go @@ -39,8 +39,8 @@ func parseSimpleStringProperty(data string) string { if len(data) > 128 { n := parseSimpleNumericProperty(data[64:128]) if n != nil { - l := n.Uint64() - if l > 0 && 2*int(l) <= len(data)-128 { + l := n.Int64() + if l > 0 && int(l) <= ((len(data)-128)>>1) { b, err := hex.DecodeString(data[128 : 128+2*l]) if err == nil { return string(b) diff --git a/bchain/coins/eth/dataparser_test.go b/bchain/coins/eth/dataparser_test.go index 5aca77ec14..745e6b6262 100644 --- a/bchain/coins/eth/dataparser_test.go +++ b/bchain/coins/eth/dataparser_test.go @@ -45,6 +45,11 @@ func Test_parseSimpleStringProperty(t *testing.T) { args: "0x2234880850896048596206002535425366538144616734015984380565810000", want: "", }, + { + name: "garbage", + args: "6080604052600436106100225760003560e01c80630cbcae701461003957610031565b366100315761002f610077565b005b61002f610077565b34801561004557600080fd5b5061004e61014e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000061011c565b60043560601b60601c6bca11c0de15dead10cced00006000195460a01c036100e9577f696d706c6f63000000000000000000000000000000000000000000000000000060005260206000fd5b8060001955005b60405136810160405236600082376000803683600019545af43d6000833e80610117573d82fd5b503d81f35b80330361014357602436036101435763ca11c0de60003560e01c036101435761014361009d565b61014b6100f0565b50565b600073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff541660005260206000f3fea2646970667358221220f27ad3f3b75609baa5d26d65ec1001c4a59f38e89088d6b47517c1cd1faf22ab64736f6c634300080d0033", + want: "", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 092c835ba8..1e48bc54ae 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -537,23 +537,37 @@ type rpcTraceResult struct { Result rpcCallTrace `json:"result"` } -func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInternalData) { +func (b *EthereumRPC) getCreationContractInfo(contract string, height uint32) *bchain.ContractInfo { + ci, err := b.fetchContractInfo(contract) + if ci == nil || err != nil { + ci = &bchain.ContractInfo{ + Contract: contract, + } + } + ci.Type = bchain.UnknownTokenType + ci.CreatedInBlock = height + return ci +} + +func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInternalData, contracts []bchain.ContractInfo, blockHeight uint32) []bchain.ContractInfo { value, err := hexutil.DecodeBig(call.Value) if call.Type == "CREATE" { d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ Type: bchain.CREATE, Value: *value, From: call.From, - To: call.To, + To: call.To, // new contract address }) + contracts = append(contracts, *b.getCreationContractInfo(call.To, blockHeight)) } else if call.Type == "SELFDESTRUCT" { d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ Type: bchain.SELFDESTRUCT, Value: *value, - From: call.From, + From: call.From, // destroyed contract address To: call.To, }) + contracts = append(contracts, bchain.ContractInfo{Contract: call.From, DestructedInBlock: blockHeight}) } else if err == nil && (value.BitLen() > 0 || b.ChainConfig.ProcessZeroInternalTransactions) { d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ Value: *value, @@ -565,13 +579,15 @@ func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInt d.Error = call.Error } for i := range call.Calls { - b.processCallTrace(&call.Calls[i], d) + contracts = b.processCallTrace(&call.Calls[i], d, contracts, blockHeight) } + return contracts } // getInternalDataForBlock fetches debug trace using callTracer, extracts internal transfers and creations and destructions of contracts -func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []bchain.RpcTransaction) ([]bchain.EthereumInternalData, error) { +func (b *EthereumRPC) getInternalDataForBlock(blockHash string, blockHeight uint32, transactions []bchain.RpcTransaction) ([]bchain.EthereumInternalData, []bchain.ContractInfo, error) { data := make([]bchain.EthereumInternalData, len(transactions)) + contracts := make([]bchain.ContractInfo, 0) if ProcessInternalTransactions { ctx, cancel := context.WithTimeout(context.Background(), b.timeout) defer cancel() @@ -579,11 +595,11 @@ func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []b err := b.rpc.CallContext(ctx, &trace, "debug_traceBlockByHash", blockHash, map[string]interface{}{"tracer": "callTracer"}) if err != nil { glog.Error("debug_traceBlockByHash block ", blockHash, ", error ", err) - return data, err + return data, contracts, err } if len(trace) != len(data) { glog.Error("debug_traceBlockByHash block ", blockHash, ", error: trace length does not match block length ", len(trace), "!=", len(data)) - return data, err + return data, contracts, err } for i, result := range trace { r := &result.Result @@ -591,11 +607,12 @@ func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []b if r.Type == "CREATE" { d.Type = bchain.CREATE d.Contract = r.To + contracts = append(contracts, *b.getCreationContractInfo(d.Contract, blockHeight)) } else if r.Type == "SELFDESTRUCT" { d.Type = bchain.SELFDESTRUCT } for j := range r.Calls { - b.processCallTrace(&r.Calls[j], d) + contracts = b.processCallTrace(&r.Calls[j], d, contracts, blockHeight) } if r.Error != "" { baseError := PackInternalTransactionError(r.Error) @@ -620,7 +637,7 @@ func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []b } } } - return data, nil + return data, contracts, nil } // GetBlock returns block with given hash or height, hash has precedence if both passed @@ -642,15 +659,16 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) } // get block events + // TODO - could be possibly done in parallel to getInternalDataForBlock logs, ens, err := b.processEventsForBlock(head.Number) if err != nil { return nil, err } // error fetching internal data does not stop the block processing var blockSpecificData *bchain.EthereumBlockSpecificData - internalData, err := b.getInternalDataForBlock(head.Hash, body.Transactions) + internalData, contracts, err := b.getInternalDataForBlock(head.Hash, bbh.Height, body.Transactions) // pass internalData error and ENS records in blockSpecificData to be stored - if err != nil || len(ens) > 0 { + if err != nil || len(ens) > 0 || len(contracts) > 0 { blockSpecificData = &bchain.EthereumBlockSpecificData{} if err != nil { blockSpecificData.InternalDataError = err.Error() @@ -658,7 +676,11 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error } if len(ens) > 0 { blockSpecificData.AddressAliasRecords = ens - glog.Info("ENS", ens) + // glog.Info("ENS", ens) + } + if len(contracts) > 0 { + blockSpecificData.Contracts = contracts + // glog.Info("Contracts", contracts) } } diff --git a/bchain/types.go b/bchain/types.go index 2f40dfe446..4d9af1f946 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -113,6 +113,27 @@ type MempoolTx struct { CoinSpecificData interface{} `json:"-"` } +// TokenType - type of token +type TokenType int + +// TokenType enumeration +const ( + FungibleToken = TokenType(iota) // ERC20 + NonFungibleToken // ERC721 + MultiToken // ERC1155 +) + +// TokenTypeName specifies type of token +type TokenTypeName string + +// Token types +const ( + UnknownTokenType TokenTypeName = "" + + // XPUBAddressTokenType is address derived from xpub + XPUBAddressTokenType TokenTypeName = "XPUBAddress" +) + // TokenTransfers is array of TokenTransfer type TokenTransfers []*TokenTransfer @@ -286,13 +307,13 @@ type BlockChain interface { EstimateFee(blocks int) (big.Int, error) SendRawTransaction(tx string) (string, error) GetMempoolEntry(txid string) (*MempoolEntry, error) + GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error) // parser GetChainParser() BlockChainParser // EthereumType specific EthereumTypeGetBalance(addrDesc AddressDescriptor) (*big.Int, error) EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) - EthereumTypeGetErc20ContractInfo(contractDesc AddressDescriptor) (*Erc20Contract, error) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error) } diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index 357f1667f4..a04a0810ec 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -47,16 +47,6 @@ const ( SELFDESTRUCT ) -// TokenTransferType - type of token transfer -type TokenTransferType int - -// TokenTransferType enumeration -const ( - ERC20 = TokenTransferType(iota) - ERC721 - ERC1155 -) - // EthereumInternalTransaction contains internal transfers type EthereumInternalData struct { Type EthereumInternalTransactionType `json:"type"` @@ -65,27 +55,41 @@ type EthereumInternalData struct { Error string } -// Erc20Contract contains info about ERC20 contract -type Erc20Contract struct { - Contract string `json:"contract"` - Name string `json:"name"` - Symbol string `json:"symbol"` - Decimals int `json:"decimals"` +// ContractInfo contains info about ERC20 contract +type ContractInfo struct { + Type TokenTypeName `json:"type"` + Contract string `json:"contract"` + Name string `json:"name"` + Symbol string `json:"symbol"` + Decimals int `json:"decimals"` + CreatedInBlock uint32 `json:"createdInBlock,omitempty"` + DestructedInBlock uint32 `json:"destructedInBlock,omitempty"` } -type TokenTransferIdValue struct { +// Ethereum token type names +const ( + ERC20TokenType TokenTypeName = "ERC20" + ERC771TokenType TokenTypeName = "ERC721" + ERC1155TokenType TokenTypeName = "ERC1155" +) + +// EthereumTokenTypeMap maps bchain.TokenType to TokenTypeName +// the map must match all bchain.TokenType to avoid index out of range panic +var EthereumTokenTypeMap []TokenTypeName = []TokenTypeName{ERC20TokenType, ERC771TokenType, ERC1155TokenType} + +type MultiTokenValue struct { Id big.Int Value big.Int } -// TokenTransfer contains a single ERC20/ERC721/ERC1155 token transfer +// TokenTransfer contains a single token transfer type TokenTransfer struct { - Type TokenTransferType - Contract string - From string - To string - Value big.Int - IdValues []TokenTransferIdValue + Type TokenType + Contract string + From string + To string + Value big.Int + MultiTokenValues []MultiTokenValue } // RpcTransaction is returned by eth_getTransactionByHash @@ -138,4 +142,5 @@ type AddressAliasRecord struct { type EthereumBlockSpecificData struct { InternalDataError string AddressAliasRecords []AddressAliasRecord + Contracts []ContractInfo } diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index 13457c7c06..a75b528444 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -6,6 +6,7 @@ import ( "math/big" "sync" + vlq "github.com/bsm/go-vlq" "github.com/flier/gorocksdb" "github.com/golang/glog" "github.com/juju/errors" @@ -18,12 +19,12 @@ const ContractIndexOffset = 2 // AddrContract is Contract address with number of transactions done by given address type AddrContract struct { - Type bchain.TokenTransferType - Contract bchain.AddressDescriptor - Txs uint - Value big.Int // single value of ERC20 - Ids []big.Int // multiple ERC721 tokens - IdValues []bchain.TokenTransferIdValue // multiple ERC1155 tokens + Type bchain.TokenType + Contract bchain.AddressDescriptor + Txs uint + Value big.Int // single value of ERC20 + Ids []big.Int // multiple ERC721 tokens + MultiTokenValues []bchain.MultiTokenValue // multiple ERC1155 tokens } // AddrContracts contains number of transactions and contracts for an address @@ -48,10 +49,10 @@ func packAddrContracts(acs *AddrContracts) []byte { buf = append(buf, ac.Contract...) l = packVaruint(uint(ac.Type)+ac.Txs<<2, varBuf) buf = append(buf, varBuf[:l]...) - if ac.Type == bchain.ERC20 { + if ac.Type == bchain.FungibleToken { l = packBigint(&ac.Value, varBuf) buf = append(buf, varBuf[:l]...) - } else if ac.Type == bchain.ERC721 { + } else if ac.Type == bchain.NonFungibleToken { l = packVaruint(uint(len(ac.Ids)), varBuf) buf = append(buf, varBuf[:l]...) for i := range ac.Ids { @@ -59,12 +60,12 @@ func packAddrContracts(acs *AddrContracts) []byte { buf = append(buf, varBuf[:l]...) } } else { // bchain.ERC1155 - l = packVaruint(uint(len(ac.IdValues)), varBuf) + l = packVaruint(uint(len(ac.MultiTokenValues)), varBuf) buf = append(buf, varBuf[:l]...) - for i := range ac.IdValues { - l = packBigint(&ac.IdValues[i].Id, varBuf) + for i := range ac.MultiTokenValues { + l = packBigint(&ac.MultiTokenValues[i].Id, varBuf) buf = append(buf, varBuf[:l]...) - l = packBigint(&ac.IdValues[i].Value, varBuf) + l = packBigint(&ac.MultiTokenValues[i].Value, varBuf) buf = append(buf, varBuf[:l]...) } } @@ -87,21 +88,21 @@ func unpackAddrContracts(buf []byte, addrDesc bchain.AddressDescriptor) (*AddrCo contract := append(bchain.AddressDescriptor(nil), buf[:eth.EthereumTypeAddressDescriptorLen]...) txs, l := unpackVaruint(buf[eth.EthereumTypeAddressDescriptorLen:]) buf = buf[eth.EthereumTypeAddressDescriptorLen+l:] - ttt := bchain.TokenTransferType(txs & 3) + ttt := bchain.TokenType(txs & 3) txs >>= 2 ac := AddrContract{ Type: ttt, Contract: contract, Txs: txs, } - if ttt == bchain.ERC20 { + if ttt == bchain.FungibleToken { b, ll := unpackBigint(buf) buf = buf[ll:] ac.Value = b } else { len, ll := unpackVaruint(buf) buf = buf[ll:] - if ttt == bchain.ERC721 { + if ttt == bchain.NonFungibleToken { ac.Ids = make([]big.Int, len) for i := uint(0); i < len; i++ { b, ll := unpackBigint(buf) @@ -109,14 +110,14 @@ func unpackAddrContracts(buf []byte, addrDesc bchain.AddressDescriptor) (*AddrCo ac.Ids[i] = b } } else { - ac.IdValues = make([]bchain.TokenTransferIdValue, len) + ac.MultiTokenValues = make([]bchain.MultiTokenValue, len) for i := uint(0); i < len; i++ { b, ll := unpackBigint(buf) buf = buf[ll:] - ac.IdValues[i].Id = b + ac.MultiTokenValues[i].Id = b b, ll = unpackBigint(buf) buf = buf[ll:] - ac.IdValues[i].Value = b + ac.MultiTokenValues[i].Value = b } } } @@ -226,9 +227,9 @@ func addToContract(c *AddrContract, contractIndex int, index int32, contract bch s.Add(s, v) } } - if transfer.Type == bchain.ERC20 { + if transfer.Type == bchain.FungibleToken { aggregate(&c.Value, &transfer.Value) - } else if transfer.Type == bchain.ERC721 { + } else if transfer.Type == bchain.NonFungibleToken { if index < 0 { // remove token from the list for i := range c.Ids { @@ -242,14 +243,14 @@ func addToContract(c *AddrContract, contractIndex int, index int32, contract bch c.Ids = append(c.Ids, transfer.Value) } } else { // bchain.ERC1155 - for _, t := range transfer.IdValues { - for i := range c.IdValues { + for _, t := range transfer.MultiTokenValues { + for i := range c.MultiTokenValues { // find the token in the list - if c.IdValues[i].Id.Cmp(&t.Id) == 0 { - aggregate(&c.IdValues[i].Value, &t.Value) + if c.MultiTokenValues[i].Id.Cmp(&t.Id) == 0 { + aggregate(&c.MultiTokenValues[i].Value, &t.Value) // if transfer from, remove if the value is zero - if index < 0 && len(c.IdValues[i].Value.Bits()) == 0 { - c.IdValues = append(c.IdValues[:i], c.IdValues[i+1:]...) + if index < 0 && len(c.MultiTokenValues[i].Value.Bits()) == 0 { + c.MultiTokenValues = append(c.MultiTokenValues[:i], c.MultiTokenValues[i+1:]...) } goto nextTransfer } @@ -257,7 +258,7 @@ func addToContract(c *AddrContract, contractIndex int, index int32, contract bch // if not found and transfer to, add to the list // it is necessary to add a copy of the value so that subsequent calls to addToContract do not change the transfer value if index >= 0 { - c.IdValues = append(c.IdValues, bchain.TokenTransferIdValue{ + c.MultiTokenValues = append(c.MultiTokenValues, bchain.MultiTokenValue{ Id: t.Id, Value: *new(big.Int).Set(&t.Value), }) @@ -327,9 +328,9 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address type ethBlockTxContract struct { from, to, contract bchain.AddressDescriptor - transferType bchain.TokenTransferType + transferType bchain.TokenType value big.Int - idValues []bchain.TokenTransferIdValue + idValues []bchain.MultiTokenValue } type ethInternalTransfer struct { @@ -476,7 +477,7 @@ func (d *RocksDB) processContractTransfers(blockTx *ethBlockTx, tx *bchain.Tx, a bc.to = to bc.contract = contract bc.value = t.Value - bc.idValues = t.IdValues + bc.idValues = t.MultiTokenValues } return nil } @@ -699,6 +700,113 @@ func (d *RocksDB) storeInternalDataEthereumType(wb *gorocksdb.WriteBatch, blockT return nil } +var cachedContracts = make(map[string]*bchain.ContractInfo) +var cachedContractsMux sync.Mutex + +func packContractInfo(contractInfo *bchain.ContractInfo) []byte { + buf := packString(contractInfo.Name) + buf = append(buf, packString(contractInfo.Symbol)...) + buf = append(buf, packString(string(contractInfo.Type))...) + varBuf := make([]byte, vlq.MaxLen64) + l := packVaruint(uint(contractInfo.Decimals), varBuf) + buf = append(buf, varBuf[:l]...) + l = packVaruint(uint(contractInfo.CreatedInBlock), varBuf) + buf = append(buf, varBuf[:l]...) + l = packVaruint(uint(contractInfo.DestructedInBlock), varBuf) + buf = append(buf, varBuf[:l]...) + return buf +} + +func unpackContractInfo(buf []byte) (*bchain.ContractInfo, error) { + var contractInfo bchain.ContractInfo + var s string + var l int + var ui uint + contractInfo.Name, l = unpackString(buf) + buf = buf[l:] + contractInfo.Symbol, l = unpackString(buf) + buf = buf[l:] + s, l = unpackString(buf) + contractInfo.Type = bchain.TokenTypeName(s) + buf = buf[l:] + ui, l = unpackVaruint(buf) + contractInfo.Decimals = int(ui) + buf = buf[l:] + ui, l = unpackVaruint(buf) + contractInfo.CreatedInBlock = uint32(ui) + buf = buf[l:] + ui, l = unpackVaruint(buf) + contractInfo.DestructedInBlock = uint32(ui) + return &contractInfo, nil +} + +func (d *RocksDB) GetContractInfoForAddress(address string) (*bchain.ContractInfo, error) { + contract, err := d.chainParser.GetAddrDescFromAddress(address) + if err != nil || contract == nil { + return nil, err + } + return d.GetContractInfo(contract, "") +} + +// GetContractInfo gets contract from cache or DB and possibly updates the type from typeFromContext +// this is because it is hard to guess the type of the contract using API, it is easier to set it the first time its usage is detected in tx +func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, error) { + cacheKey := string(contract) + cachedContractsMux.Lock() + contractInfo, found := cachedContracts[cacheKey] + cachedContractsMux.Unlock() + if !found { + val, err := d.db.GetCF(d.ro, d.cfh[cfContracts], contract) + if err != nil { + return nil, err + } + defer val.Free() + buf := val.Data() + if len(buf) == 0 { + return nil, nil + } + contractInfo, err = unpackContractInfo(buf) + addresses, _, _ := d.chainParser.GetAddressesFromAddrDesc(contract) + if len(addresses) > 0 { + contractInfo.Contract = addresses[0] + } + // if the type is specified and stored contractInfo has unknown type, set and store it + if typeFromContext != bchain.UnknownTokenType && contractInfo.Type == bchain.UnknownTokenType { + contractInfo.Type = typeFromContext + err = d.db.PutCF(d.wo, d.cfh[cfContracts], contract, packContractInfo(contractInfo)) + } + cachedContractsMux.Lock() + cachedContracts[cacheKey] = contractInfo + cachedContractsMux.Unlock() + } + return contractInfo, nil +} + +// StoreContractInfo stores contractInfo in DB +// if CreatedInBlock==0 and DestructedInBlock!=0, it is evaluated as a desctruction of a contract, the contract info is updated +// in all other cases the contractInfo overwrites previously stored data in DB (however it should not really happen as contract is created only once) +func (d *RocksDB) StoreContractInfo(wb *gorocksdb.WriteBatch, contractInfo *bchain.ContractInfo) error { + if contractInfo.Contract != "" { + key, err := d.chainParser.GetAddrDescFromAddress(contractInfo.Contract) + if err != nil { + return err + } + if contractInfo.CreatedInBlock == 0 && contractInfo.DestructedInBlock != 0 { + storedCI, err := d.GetContractInfo(key, "") + if err != nil { + return err + } + if storedCI == nil { + return nil + } + storedCI.DestructedInBlock = contractInfo.DestructedInBlock + contractInfo = storedCI + } + wb.PutCF(d.cfh[cfContracts], key, packContractInfo(contractInfo)) + } + return nil +} + func packBlockTx(buf []byte, blockTx *ethBlockTx) []byte { varBuf := make([]byte, maxPackedBigintBytes) buf = append(buf, blockTx.btxID...) @@ -715,7 +823,7 @@ func packBlockTx(buf []byte, blockTx *ethBlockTx) []byte { buf = appendAddress(buf, c.contract) l = packVaruint(uint(c.transferType), varBuf) buf = append(buf, varBuf[:l]...) - if c.transferType == bchain.ERC1155 { + if c.transferType == bchain.MultiToken { l = packVaruint(uint(len(c.idValues)), varBuf) buf = append(buf, varBuf[:l]...) for i := range c.idValues { @@ -773,6 +881,11 @@ func (d *RocksDB) storeBlockSpecificDataEthereumType(wb *gorocksdb.WriteBatch, b return err } } + for i := range blockSpecificData.Contracts { + if err := d.StoreContractInfo(wb, &blockSpecificData.Contracts[i]); err != nil { + return err + } + } } return nil } @@ -823,12 +936,12 @@ func unpackBlockTx(buf []byte, pos int) (*ethBlockTx, int, error) { return nil, 0, err } cc, l = unpackVaruint(buf[pos:]) - c.transferType = bchain.TokenTransferType(cc) + c.transferType = bchain.TokenType(cc) pos += l - if c.transferType == bchain.ERC1155 { + if c.transferType == bchain.MultiToken { cc, l = unpackVaruint(buf[pos:]) pos += l - c.idValues = make([]bchain.TokenTransferIdValue, cc) + c.idValues = make([]bchain.MultiTokenValue, cc) for i := range c.idValues { c.idValues[i].Id, l = unpackBigint(buf[pos:]) pos += l @@ -938,9 +1051,9 @@ func (d *RocksDB) disconnectAddress(btxID []byte, internal bool, addrDesc bchain index = transferTo } addToContract(addrContract, contractIndex, index, btxContract.contract, &bchain.TokenTransfer{ - Type: btxContract.transferType, - Value: btxContract.value, - IdValues: btxContract.idValues, + Type: btxContract.transferType, + Value: btxContract.value, + MultiTokenValues: btxContract.idValues, }, false) } } else { diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index 6631df9f2d..bf651335e9 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -58,11 +58,11 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "020102", nil}, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), - "020100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("10000000000000000000000"), nil, + "020100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("10000000000000000000000"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), - "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintToHex(big.NewInt(0)), nil, + "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintToHex(big.NewInt(0)), nil, }, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "010002", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010101", nil}, @@ -72,6 +72,25 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo } } + var destructedInBlock uint + if afterDisconnect { + destructedInBlock = 44445 + } + if err := checkColumn(d, cfContracts, []keyPair{ + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), + "0b436f6e7472616374203734" + // Contract 74 + "03533734" + // S74 + "054552433230" + // ERC20 + varuintToHex(12) + varuintToHex(44444) + varuintToHex(destructedInBlock), + nil, + }, + }); err != nil { + { + t.Fatal(err) + } + } + if err := checkColumn(d, cfInternalData, []keyPair{ { dbtestdata.EthTxidB1T2, @@ -98,7 +117,7 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + "00" + dbtestdata.EthTxidB1T2 + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + - "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("10000000000000000000000"), + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("10000000000000000000000"), nil, }, } @@ -158,43 +177,43 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa if err := checkColumn(d, cfAddressContracts, []keyPair{ { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), - "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintToHex(big.NewInt(0)), nil, + "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintToHex(big.NewInt(0)), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), - "030202" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC1155)) + varuintToHex(1) + bigintFromStringToHex("150") + bigintFromStringToHex("1"), nil, + "030202" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(1) + bigintFromStringToHex("150") + bigintFromStringToHex("1"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), "010101" + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(2<<2+uint(bchain.ERC20)) + bigintFromStringToHex("8086") + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.ERC20)) + bigintFromStringToHex("871180000950184"), nil, + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(2<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("8086") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("871180000950184"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "050300" + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.ERC20)) + bigintFromStringToHex("10000000854307892726464") + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("0") + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("0"), nil, + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("10000000854307892726464") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("0") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("0"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser), - "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC1155)) + varuintToHex(2) + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10"), nil, + "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(2) + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser), "020000" + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("0") + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("7674999999999991915") + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC721)) + varuintToHex(1) + bigintFromStringToHex("1"), nil, + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("0") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("7674999999999991915") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.NonFungibleToken)) + varuintToHex(1) + bigintFromStringToHex("1"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser), - "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC721)) + varuintToHex(0), nil, + "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.NonFungibleToken)) + varuintToHex(0), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrA3, d.chainParser), - "010000" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC1155)) + varuintToHex(0), nil, + "010000" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(0), nil, }, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr92, d.chainParser), "010100", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "030104", nil}, @@ -209,6 +228,21 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa } } + if err := checkColumn(d, cfContracts, []keyPair{ + { + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), + "0b436f6e7472616374203734" + // Contract 74 + "03533734" + // S74 + "054552433230" + // ERC20 + varuintToHex(12) + varuintToHex(44444) + varuintToHex(44445), + nil, + }, + }); err != nil { + { + t.Fatal(err) + } + } + if err := checkColumn(d, cfInternalData, []keyPair{ { dbtestdata.EthTxidB1T2, @@ -243,22 +277,22 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + "00" + dbtestdata.EthTxidB2T2 + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser) + - "04" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("7675000000000000001") + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("854307892726464") + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("871180000950184") + - dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("7674999999999991915") + + "04" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("7675000000000000001") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("854307892726464") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("871180000950184") + + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("7674999999999991915") + dbtestdata.EthTxidB2T3 + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + - "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(uint(bchain.ERC721)) + bigintFromStringToHex("1") + + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(uint(bchain.NonFungibleToken)) + bigintFromStringToHex("1") + dbtestdata.EthTxidB2T4 + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr92, d.chainParser) + - "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrA3, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(uint(bchain.ERC1155)) + "01" + bigintFromStringToHex("150") + bigintFromStringToHex("1") + + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrA3, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(uint(bchain.MultiToken)) + "01" + bigintFromStringToHex("150") + bigintFromStringToHex("1") + dbtestdata.EthTxidB2T5 + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + - "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrZero, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(uint(bchain.ERC1155)) + "02" + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10") + + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrZero, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(uint(bchain.MultiToken)) + "02" + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10") + dbtestdata.EthTxidB2T6 + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + - "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("10000000000000000000000"), + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("10000000000000000000000"), nil, }, }); err != nil { @@ -722,13 +756,13 @@ func Test_packUnpackAddrContracts(t *testing.T) { InternalTxs: 8873, Contracts: []AddrContract{ { - Type: bchain.ERC20, + Type: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract0d, parser), Txs: 8, Value: *big.NewInt(793201132), }, { - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Txs: 41235, Ids: []big.Int{ @@ -740,10 +774,10 @@ func Test_packUnpackAddrContracts(t *testing.T) { }, }, { - Type: bchain.ERC1155, + Type: bchain.MultiToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), Txs: 64, - IdValues: []bchain.TokenTransferIdValue{ + MultiTokenValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(1), Value: *big.NewInt(1412341234), @@ -796,7 +830,7 @@ func Test_addToContracts(t *testing.T) { index: 1, contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.ERC20, + Type: bchain.FungibleToken, Value: *big.NewInt(123456), }, addTxCount: true, @@ -805,7 +839,7 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.ERC20, + Type: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Txs: 1, Value: *big.NewInt(123456), @@ -819,7 +853,7 @@ func Test_addToContracts(t *testing.T) { index: ^1, contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.ERC20, + Type: bchain.FungibleToken, Value: *big.NewInt(23456), }, addTxCount: true, @@ -828,7 +862,7 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.ERC20, + Type: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, @@ -842,7 +876,7 @@ func Test_addToContracts(t *testing.T) { index: 1, contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Value: *big.NewInt(1), }, addTxCount: true, @@ -851,13 +885,13 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.ERC20, + Type: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 1, Ids: []big.Int{*big.NewInt(1)}, @@ -871,7 +905,7 @@ func Test_addToContracts(t *testing.T) { index: 1, contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Value: *big.NewInt(2), }, addTxCount: true, @@ -880,13 +914,13 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.ERC20, + Type: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 2, Ids: []big.Int{*big.NewInt(1), *big.NewInt(2)}, @@ -900,7 +934,7 @@ func Test_addToContracts(t *testing.T) { index: ^1, contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Value: *big.NewInt(1), }, addTxCount: false, @@ -909,13 +943,13 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.ERC20, + Type: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 2, Ids: []big.Int{*big.NewInt(2)}, @@ -929,8 +963,8 @@ func Test_addToContracts(t *testing.T) { index: 1, contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.ERC1155, - IdValues: []bchain.TokenTransferIdValue{ + Type: bchain.MultiToken, + MultiTokenValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(11), Value: *big.NewInt(56789), @@ -943,22 +977,22 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.ERC20, + Type: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 2, Ids: []big.Int{*big.NewInt(2)}, }, { - Type: bchain.ERC1155, + Type: bchain.MultiToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), Txs: 1, - IdValues: []bchain.TokenTransferIdValue{ + MultiTokenValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(11), Value: *big.NewInt(56789), @@ -974,8 +1008,8 @@ func Test_addToContracts(t *testing.T) { index: 1, contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.ERC1155, - IdValues: []bchain.TokenTransferIdValue{ + Type: bchain.MultiToken, + MultiTokenValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(11), Value: *big.NewInt(111), @@ -992,22 +1026,22 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.ERC20, + Type: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 2, Ids: []big.Int{*big.NewInt(2)}, }, { - Type: bchain.ERC1155, + Type: bchain.MultiToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), Txs: 2, - IdValues: []bchain.TokenTransferIdValue{ + MultiTokenValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(11), Value: *big.NewInt(56900), @@ -1027,8 +1061,8 @@ func Test_addToContracts(t *testing.T) { index: ^1, contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.ERC1155, - IdValues: []bchain.TokenTransferIdValue{ + Type: bchain.MultiToken, + MultiTokenValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(11), Value: *big.NewInt(112), @@ -1045,22 +1079,22 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.ERC20, + Type: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.ERC721, + Type: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 2, Ids: []big.Int{*big.NewInt(2)}, }, { - Type: bchain.ERC1155, + Type: bchain.MultiToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), Txs: 3, - IdValues: []bchain.TokenTransferIdValue{ + MultiTokenValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(11), Value: *big.NewInt(56788), @@ -1119,7 +1153,7 @@ func Test_packUnpackBlockTx(t *testing.T) { from: addressToAddrDesc(dbtestdata.EthAddr20, parser), to: addressToAddrDesc(dbtestdata.EthAddr5d, parser), contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), - transferType: bchain.ERC20, + transferType: bchain.FungibleToken, value: *big.NewInt(10000), }, }, @@ -1137,22 +1171,22 @@ func Test_packUnpackBlockTx(t *testing.T) { from: addressToAddrDesc(dbtestdata.EthAddr20, parser), to: addressToAddrDesc(dbtestdata.EthAddr3e, parser), contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), - transferType: bchain.ERC20, + transferType: bchain.FungibleToken, value: *big.NewInt(987654321), }, { from: addressToAddrDesc(dbtestdata.EthAddr4b, parser), to: addressToAddrDesc(dbtestdata.EthAddr55, parser), contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), - transferType: bchain.ERC721, + transferType: bchain.NonFungibleToken, value: *big.NewInt(13), }, { from: addressToAddrDesc(dbtestdata.EthAddr5d, parser), to: addressToAddrDesc(dbtestdata.EthAddr7b, parser), contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), - transferType: bchain.ERC1155, - idValues: []bchain.TokenTransferIdValue{ + transferType: bchain.MultiToken, + idValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(1234), Value: *big.NewInt(98765), @@ -1222,3 +1256,45 @@ func Test_packUnpackFourByteSignature(t *testing.T) { }) } } + +func Test_packUnpackContractInfo(t *testing.T) { + tests := []struct { + name string + contractInfo bchain.ContractInfo + }{ + { + name: "empty", + contractInfo: bchain.ContractInfo{}, + }, + { + name: "unknown", + contractInfo: bchain.ContractInfo{ + Type: bchain.UnknownTokenType, + Name: "Test contract", + Symbol: "TCT", + Decimals: 18, + CreatedInBlock: 1234567, + DestructedInBlock: 234567890, + }, + }, + { + name: "ERC20", + contractInfo: bchain.ContractInfo{ + Type: bchain.ERC20TokenType, + Name: "GreenContract🟢", + Symbol: "🟢", + Decimals: 0, + CreatedInBlock: 1, + DestructedInBlock: 2, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := packContractInfo(&tt.contractInfo) + if got, err := unpackContractInfo(buf); !reflect.DeepEqual(*got, tt.contractInfo) || err != nil { + t.Errorf("packUnpackContractInfo() = %v, want %v, error %v", *got, tt.contractInfo, err) + } + }) + } +} diff --git a/server/public.go b/server/public.go index be0dc4027e..963056dd57 100644 --- a/server/public.go +++ b/server/public.go @@ -562,7 +562,7 @@ func isOwnAddress(td *TemplateData, a string) bool { } // called from template, returns count of token transfers of given type in a tx -func tokenTransfersCount(tx *api.Tx, t api.TokenType) int { +func tokenTransfersCount(tx *api.Tx, t bchain.TokenTypeName) int { count := 0 for i := range tx.TokenTransfers { if tx.TokenTransfers[i].Type == t { @@ -573,7 +573,7 @@ func tokenTransfersCount(tx *api.Tx, t api.TokenType) int { } // called from template, returns count of tokens in array of given type -func tokenCount(tokens []api.Token, t api.TokenType) int { +func tokenCount(tokens []api.Token, t bchain.TokenTypeName) int { count := 0 for i := range tokens { if tokens[i].Type == t { diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index bd4461c6a3..93367959e0 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -24,7 +24,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Contract Contract 123 (S123) 0.000000000123450123 FAKE

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

Confirmed

Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ERC20 Tokens
ContractTokensTransfers
Contract 740.000000001000123074 S741
Contract 130.000000001000123013 S131
ERC721 Tokens
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
ID 1 S205
Fee: 0.00008794500041041 FAKE
Unconfirmed Transaction!0 FAKE
ERC20 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
0.000871180000950184 S74
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
7.674999999999991915 S13
Fee: 0.000216368 FAKE
Unconfirmed Transaction!0 FAKE
`, + `Trezor Fake Coin Explorer

Address 0.000000000123450123 FAKE

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

Confirmed

Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ERC20 Tokens
ContractTokensTransfers
Contract 740.001000123074 S741
Contract 130.000000001000123013 S131
ERC721 Tokens
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
ID 1 S205
Fee: 0.00008794500041041 FAKE
Unconfirmed Transaction!0 FAKE
ERC20 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
871.180000950184 S74
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
7.674999999999991915 S13
Fee: 0.000216368 FAKE
Unconfirmed Transaction!0 FAKE
`, }, }, { @@ -33,7 +33,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Contract Contract 93 (S93) 0.000000000123450093 FAKE

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

Confirmed

Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ERC1155 Tokens
ContractTokensTransfers
Contract 1111776:1 S111, 1898:10 S1111

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0 FAKE
ERC1155 Token Transfers
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1776:1 S1111898:10 S111
Fee: 0.000081891755740665 FAKE
Unconfirmed Transaction!0 FAKE
`, + `Trezor Fake Coin Explorer

Address 0.000000000123450093 FAKE

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

Confirmed

Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ERC1155 Tokens
ContractTokensTransfers
Contract 1111776:1 S111, 1898:10 S1111

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0 FAKE
ERC1155 Token Transfers
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1776:1 S1111898:10 S111
Fee: 0.000081891755740665 FAKE
Unconfirmed Transaction!0 FAKE
`, }, }, { @@ -42,7 +42,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101

Summary

In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE
Fees0.002081 FAKE
RBFON

Details

Input Data
Transfer
Method ID: 0xa9059cbb
Function: transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101

Summary

In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE
Fees0.002081 FAKE
RBFON

Details

Input Data
Transfer
Method ID: 0xa9059cbb
Function: transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, }, }, { @@ -64,7 +64,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":18,"balance":"1000075074"}],"erc20Contract":{"contract":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","name":"Contract 75","symbol":"S75","decimals":18}}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":12,"balance":"1000075074"}]}`, }, }, { @@ -73,7 +73,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":18,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"erc20Contract":{"contract":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","name":"Contract 123","symbol":"S123","decimals":18},"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"}}}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"}}}`, }, }, { @@ -82,7 +82,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}},"addressAliases":{"0x20cD153de35D469BA46127A0C8F18626b59a256A":{"Type":"ENS","Alias":"address20.eth"}}}`, + `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}},"addressAliases":{"0x20cD153de35D469BA46127A0C8F18626b59a256A":{"Type":"ENS","Alias":"address20.eth"},"0x4af4114F73d1c1C903aC9E0361b379D1291808A2":{"Type":"Contract","Alias":"Contract 74"}}}`, }, }, } diff --git a/server/websocket.go b/server/websocket.go index 27a738275a..ab47f5659e 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -896,7 +896,7 @@ func (s *WebsocketServer) sendOnNewTxAddr(stringAddressDescriptor string, tx *ap } func (s *WebsocketServer) getNewTxSubscriptions(tx *bchain.MempoolTx) map[string]struct{} { - // check if there is any subscription in inputs, outputs and erc20 + // check if there is any subscription in inputs, outputs and token transfers s.addressSubscriptionsLock.Lock() defer s.addressSubscriptionsLock.Unlock() subscribed := make(map[string]struct{}) diff --git a/static/templates/address.html b/static/templates/address.html index c662ff12aa..987ea89e74 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -1,5 +1,5 @@ {{define "specific"}}{{$cs := .CoinShortcut}}{{$addr := .Address}}{{$data := .}} -

{{if $addr.Erc20Contract}}Contract {{$addr.Erc20Contract.Name}} ({{$addr.Erc20Contract.Symbol}}){{else}}Address{{end}} {{formatAmount $addr.BalanceSat}} {{$cs}} +

{{if $addr.ContractInfo}}Contract {{$addr.ContractInfo.Name}} ({{$addr.ContractInfo.Symbol}}){{else}}Address{{end}} {{formatAmount $addr.BalanceSat}} {{$cs}}

{{$addr.AddrStr}} @@ -98,7 +98,7 @@

Confirmed

{{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} - {{range $i, $iv := $t.IdValues}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$t.Symbol}}{{end}} + {{range $i, $iv := $t.MultiTokenValues}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$t.Symbol}}{{end}} {{$t.Transfers}} diff --git a/static/templates/txdetail_ethereumtype.html b/static/templates/txdetail_ethereumtype.html index d47d7f0c47..28bcfe780b 100644 --- a/static/templates/txdetail_ethereumtype.html +++ b/static/templates/txdetail_ethereumtype.html @@ -267,7 +267,7 @@
- {{- range $iv := $tt.Values -}} + {{- range $iv := $tt.MultiTokenValues -}} {{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$tt.Symbol}} {{- end -}}
diff --git a/tests/dbtestdata/dbtestdata_ethereumtype.go b/tests/dbtestdata/dbtestdata_ethereumtype.go index e34012bd0d..41cfac5761 100644 --- a/tests/dbtestdata/dbtestdata_ethereumtype.go +++ b/tests/dbtestdata/dbtestdata_ethereumtype.go @@ -128,6 +128,19 @@ var EthTx4InternalData = &bchain.EthereumInternalData{ }, } +var Block1SpecificData = &bchain.EthereumBlockSpecificData{ + Contracts: []bchain.ContractInfo{ + { + Contract: EthAddrContract4a, + Type: bchain.ERC20TokenType, + Name: "Contract 74", + Symbol: "S74", + Decimals: 12, + CreatedInBlock: 44444, + }, + }, +} + var Block2SpecificData = &bchain.EthereumBlockSpecificData{ InternalDataError: "test error", AddressAliasRecords: []bchain.AddressAliasRecord{ @@ -140,6 +153,12 @@ var Block2SpecificData = &bchain.EthereumBlockSpecificData{ Name: "address20", }, }, + Contracts: []bchain.ContractInfo{ + { + Contract: EthAddrContract4a, + DestructedInBlock: 44445, + }, + }, } type packedAndInternal struct { @@ -182,6 +201,7 @@ func GetTestEthereumTypeBlock1(parser bchain.BlockChainParser) *bchain.Block { packed: EthTx2Packed, internal: EthTx2InternalData, }}, parser), + CoinSpecificData: Block1SpecificData, } } diff --git a/tests/dbtestdata/fakechain_ethereumtype.go b/tests/dbtestdata/fakechain_ethereumtype.go index b19276162c..4ee5047281 100644 --- a/tests/dbtestdata/fakechain_ethereumtype.go +++ b/tests/dbtestdata/fakechain_ethereumtype.go @@ -117,13 +117,15 @@ func (c *fakeBlockChainEthereumType) EthereumTypeGetNonce(addrDesc bchain.Addres return uint64(addrDesc[0]), nil } -func (c *fakeBlockChainEthereumType) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.Erc20Contract, error) { +func (c *fakeBlockChainEthereumType) GetContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.ContractInfo, error) { addresses, _, _ := c.Parser.GetAddressesFromAddrDesc(contractDesc) - return &bchain.Erc20Contract{ - Contract: addresses[0], - Name: "Contract " + strconv.Itoa(int(contractDesc[0])), - Symbol: "S" + strconv.Itoa(int(contractDesc[0])), - Decimals: 18, + return &bchain.ContractInfo{ + Type: bchain.ERC20TokenType, + Contract: addresses[0], + Name: "Contract " + strconv.Itoa(int(contractDesc[0])), + Symbol: "S" + strconv.Itoa(int(contractDesc[0])), + Decimals: 18, + CreatedInBlock: 12345, }, nil } From e0be8aa4001ee33f72c885c7aca2bdd2b7f58432 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 15 Jun 2022 09:32:48 +0200 Subject: [PATCH 059/530] Fiat rates refactor, fetch rates for tokens --- api/types.go | 21 +- api/worker.go | 55 +-- blockbook.go | 4 +- .../ethereum_testnet_ropsten_archive.json | 2 + db/fiat.go | 240 +++++++--- db/fiat_test.go | 146 +++++- fiat/coingecko.go | 423 +++++++++++++++--- fiat/fiat_rates.go | 211 +++------ fiat/fiat_rates_test.go | 201 +++++++-- 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/coinlist.json | 30 ++ fiat/mock_data/current.json | 1 - fiat/mock_data/market_chart_eth_other.json | 23 + fiat/mock_data/market_chart_eth_usd_1.json | 14 + fiat/mock_data/market_chart_eth_usd_max.json | 17 + fiat/mock_data/market_chart_token_other.json | 23 + fiat/mock_data/simpleprice_base.json | 12 + fiat/mock_data/simpleprice_tokens.json | 8 + fiat/mock_data/vs_currencies.json | 10 + server/public.go | 12 +- server/public_test.go | 24 +- server/websocket.go | 10 +- 36 files changed, 1094 insertions(+), 408 deletions(-) delete mode 100644 fiat/mock_data/01-02-2013.json delete mode 100644 fiat/mock_data/01-05-2013.json delete mode 100644 fiat/mock_data/04-04-2013.json delete mode 100644 fiat/mock_data/05-05-2013.json delete mode 100644 fiat/mock_data/05-06-2013.json delete mode 100644 fiat/mock_data/07-10-2013.json delete mode 100644 fiat/mock_data/13-06-2014.json delete mode 100644 fiat/mock_data/20-04-2013.json delete mode 100644 fiat/mock_data/20-11-2019.json delete mode 100644 fiat/mock_data/21-11-2019.json delete mode 100644 fiat/mock_data/22-11-2019.json delete mode 100644 fiat/mock_data/23-09-2011.json delete mode 100644 fiat/mock_data/27-04-2013.json delete mode 100644 fiat/mock_data/28-04-2013.json delete mode 100644 fiat/mock_data/29-04-2013.json create mode 100644 fiat/mock_data/coinlist.json delete mode 100644 fiat/mock_data/current.json create mode 100644 fiat/mock_data/market_chart_eth_other.json create mode 100644 fiat/mock_data/market_chart_eth_usd_1.json create mode 100644 fiat/mock_data/market_chart_eth_usd_max.json create mode 100644 fiat/mock_data/market_chart_token_other.json create mode 100644 fiat/mock_data/simpleprice_base.json create mode 100644 fiat/mock_data/simpleprice_tokens.json create mode 100644 fiat/mock_data/vs_currencies.json diff --git a/api/types.go b/api/types.go index c8241e3679..d1d200e002 100644 --- a/api/types.go +++ b/api/types.go @@ -331,7 +331,7 @@ type BalanceHistory struct { ReceivedSat *Amount `json:"received"` SentSat *Amount `json:"sent"` SentToSelfSat *Amount `json:"sentToSelf"` - FiatRates map[string]float64 `json:"rates,omitempty"` + FiatRates map[string]float32 `json:"rates,omitempty"` Txid string `json:"txid,omitempty"` } @@ -468,3 +468,22 @@ type MempoolTxids struct { Mempool []MempoolTxid `json:"mempool"` MempoolSize int `json:"mempoolSize"` } + +// FiatTicker contains formatted CurrencyRatesTicker data +type FiatTicker struct { + Timestamp int64 `json:"ts,omitempty"` + Rates map[string]float32 `json:"rates"` + Error string `json:"error,omitempty"` +} + +// FiatTickers contains a formatted CurrencyRatesTicker list +type FiatTickers struct { + Tickers []FiatTicker `json:"tickers"` +} + +// AvailableVsCurrencies contains formatted data about available versus currencies for exchange rates +type AvailableVsCurrencies struct { + Timestamp int64 `json:"ts,omitempty"` + Tickers []string `json:"available_currencies"` + Error string `json:"error,omitempty"` +} diff --git a/api/worker.go b/api/worker.go index 0fad5f2be9..cc9d1af606 100644 --- a/api/worker.go +++ b/api/worker.go @@ -1342,7 +1342,8 @@ func (w *Worker) setFiatRateToBalanceHistories(histories BalanceHistories, curre for i := range histories { bh := &histories[i] t := time.Unix(int64(bh.Time), 0) - ticker, err := w.db.FiatRatesFindTicker(&t) + // TODO + ticker, err := w.db.FiatRatesFindTicker(&t, "", "") if err != nil { glog.Errorf("Error finding ticker by date %v. Error: %v", t, err) continue @@ -1352,7 +1353,7 @@ func (w *Worker) setFiatRateToBalanceHistories(histories BalanceHistories, curre if len(currencies) == 0 { bh.FiatRates = ticker.Rates } else { - rates := make(map[string]float64) + rates := make(map[string]float32) for _, currency := range currencies { currency = strings.ToLower(currency) if rate, found := ticker.Rates[currency]; found { @@ -1593,17 +1594,17 @@ func removeEmpty(stringSlice []string) []string { } // getFiatRatesResult checks if CurrencyRatesTicker contains all necessary data and returns formatted result -func (w *Worker) getFiatRatesResult(currencies []string, ticker *db.CurrencyRatesTicker) (*db.ResultTickerAsString, error) { +func (w *Worker) getFiatRatesResult(currencies []string, ticker *db.CurrencyRatesTicker) (*FiatTicker, error) { currencies = removeEmpty(currencies) if len(currencies) == 0 { // Return all available ticker rates - return &db.ResultTickerAsString{ + return &FiatTicker{ Timestamp: ticker.Timestamp.UTC().Unix(), Rates: ticker.Rates, }, nil } // Check if currencies from the list are available in the ticker rates - rates := make(map[string]float64) + rates := make(map[string]float32) for _, currency := range currencies { currency = strings.ToLower(currency) if rate, found := ticker.Rates[currency]; found { @@ -1612,25 +1613,26 @@ func (w *Worker) getFiatRatesResult(currencies []string, ticker *db.CurrencyRate rates[currency] = -1 } } - return &db.ResultTickerAsString{ + return &FiatTicker{ Timestamp: ticker.Timestamp.UTC().Unix(), Rates: rates, }, nil } // GetFiatRatesForBlockID returns fiat rates for block height or block hash -func (w *Worker) GetFiatRatesForBlockID(bid string, currencies []string) (*db.ResultTickerAsString, error) { +func (w *Worker) GetFiatRatesForBlockID(blockID string, currencies []string) (*FiatTicker, error) { var ticker *db.CurrencyRatesTicker - bi, err := w.getBlockInfoFromBlockID(bid) + bi, err := w.getBlockInfoFromBlockID(blockID) 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", blockID), true) } - return nil, NewAPIError(fmt.Sprintf("Block %v not found, error: %v", bid, err), false) + return nil, NewAPIError(fmt.Sprintf("Block %v not found, error: %v", blockID, err), false) } dbi := &db.BlockInfo{Time: bi.Time} // get Unix timestamp from block tm := time.Unix(dbi.Time, 0) // convert it to Time object - ticker, err = w.db.FiatRatesFindTicker(&tm) + // TODO + ticker, err = w.db.FiatRatesFindTicker(&tm, "", "") if err != nil { return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false) } else if ticker == nil { @@ -1644,8 +1646,9 @@ func (w *Worker) GetFiatRatesForBlockID(bid string, currencies []string) (*db.Re } // GetCurrentFiatRates returns last available fiat rates -func (w *Worker) GetCurrentFiatRates(currencies []string) (*db.ResultTickerAsString, error) { - ticker, err := w.db.FiatRatesFindLastTicker() +func (w *Worker) GetCurrentFiatRates(currencies []string) (*FiatTicker, error) { + // TODO + ticker, err := w.db.FiatRatesFindLastTicker("", "") if err != nil { return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false) } else if ticker == nil { @@ -1660,8 +1663,8 @@ func (w *Worker) GetCurrentFiatRates(currencies []string) (*db.ResultTickerAsStr // makeErrorRates returns a map of currrencies, with each value equal to -1 // used when there was an error finding ticker -func makeErrorRates(currencies []string) map[string]float64 { - rates := make(map[string]float64) +func makeErrorRates(currencies []string) map[string]float32 { + rates := make(map[string]float32) for _, currency := range currencies { rates[strings.ToLower(currency)] = -1 } @@ -1669,28 +1672,29 @@ func makeErrorRates(currencies []string) map[string]float64 { } // GetFiatRatesForTimestamps returns fiat rates for each of the provided dates -func (w *Worker) GetFiatRatesForTimestamps(timestamps []int64, currencies []string) (*db.ResultTickersAsString, error) { +func (w *Worker) GetFiatRatesForTimestamps(timestamps []int64, currencies []string) (*FiatTickers, error) { if len(timestamps) == 0 { return nil, NewAPIError("No timestamps provided", true) } currencies = removeEmpty(currencies) - ret := &db.ResultTickersAsString{} + ret := &FiatTickers{} for _, timestamp := range timestamps { date := time.Unix(timestamp, 0) date = date.UTC() - ticker, err := w.db.FiatRatesFindTicker(&date) + // TODO + ticker, err := w.db.FiatRatesFindTicker(&date, "", "") if err != nil { glog.Errorf("Error finding ticker for date %v. Error: %v", date, err) - ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)}) + ret.Tickers = append(ret.Tickers, FiatTicker{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)}) continue } else if ticker == nil { - ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)}) + ret.Tickers = append(ret.Tickers, FiatTicker{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)}) continue } result, err := w.getFiatRatesResult(currencies, ticker) if err != nil { - ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)}) + ret.Tickers = append(ret.Tickers, FiatTicker{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)}) continue } ret.Tickers = append(ret.Tickers, *result) @@ -1698,12 +1702,13 @@ func (w *Worker) GetFiatRatesForTimestamps(timestamps []int64, currencies []stri return ret, nil } -// GetFiatRatesTickersList returns the list of available fiatRates tickers -func (w *Worker) GetFiatRatesTickersList(timestamp int64) (*db.ResultTickerListAsString, error) { +// GetAvailableVsCurrencies returns the list of available versus currencies for exchange rates +func (w *Worker) GetAvailableVsCurrencies(timestamp int64) (*AvailableVsCurrencies, error) { date := time.Unix(timestamp, 0) date = date.UTC() - ticker, err := w.db.FiatRatesFindTicker(&date) + // TODO + ticker, err := w.db.FiatRatesFindTicker(&date, "", "") if err != nil { return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false) } else if ticker == nil { @@ -1716,7 +1721,7 @@ func (w *Worker) GetFiatRatesTickersList(timestamp int64) (*db.ResultTickerListA } sort.Strings(keys) // sort to get deterministic results - return &db.ResultTickerListAsString{ + return &AvailableVsCurrencies{ Timestamp: ticker.Timestamp.Unix(), Tickers: keys, }, nil diff --git a/blockbook.go b/blockbook.go index 85fab7771b..304c6f662e 100644 --- a/blockbook.go +++ b/blockbook.go @@ -688,9 +688,9 @@ func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, configfile string) } if config.FiatRates == "" || config.FiatRatesParams == "" { - glog.Infof("FiatRates config (%v) is empty, not downloading fiat rates.", configfile) + glog.Infof("FiatRates config (%v) is empty, not downloading fiat rates", configfile) } else { - fiatRates, err := fiat.NewFiatRatesDownloader(db, config.FiatRates, config.FiatRatesParams, nil, onNewFiatRatesTicker) + fiatRates, err := fiat.NewFiatRatesDownloader(db, config.FiatRates, config.FiatRatesParams, onNewFiatRatesTicker) if err != nil { glog.Errorf("NewFiatRatesDownloader Init error: %v", err) } else { diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index 35bf48aa65..e34437dc34 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -53,6 +53,8 @@ "mempoolTxTimeoutHours": 12, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } diff --git a/db/fiat.go b/db/fiat.go index c2fa7325b3..e4b57f794e 100644 --- a/db/fiat.go +++ b/db/fiat.go @@ -1,9 +1,13 @@ package db import ( - "encoding/json" + "encoding/binary" + "math" + "sync" "time" + vlq "github.com/bsm/go-vlq" + "github.com/flier/gorocksdb" "github.com/golang/glog" "github.com/juju/errors" ) @@ -11,29 +15,83 @@ import ( // FiatRatesTimeFormat is a format string for storing FiatRates timestamps in rocksdb const FiatRatesTimeFormat = "20060102150405" // YYYYMMDDhhmmss +var tickersMux sync.Mutex +var lastTickerInDB *CurrencyRatesTicker +var currentTicker *CurrencyRatesTicker + // CurrencyRatesTicker contains coin ticker data fetched from API type CurrencyRatesTicker struct { - Timestamp *time.Time // return as unix timestamp in API - Rates map[string]float64 + Timestamp time.Time // return as unix timestamp in API + Rates map[string]float32 // rates of the base currency against a list of vs currencies + TokenRates map[string]float32 // rates of the tokens (identified by the address of the contract) against the base currency +} + +func packTimestamp(t *time.Time) []byte { + return []byte(t.UTC().Format(FiatRatesTimeFormat)) } -// ResultTickerAsString contains formatted CurrencyRatesTicker data -type ResultTickerAsString struct { - Timestamp int64 `json:"ts,omitempty"` - Rates map[string]float64 `json:"rates"` - Error string `json:"error,omitempty"` +func packFloat32(buf []byte, n float32) int { + binary.BigEndian.PutUint32(buf, math.Float32bits(n)) + return 4 } -// ResultTickersAsString contains a formatted CurrencyRatesTicker list -type ResultTickersAsString struct { - Tickers []ResultTickerAsString `json:"tickers"` +func unpackFloat32(buf []byte) (float32, int) { + return math.Float32frombits(binary.BigEndian.Uint32(buf)), 4 +} + +func packCurrencyRatesTicker(ticker *CurrencyRatesTicker) []byte { + buf := make([]byte, 0, 32) + varBuf := make([]byte, vlq.MaxLen64) + l := packVaruint(uint(len(ticker.Rates)), varBuf) + buf = append(buf, varBuf[:l]...) + for c, v := range ticker.Rates { + buf = append(buf, packString(c)...) + l = packFloat32(varBuf, v) + buf = append(buf, varBuf[:l]...) + } + l = packVaruint(uint(len(ticker.TokenRates)), varBuf) + buf = append(buf, varBuf[:l]...) + for c, v := range ticker.TokenRates { + buf = append(buf, packString(c)...) + l = packFloat32(varBuf, v) + buf = append(buf, varBuf[:l]...) + } + return buf } -// ResultTickerListAsString contains formatted data about available currency tickers -type ResultTickerListAsString struct { - Timestamp int64 `json:"ts,omitempty"` - Tickers []string `json:"available_currencies"` - Error string `json:"error,omitempty"` +func unpackCurrencyRatesTicker(buf []byte) (*CurrencyRatesTicker, error) { + var ( + ticker CurrencyRatesTicker + s string + l int + len uint + v float32 + ) + len, l = unpackVaruint(buf) + buf = buf[l:] + if len > 0 { + ticker.Rates = make(map[string]float32, len) + for i := 0; i < int(len); i++ { + s, l = unpackString(buf) + buf = buf[l:] + v, l = unpackFloat32(buf) + buf = buf[l:] + ticker.Rates[s] = v + } + } + len, l = unpackVaruint(buf) + buf = buf[l:] + if len > 0 { + ticker.TokenRates = make(map[string]float32, len) + for i := 0; i < int(len); i++ { + s, l = unpackString(buf) + buf = buf[l:] + v, l = unpackFloat32(buf) + buf = buf[l:] + ticker.TokenRates[s] = v + } + } + return &ticker, nil } // FiatRatesConvertDate checks if the date is in correct format and returns the Time object. @@ -51,85 +109,131 @@ func FiatRatesConvertDate(date string) (*time.Time, error) { } // FiatRatesStoreTicker stores ticker data at the specified time -func (d *RocksDB) FiatRatesStoreTicker(ticker *CurrencyRatesTicker) error { +func (d *RocksDB) FiatRatesStoreTicker(wb *gorocksdb.WriteBatch, 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) + wb.PutCF(d.cfh[cfFiatRates], packTimestamp(&ticker.Timestamp), packCurrencyRatesTicker(ticker)) + return nil +} + +func getTickerFromIterator(it *gorocksdb.Iterator, vsCurrency string, token string) (*CurrencyRatesTicker, error) { + timeObj, err := time.Parse(FiatRatesTimeFormat, string(it.Key().Data())) if err != nil { - glog.Error("Error marshalling ticker rates: ", err) - return err + return nil, err } - timeFormatted := ticker.Timestamp.UTC().Format(FiatRatesTimeFormat) - err = d.db.PutCF(d.wo, d.cfh[cfFiatRates], []byte(timeFormatted), ratesMarshalled) + ticker, err := unpackCurrencyRatesTicker(it.Value().Data()) if err != nil { - glog.Error("Error storing ticker: ", err) - return err + return nil, err } - return nil + if vsCurrency != "" { + if ticker.Rates == nil { + return nil, nil + } + if _, found := ticker.Rates[vsCurrency]; !found { + return nil, nil + } + } + if token != "" { + if ticker.TokenRates == nil { + return nil, nil + } + if _, found := ticker.TokenRates[token]; !found { + return nil, nil + } + } + ticker.Timestamp = timeObj.UTC() + return ticker, nil } -// FiatRatesFindTicker gets FiatRates data closest to the specified timestamp -func (d *RocksDB) FiatRatesFindTicker(tickerTime *time.Time) (*CurrencyRatesTicker, error) { - ticker := &CurrencyRatesTicker{} +// FiatRatesGetTicker gets FiatRates ticker at the specified timestamp if it exist +func (d *RocksDB) FiatRatesGetTicker(tickerTime *time.Time) (*CurrencyRatesTicker, error) { + tickerTimeFormatted := tickerTime.UTC().Format(FiatRatesTimeFormat) + val, err := d.db.GetCF(d.ro, d.cfh[cfFiatRates], []byte(tickerTimeFormatted)) + if err != nil { + return nil, err + } + defer val.Free() + data := val.Data() + if len(data) == 0 { + return nil, nil + } + ticker, err := unpackCurrencyRatesTicker(data) + if err != nil { + return nil, err + } + ticker.Timestamp = tickerTime.UTC() + return ticker, nil +} + +// FiatRatesFindTicker gets FiatRates data closest to the specified timestamp, of the base currency, vsCurrency or the token if specified +func (d *RocksDB) FiatRatesFindTicker(tickerTime *time.Time, vsCurrency string, token string) (*CurrencyRatesTicker, error) { + tickersMux.Lock() + if currentTicker != nil && lastTickerInDB != nil { + if tickerTime.After(lastTickerInDB.Timestamp) { + f := true + if token != "" && currentTicker.TokenRates != nil { + _, f = currentTicker.TokenRates[token] + } + if f { + tickersMux.Unlock() + return currentTicker, nil + } + } + } + tickersMux.Unlock() + 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())) + ticker, err := getTickerFromIterator(it, vsCurrency, token) if err != nil { - glog.Error("FiatRatesFindTicker time parse error: ", err) + glog.Error("FiatRatesFindTicker 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 + if ticker != nil { + return ticker, nil } - 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 + return nil, nil } -// FiatRatesFindLastTicker gets the last FiatRates record -func (d *RocksDB) FiatRatesFindLastTicker() (*CurrencyRatesTicker, error) { - ticker := &CurrencyRatesTicker{} +// FiatRatesFindLastTicker gets the last FiatRates record, of the base currency, vsCurrency or the token if specified +func (d *RocksDB) FiatRatesFindLastTicker(vsCurrency string, token string) (*CurrencyRatesTicker, error) { 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())) + for it.SeekToLast(); it.Valid(); it.Prev() { + ticker, err := getTickerFromIterator(it, vsCurrency, token) if err != nil { - glog.Error("FiatRatesFindTicker time parse error: ", err) + glog.Error("FiatRatesFindLastTicker 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 + if ticker != nil { + // if without filter, store the ticker for later use + if vsCurrency == "" && token == "" { + tickersMux.Lock() + lastTickerInDB = ticker + tickersMux.Unlock() + } + return ticker, nil } - 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 + return nil, nil +} + +// FiatRatesGetCurrentTicker return current ticker +func (d *RocksDB) FiatRatesGetCurrentTicker(tickerTime *time.Time, token string) (*CurrencyRatesTicker, error) { + tickersMux.Lock() + defer tickersMux.Unlock() + return currentTicker, nil +} + +// FiatRatesCurrentTicker return current ticker +func (d *RocksDB) FiatRatesSetCurrentTicker(t *CurrencyRatesTicker) { + tickersMux.Lock() + defer tickersMux.Unlock() + currentTicker = t } diff --git a/db/fiat_test.go b/db/fiat_test.go index 95e83eab74..b2c2e7cff2 100644 --- a/db/fiat_test.go +++ b/db/fiat_test.go @@ -3,8 +3,11 @@ package db import ( + "reflect" "testing" "time" + + "github.com/flier/gorocksdb" ) func TestRocksTickers(t *testing.T) { @@ -30,34 +33,63 @@ func TestRocksTickers(t *testing.T) { } // Test storing & finding tickers - key, _ := time.Parse(FiatRatesTimeFormat, "20190627000000") + pastKey, _ := time.Parse(FiatRatesTimeFormat, "20190627000000") futureKey, _ := time.Parse(FiatRatesTimeFormat, "20190630000000") ts1, _ := time.Parse(FiatRatesTimeFormat, "20190628000000") ticker1 := &CurrencyRatesTicker{ - Timestamp: &ts1, - Rates: map[string]float64{ + Timestamp: ts1, + Rates: map[string]float32{ "usd": 20000, + "eur": 18000, + }, + TokenRates: map[string]float32{ + "0x6B175474E89094C44Da98b954EedeAC495271d0F": 17.2, }, } ts2, _ := time.Parse(FiatRatesTimeFormat, "20190629000000") ticker2 := &CurrencyRatesTicker{ - Timestamp: &ts2, - Rates: map[string]float64{ + Timestamp: ts2, + Rates: map[string]float32{ "usd": 30000, }, + TokenRates: map[string]float32{ + "0x82dF128257A7d7556262E1AB7F1f639d9775B85E": 13.1, + "0x6B175474E89094C44Da98b954EedeAC495271d0F": 17.5, + }, + } + + wb := gorocksdb.NewWriteBatch() + defer wb.Destroy() + err := d.FiatRatesStoreTicker(wb, ticker1) + if err != nil { + t.Errorf("Error storing ticker! %v", err) } - err := d.FiatRatesStoreTicker(ticker1) + err = d.FiatRatesStoreTicker(wb, ticker2) if err != nil { t.Errorf("Error storing ticker! %v", err) } - d.FiatRatesStoreTicker(ticker2) + err = d.WriteBatch(wb) if err != nil { t.Errorf("Error storing ticker! %v", err) } - ticker, err := d.FiatRatesFindTicker(&key) // should find the closest key (ticker1) + // test FiatRatesGetTicker with ticker that should be in DB + t1, err := d.FiatRatesGetTicker(&ts1) + if err != nil || t1 == nil { + t.Fatalf("FiatRatesGetTicker t1 %v", err) + } + if !reflect.DeepEqual(t1, ticker1) { + t.Fatalf("FiatRatesGetTicker(t1) = %v, want %v", *t1, *ticker1) + } + // test FiatRatesGetTicker with ticker that is not in DB + t2, err := d.FiatRatesGetTicker(&pastKey) + if err != nil || t2 != nil { + t.Fatalf("FiatRatesGetTicker t2 %v, %v", err, t2) + } + + ticker, err := d.FiatRatesFindTicker(&pastKey, "", "") // should find the closest key (ticker1) if err != nil { t.Errorf("TestRocksTickers err: %+v", err) } else if ticker == nil { @@ -66,7 +98,7 @@ func TestRocksTickers(t *testing.T) { t.Errorf("Incorrect ticker found. Expected: %v, found: %+v", ticker1.Timestamp, ticker.Timestamp) } - ticker, err = d.FiatRatesFindLastTicker() // should find the last key (ticker2) + ticker, err = d.FiatRatesFindLastTicker("", "") // should find the last key (ticker2) if err != nil { t.Errorf("TestRocksTickers err: %+v", err) } else if ticker == nil { @@ -75,10 +107,104 @@ func TestRocksTickers(t *testing.T) { t.Errorf("Incorrect ticker found. Expected: %v, found: %+v", ticker1.Timestamp, ticker.Timestamp) } - ticker, err = d.FiatRatesFindTicker(&futureKey) // should not find anything + 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.") } + + ticker, err = d.FiatRatesFindTicker(&pastKey, "", "0x6B175474E89094C44Da98b954EedeAC495271d0F") // 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.FiatRatesFindTicker(&pastKey, "", "0x82dF128257A7d7556262E1AB7F1f639d9775B85E") // 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", ticker2.Timestamp, ticker.Timestamp) + } + + ticker, err = d.FiatRatesFindLastTicker("eur", "") // 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("usd", "") // 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", ticker2.Timestamp, ticker.Timestamp) + } + + ticker, err = d.FiatRatesFindLastTicker("aud", "") // should not find any key + if err != nil { + t.Errorf("TestRocksTickers err: %+v", err) + } else if ticker != nil { + t.Errorf("Ticker %v found unexpectedly for aud vsCurrency", ticker) + } + +} + +func Test_packUnpackCurrencyRatesTicker(t *testing.T) { + type args struct { + } + tests := []struct { + name string + data CurrencyRatesTicker + }{ + { + name: "empty", + data: CurrencyRatesTicker{}, + }, + { + name: "rates", + data: CurrencyRatesTicker{ + Rates: map[string]float32{ + "usd": 2129.2341123, + "eur": 1332.51234, + }, + }, + }, + { + name: "rates&tokenrates", + data: CurrencyRatesTicker{ + Rates: map[string]float32{ + "usd": 322129.987654321, + "eur": 291332.12345678, + }, + TokenRates: map[string]float32{ + "0x82dF128257A7d7556262E1AB7F1f639d9775B85E": 0.4092341123, + "0x6B175474E89094C44Da98b954EedeAC495271d0F": 12.32323232323232, + "0xdAC17F958D2ee523a2206206994597C13D831ec7": 1332421341235.51234, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + packed := packCurrencyRatesTicker(&tt.data) + got, err := unpackCurrencyRatesTicker(packed) + if err != nil { + t.Errorf("unpackCurrencyRatesTicker() error = %v", err) + return + } + if !reflect.DeepEqual(got, &tt.data) { + t.Errorf("unpackCurrencyRatesTicker() = %v, want %v", *got, tt.data) + } + }) + } } diff --git a/fiat/coingecko.go b/fiat/coingecko.go index 7ce07f9ba9..d800d654bf 100644 --- a/fiat/coingecko.go +++ b/fiat/coingecko.go @@ -2,12 +2,15 @@ package fiat import ( "encoding/json" - "errors" + "fmt" "io/ioutil" "net/http" + "net/url" "strconv" + "strings" "time" + "github.com/flier/gorocksdb" "github.com/golang/glog" "github.com/trezor/blockbook/db" ) @@ -16,121 +19,405 @@ import ( type Coingecko struct { url string coin string + platformIdentifier string + platformVsCurrency string httpTimeoutSeconds time.Duration + throttlingDelay time.Duration timeFormat string + httpClient *http.Client + db *db.RocksDB + updatingTokens bool +} + +// simpleSupportedVSCurrencies https://api.coingecko.com/api/v3/simple/supported_vs_currencies +type simpleSupportedVSCurrencies []string + +type coinsListItem struct { + ID string `json:"id"` + Symbol string `json:"symbol"` + Name string `json:"name"` + Platforms map[string]string `json:"platforms"` +} + +// coinList https://api.coingecko.com/api/v3/coins/list +type coinList []coinsListItem + +type marketPoint [2]float32 +type marketChartPrices struct { + Prices []marketPoint `json:"prices"` } // NewCoinGeckoDownloader creates a coingecko structure that implements the RatesDownloaderInterface -func NewCoinGeckoDownloader(url string, coin string, timeFormat string) RatesDownloaderInterface { +func NewCoinGeckoDownloader(db *db.RocksDB, url string, coin string, platformIdentifier string, platformVsCurrency string, timeFormat string, throttlingDelayMs int) RatesDownloaderInterface { + httpTimeoutSeconds := 15 * time.Second return &Coingecko{ url: url, coin: coin, - httpTimeoutSeconds: 15 * time.Second, + platformIdentifier: platformIdentifier, + platformVsCurrency: platformVsCurrency, + httpTimeoutSeconds: httpTimeoutSeconds, timeFormat: timeFormat, + httpClient: &http.Client{ + Timeout: httpTimeoutSeconds, + }, + db: db, + throttlingDelay: time.Duration(throttlingDelayMs) * time.Millisecond, } } -// 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" +// doReq HTTP client +func doReq(req *http.Request, client *http.Client) ([]byte, error) { + resp, err := client.Do(req) + if err != nil { + return nil, err } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, fmt.Errorf("%s", body) + } + return body, nil +} - req, err := http.NewRequest("GET", requestURL, nil) +// makeReq HTTP request helper - will retry the call after 1 minute on error +func (cg *Coingecko) makeReq(url string) ([]byte, error) { + for { + // glog.Infof("Coingecko makeReq %v", url) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + resp, err := doReq(req, cg.httpClient) + if err == nil { + return resp, err + } + if err.Error() != "error code: 1015" { + glog.Errorf("Coingecko makeReq %v error %v", url, err) + return nil, err + } + // if there is a throttling error, wait 70 seconds and retry + glog.Errorf("Coingecko makeReq %v error %v, will retry in 70 seconds", url, err) + time.Sleep(70 * time.Second) + } +} + +// SimpleSupportedVSCurrencies /simple/supported_vs_currencies +func (cg *Coingecko) simpleSupportedVSCurrencies() (simpleSupportedVSCurrencies, error) { + url := cg.url + "/simple/supported_vs_currencies" + resp, err := cg.makeReq(url) 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") + var data simpleSupportedVSCurrencies + err = json.Unmarshal(resp, &data) + if err != nil { + return nil, err + } + return data, nil +} - // Add query parameters - q := req.URL.Query() +// SimplePrice /simple/price Multiple ID and Currency (ids, vs_currencies) +func (cg *Coingecko) simplePrice(ids []string, vsCurrencies []string) (*map[string]map[string]float32, error) { + params := url.Values{} + idsParam := strings.Join(ids, ",") + vsCurrenciesParam := strings.Join(vsCurrencies, ",") - // Add a unix timestamp to query parameters to get uncached responses - currentTimestamp := strconv.FormatInt(time.Now().UTC().UnixNano(), 10) - q.Add("current_timestamp", currentTimestamp) + params.Add("ids", idsParam) + params.Add("vs_currencies", vsCurrenciesParam) - 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) + url := fmt.Sprintf("%s/simple/price?%s", cg.url, params.Encode()) + resp, err := cg.makeReq(url) + if err != nil { + return nil, err } - req.URL.RawQuery = q.Encode() - client := &http.Client{ - Timeout: cg.httpTimeoutSeconds, - } - resp, err := client.Do(req) + t := make(map[string]map[string]float32) + err = json.Unmarshal(resp, &t) 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)) + + return &t, nil +} + +// CoinsList /coins/list +func (cg *Coingecko) coinsList() (coinList, error) { + params := url.Values{} + platform := "false" + if cg.platformIdentifier != "" { + platform = "true" } - bodyBytes, err := ioutil.ReadAll(resp.Body) + params.Add("include_platform", platform) + url := fmt.Sprintf("%s/coins/list?%s", cg.url, params.Encode()) + resp, err := cg.makeReq(url) + if err != nil { + return nil, err + } + + var data coinList + err = json.Unmarshal(resp, &data) if err != nil { return nil, err } - return bodyBytes, nil + return data, 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 +// coinMarketChart /coins/{id}/market_chart?vs_currency={usd, eur, jpy, etc.}&days={1,14,30,max} +func (cg *Coingecko) coinMarketChart(id string, vs_currency string, days string) (*marketChartPrices, error) { + if len(id) == 0 || len(vs_currency) == 0 || len(days) == 0 { + return nil, fmt.Errorf("id, vs_currency, and days is required") } - dataTimestampUTC := dataTimestamp.UTC() - ticker := &db.CurrencyRatesTicker{Timestamp: &dataTimestampUTC} - bodyBytes, err := cg.makeRequest(timestamp) + + params := url.Values{} + params.Add("interval", "daily") + params.Add("vs_currency", vs_currency) + params.Add("days", days) + + url := fmt.Sprintf("%s/coins/%s/market_chart?%s", cg.url, id, params.Encode()) + resp, err := cg.makeReq(url) if err != nil { return nil, err } - type FiatRatesResponse struct { - MarketData struct { - Prices map[string]float64 `json:"current_price"` - } `json:"market_data"` + m := marketChartPrices{} + err = json.Unmarshal(resp, &m) + if err != nil { + return &m, err } - var data FiatRatesResponse - err = json.Unmarshal(bodyBytes, &data) + return &m, nil +} + +var vsCurrencies []string +var platformIds []string +var platformIdsToTokens map[string]string + +func (cg *Coingecko) platformIds() error { + if cg.platformIdentifier == "" { + return nil + } + cl, err := cg.coinsList() if err != nil { - glog.Errorf("Error parsing FiatRates response: %v", err) + return err + } + idsMap := make(map[string]string, 64) + ids := make([]string, 0, 64) + for i := range cl { + id, found := cl[i].Platforms[cg.platformIdentifier] + if found && id != "" { + idsMap[cl[i].ID] = id + ids = append(ids, cl[i].ID) + } + } + platformIds = ids + platformIdsToTokens = idsMap + return nil +} + +func (cg *Coingecko) CurrentTickers() (*db.CurrencyRatesTicker, error) { + var newTickers = db.CurrencyRatesTicker{} + + if vsCurrencies == nil { + vs, err := cg.simpleSupportedVSCurrencies() + if err != nil { + return nil, err + } + vsCurrencies = vs + } + prices, err := cg.simplePrice([]string{cg.coin}, vsCurrencies) + if err != nil || prices == nil { return nil, err } - ticker.Rates = data.MarketData.Prices - return ticker, nil + newTickers.Rates = make(map[string]float32, len((*prices)[cg.coin])) + for t, v := range (*prices)[cg.coin] { + newTickers.Rates[t] = v + } + + if cg.platformIdentifier != "" && cg.platformVsCurrency != "" { + if platformIdsToTokens == nil { + err = cg.platformIds() + if err != nil { + return nil, err + } + } + newTickers.TokenRates = make(map[string]float32) + const platformIdsGroup = 200 + for from := 0; from < len(platformIds); from += platformIdsGroup { + to := from + platformIdsGroup + if to > len(platformIds) { + to = len(platformIds) + } + tokenPrices, err := cg.simplePrice(platformIds[from:to], []string{cg.platformVsCurrency}) + if err != nil || tokenPrices == nil { + return nil, err + } + for id, v := range *tokenPrices { + t, found := platformIdsToTokens[id] + if found { + newTickers.TokenRates[t] = v[cg.platformVsCurrency] + } + } + } + } + newTickers.Timestamp = time.Now().UTC() + return &newTickers, 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) +func (cg *Coingecko) getHistoricalTicker(tickersToUpdate map[uint]*db.CurrencyRatesTicker, coinId string, vsCurrency string, token string) (bool, error) { + lastTicker, err := cg.db.FiatRatesFindLastTicker(vsCurrency, token) 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 days string + if lastTicker == nil { + days = "max" + } else { + diff := time.Since(lastTicker.Timestamp) + d := int(diff / (24 * 3600 * 1000000000)) + if d == 0 { // nothing to do, the last ticker exist + return false, nil + } + days = strconv.Itoa(d) } - var data FiatRatesResponse - err = json.Unmarshal(resp, &data) + mc, err := cg.coinMarketChart(coinId, vsCurrency, days) if err != nil { - glog.Errorf("Error parsing Coingecko response: %v", err) return false, err } - return len(data.MarketData.Prices) != 0, nil + warningLogged := false + for _, p := range mc.Prices { + var timestamp uint + if p[0] > 100000000000 { + // convert timestamp from milliseconds to seconds + timestamp = uint(p[0] / 1000) + } else { + timestamp = uint(p[0]) + } + rate := p[1] + if timestamp%(24*3600) == 0 && timestamp != 0 && rate != 0 { // process only tickers for the whole day with non 0 value + var found bool + var ticker *db.CurrencyRatesTicker + if ticker, found = tickersToUpdate[timestamp]; !found { + u := time.Unix(int64(timestamp), 0).UTC() + ticker, err = cg.db.FiatRatesGetTicker(&u) + if err != nil { + return false, err + } + if ticker == nil { + if token != "" { // if the base currency is not found in DB, do not create ticker for the token + if !warningLogged { + glog.Warningf("No base currency ticker for date %v for token %s", u, token) + warningLogged = true + } + continue + } + ticker = &db.CurrencyRatesTicker{ + Timestamp: u, + Rates: make(map[string]float32), + } + } + tickersToUpdate[timestamp] = ticker + } + if token == "" { + ticker.Rates[vsCurrency] = rate + } else { + if ticker.TokenRates == nil { + ticker.TokenRates = make(map[string]float32) + } + ticker.TokenRates[token] = rate + } + } + } + return true, nil +} + +func (cg *Coingecko) storeTickers(tickersToUpdate map[uint]*db.CurrencyRatesTicker) error { + if len(tickersToUpdate) > 0 { + wb := gorocksdb.NewWriteBatch() + defer wb.Destroy() + for _, v := range tickersToUpdate { + if err := cg.db.FiatRatesStoreTicker(wb, v); err != nil { + return err + } + } + if err := cg.db.WriteBatch(wb); err != nil { + return err + } + } + return nil +} + +// UpdateHistoricalTickers gets historical tickers for the main crypto currency +func (cg *Coingecko) UpdateHistoricalTickers() error { + tickersToUpdate := make(map[uint]*db.CurrencyRatesTicker) + + // reload vs_currencies + vs, err := cg.simpleSupportedVSCurrencies() + if err != nil { + return err + } + vsCurrencies = vs + + for _, currency := range vsCurrencies { + // get historical rates for each currency + var err error + var req bool + if req, err = cg.getHistoricalTicker(tickersToUpdate, cg.coin, currency, ""); err != nil { + // report error and continue, Coingecko may return error like "Could not find coin with the given id" + // the rates will be updated next run + glog.Errorf("getHistoricalTicker %s-%s %v", cg.coin, currency, err) + } + if req { + time.Sleep(cg.throttlingDelay) + } + } + + return cg.storeTickers(tickersToUpdate) +} + +// UpdateHistoricalTokenTickers gets historical tickers for the tokens +func (cg *Coingecko) UpdateHistoricalTokenTickers() error { + if cg.updatingTokens { + return nil + } + cg.updatingTokens = true + defer func() { cg.updatingTokens = false }() + tickersToUpdate := make(map[uint]*db.CurrencyRatesTicker) + + if cg.platformIdentifier != "" && cg.platformVsCurrency != "" { + // reload platform ids + if err := cg.platformIds(); err != nil { + return err + } + glog.Infof("Coingecko returned %d %s tokens ", len(platformIds), cg.coin) + count := 0 + // get token historical rates + for tokenId, token := range platformIdsToTokens { + var err error + var req bool + if req, err = cg.getHistoricalTicker(tickersToUpdate, tokenId, cg.platformVsCurrency, token); err != nil { + // report error and continue, Coingecko may return error like "Could not find coin with the given id" + // the rates will be updated next run + glog.Errorf("getHistoricalTicker %s-%s %v", tokenId, cg.platformVsCurrency, err) + } + count++ + if count%100 == 0 { + err := cg.storeTickers(tickersToUpdate) + if err != nil { + return err + } + tickersToUpdate = make(map[uint]*db.CurrencyRatesTicker) + glog.Infof("Coingecko updated %d of %d token tickers", count, len(platformIds)) + } + if req { + // long delay next request to avoid throttling + time.Sleep(cg.throttlingDelay * 20) + } + } + } + + return cg.storeTickers(tickersToUpdate) } diff --git a/fiat/fiat_rates.go b/fiat/fiat_rates.go index 5f6df96d88..8ac8fe0763 100644 --- a/fiat/fiat_rates.go +++ b/fiat/fiat_rates.go @@ -4,7 +4,7 @@ import ( "encoding/json" "errors" "fmt" - "reflect" + "math/rand" "time" "github.com/golang/glog" @@ -16,28 +16,29 @@ 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) + CurrentTickers() (*db.CurrencyRatesTicker, error) + UpdateHistoricalTickers() error + UpdateHistoricalTokenTickers() error } // RatesDownloader stores FiatRates API parameters type RatesDownloader struct { - periodSeconds time.Duration + periodSeconds int64 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 initializes 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) { +func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, callback OnNewFiatRatesTicker) (*RatesDownloader, error) { var rd = &RatesDownloader{} type fiatRatesParams struct { - URL string `json:"url"` - Coin string `json:"coin"` - PeriodSeconds int `json:"periodSeconds"` + URL string `json:"url"` + Coin string `json:"coin"` + PlatformIdentifier string `json:"platformIdentifier"` + PlatformVsCurrency string `json:"platformVsCurrency"` + PeriodSeconds int64 `json:"periodSeconds"` } rdParams := &fiatRatesParams{} err := json.Unmarshal([]byte(params), &rdParams) @@ -47,168 +48,62 @@ func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, start 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.timeFormat = "02-01-2006" // Layout string for FiatRates date formatting (DD-MM-YYYY) + rd.periodSeconds = rdParams.PeriodSeconds // Time period for syncing the latest market data + if rd.periodSeconds < 60 { // minimum is one minute + rd.periodSeconds = 60 + } 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) + throttlingDelayMs := 50 + if callback == nil { + // a small hack - in tests the callback is not used, therefore there is no delay slowing the test + throttlingDelayMs = 0 + } + rd.downloader = NewCoinGeckoDownloader(db, rdParams.URL, rdParams.Coin, rdParams.PlatformIdentifier, rdParams.PlatformVsCurrency, rd.timeFormat, throttlingDelayMs) } 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. +// Run periodically downloads current (every 15 minutes) and historical (once a day) tickers 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 -} + var lastHistoricalTickers time.Time -// 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) + tickers, err := rd.downloader.CurrentTickers() + if err != nil && tickers != nil { + glog.Error("FiatRatesDownloader: CurrentTickers error ", err) } else { - minDate = currentDate - currentDate = currentDate.Add(maxDate.Sub(currentDate) / 2) + rd.db.FiatRatesSetCurrentTicker(tickers) + glog.Info("FiatRatesDownloader: CurrentTickers updated") } - } -} - -// syncLatest downloads the latest FiatRates data every rd.PeriodSeconds -func (rd *RatesDownloader) syncLatest() error { - timer := time.NewTimer(rd.periodSeconds) - var lastTickerRates map[string]float64 - 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) + if time.Now().UTC().YearDay() != lastHistoricalTickers.YearDay() || time.Now().UTC().Year() != lastHistoricalTickers.Year() { + err = rd.downloader.UpdateHistoricalTickers() + if err != nil { + glog.Error("FiatRatesDownloader: UpdateHistoricalTickers error ", err) + } else { + lastHistoricalTickers = time.Now().UTC() + glog.Info("FiatRatesDownloader: UpdateHistoricalTickers finished") + } + // UpdateHistoricalTokenTickers in a goroutine, it can take quite some time as there may be many tokens + go func() { + err := rd.downloader.UpdateHistoricalTokenTickers() + if err != nil { + glog.Error("FiatRatesDownloader: UpdateHistoricalTokenTickers error ", err) + } else { + lastHistoricalTickers = time.Now().UTC() + glog.Info("FiatRatesDownloader: UpdateHistoricalTokenTickers finished") + } + }() } - - *timestamp = timestamp.Add(time.Hour * 24) // go to the next day - - <-timer.C - timer.Reset(period) + // next run on the + now := time.Now().Unix() + next := now + rd.periodSeconds + next -= next % rd.periodSeconds + next += int64(rand.Intn(12)) + time.Sleep(time.Duration(next-now) * time.Second) } - return nil } diff --git a/fiat/fiat_rates_test.go b/fiat/fiat_rates_test.go index f119fce9c1..4d7a503f12 100644 --- a/fiat/fiat_rates_test.go +++ b/fiat/fiat_rates_test.go @@ -9,6 +9,7 @@ import ( "net/http" "net/http/httptest" "os" + "reflect" "testing" "time" @@ -66,13 +67,9 @@ func bitcoinTestnetParser() *btc.BitcoinParser { } // getFiatRatesMockData reads a stub JSON response from a file and returns its content as string -func getFiatRatesMockData(dateParam string) (string, error) { +func getFiatRatesMockData(name string) (string, error) { var filename string - if dateParam == "current" { - filename = "fiat/mock_data/current.json" - } else { - filename = "fiat/mock_data/" + dateParam + ".json" - } + filename = "fiat/mock_data/" + name + ".json" mockFile, err := os.Open(filename) if err != nil { glog.Errorf("Cannot open file %v", filename) @@ -98,27 +95,43 @@ func TestFiatRates(t *testing.T) { 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 if r.URL.Path == "/coins/list" { + mockData, err = getFiatRatesMockData("coinlist") + } else if r.URL.Path == "/simple/supported_vs_currencies" { + mockData, err = getFiatRatesMockData("vs_currencies") + } else if r.URL.Path == "/simple/price" { + if r.URL.Query().Get("ids") == "ethereum" { + mockData, err = getFiatRatesMockData("simpleprice_base") + } else { + mockData, err = getFiatRatesMockData("simpleprice_tokens") + } + } else if r.URL.Path == "/coins/ethereum/market_chart" { + vsCurrency := r.URL.Query().Get("vs_currency") + if vsCurrency == "usd" { + days := r.URL.Query().Get("days") + if days == "max" { + mockData, err = getFiatRatesMockData("market_chart_eth_usd_max") + } else { + mockData, err = getFiatRatesMockData("market_chart_eth_usd_1") + } + } else { + mockData, err = getFiatRatesMockData("market_chart_eth_other") + } + } else if r.URL.Path == "/coins/vendit/market_chart" || r.URL.Path == "/coins/ethereum-cash-token/market_chart" { + mockData, err = getFiatRatesMockData("market_chart_token_other") } else { - t.Errorf("Unknown URL path: %v", r.URL.Path) + t.Fatalf("Unknown URL path: %v", r.URL.Path) } if err != nil { - t.Errorf("Error loading stub data: %v", err) + t.Fatalf("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}"}` + configJSON := `{"fiat_rates": "coingecko", "fiat_rates_params": "{\"url\": \"` + mockServer.URL + `\", \"coin\": \"ethereum\",\"platformIdentifier\":\"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 60}"}` type fiatRatesConfig struct { FiatRates string `json:"fiat_rates"` @@ -128,49 +141,157 @@ func TestFiatRates(t *testing.T) { var config fiatRatesConfig err := json.Unmarshal([]byte(configJSON), &config) if err != nil { - t.Errorf("Error parsing config: %v", err) + t.Fatalf("Error parsing config: %v", err) } if config.FiatRates == "" || config.FiatRatesParams == "" { - t.Errorf("Error parsing FiatRates config - empty parameter") + t.Fatalf("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) + fiatRates, err := NewFiatRatesDownloader(d, config.FiatRates, config.FiatRatesParams, nil) if err != nil { - t.Errorf("FiatRates init error: %v\n", err) + t.Fatalf("FiatRates init error: %v", err) } if config.FiatRates == "coingecko" { - timestamp, err := fiatRates.findEarliestMarketData() + + // get current tickers + currentTickers, err := fiatRates.downloader.CurrentTickers() if err != nil { - t.Errorf("Error looking up earliest market data: %v", err) + t.Fatalf("Error in CurrentTickers: %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) + if currentTickers == nil { + t.Fatalf("CurrentTickers returned nil value") 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) + wantCurrentTickers := db.CurrencyRatesTicker{ + Rates: map[string]float32{ + "aed": 8447.1, + "ars": 268901, + "aud": 3314.36, + "btc": 0.07531005, + "eth": 1, + "eur": 2182.99, + "ltc": 29.097696, + "usd": 2299.72, + }, + TokenRates: map[string]float32{ + "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 5.58195e-07, + "0x906710835d1ae85275eb770f06873340ca54274b": 1.39852e-10, + }, + Timestamp: currentTickers.Timestamp, + } + if !reflect.DeepEqual(currentTickers, &wantCurrentTickers) { + t.Fatalf("CurrentTickers() = %v, want %v", *currentTickers, wantCurrentTickers) + } - err = fiatRates.syncHistorical(timestamp) + ticker, err := fiatRates.db.FiatRatesFindLastTicker("usd", "") if err != nil { - t.Errorf("RatesDownloader syncHistorical error: %v", err) - return + t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) + } + if ticker != nil { + t.Fatalf("FiatRatesFindLastTicker found unexpected data") } - ticker, err := fiatRates.downloader.getTicker(fiatRates.startTime) + + // update historical tickers for the first time + err = fiatRates.downloader.UpdateHistoricalTickers() if err != nil { - // Do not exit on GET error, log it, wait and try again - glog.Errorf("Sync GetData error: %v", err) - return + t.Fatalf("UpdateHistoricalTickers 1st pass failed with error: %v", err) } - err = fiatRates.db.FiatRatesStoreTicker(ticker) + err = fiatRates.downloader.UpdateHistoricalTokenTickers() if err != nil { - glog.Errorf("Sync StoreTicker error %v", err) - return + t.Fatalf("UpdateHistoricalTokenTickers 1st pass failed with error: %v", err) + } + + ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "") + if err != nil || ticker == nil { + t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) + } + wantTicker := db.CurrencyRatesTicker{ + Rates: map[string]float32{ + "aed": 241272.48, + "ars": 241272.48, + "aud": 241272.48, + "btc": 241272.48, + "eth": 241272.48, + "eur": 241272.48, + "ltc": 241272.48, + "usd": 1794.5397, + }, + TokenRates: map[string]float32{ + "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.161734e+07, + "0x906710835d1ae85275eb770f06873340ca54274b": 4.161734e+07, + }, + Timestamp: time.Unix(1654732800, 0).UTC(), + } + if !reflect.DeepEqual(ticker, &wantTicker) { + t.Fatalf("UpdateHistoricalTickers(usd) 1st pass = %v, want %v", *ticker, wantTicker) + } + + ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "") + if err != nil || ticker == nil { + t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) + } + wantTicker = db.CurrencyRatesTicker{ + Rates: map[string]float32{ + "aed": 240402.97, + "ars": 240402.97, + "aud": 240402.97, + "btc": 240402.97, + "eth": 240402.97, + "eur": 240402.97, + "ltc": 240402.97, + }, + TokenRates: map[string]float32{ + "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07, + "0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07, + }, + Timestamp: time.Unix(1654819200, 0).UTC(), + } + if !reflect.DeepEqual(ticker, &wantTicker) { + t.Fatalf("UpdateHistoricalTickers(eur) 1st pass = %v, want %v", *ticker, wantTicker) + } + + // update historical tickers for the second time + err = fiatRates.downloader.UpdateHistoricalTickers() + if err != nil { + t.Fatalf("UpdateHistoricalTickers 2nd pass failed with error: %v", err) + } + err = fiatRates.downloader.UpdateHistoricalTokenTickers() + if err != nil { + t.Fatalf("UpdateHistoricalTokenTickers 2nd pass failed with error: %v", err) + } + ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "") + if err != nil || ticker == nil { + t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) + } + wantTicker = db.CurrencyRatesTicker{ + Rates: map[string]float32{ + "aed": 240402.97, + "ars": 240402.97, + "aud": 240402.97, + "btc": 240402.97, + "eth": 240402.97, + "eur": 240402.97, + "ltc": 240402.97, + "usd": 1788.4183, + }, + TokenRates: map[string]float32{ + "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07, + "0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07, + }, + Timestamp: time.Unix(1654819200, 0).UTC(), + } + if !reflect.DeepEqual(ticker, &wantTicker) { + t.Fatalf("UpdateHistoricalTickers(usd) 2nd pass = %v, want %v", *ticker, wantTicker) + } + ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "") + if err != nil || ticker == nil { + t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) + } + if !reflect.DeepEqual(ticker, &wantTicker) { + t.Fatalf("UpdateHistoricalTickers(eur) 2nd pass = %v, want %v", *ticker, wantTicker) } } } diff --git a/fiat/mock_data/01-02-2013.json b/fiat/mock_data/01-02-2013.json deleted file mode 100644 index 94ba1bd575..0000000000 --- a/fiat/mock_data/01-02-2013.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index 060f0e0856..0000000000 --- a/fiat/mock_data/01-05-2013.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index 94ba1bd575..0000000000 --- a/fiat/mock_data/04-04-2013.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index 1cd65273ca..0000000000 --- a/fiat/mock_data/05-05-2013.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index d45b810ae0..0000000000 --- a/fiat/mock_data/05-06-2013.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index 328a45ddaf..0000000000 --- a/fiat/mock_data/07-10-2013.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index c784ee4862..0000000000 --- a/fiat/mock_data/13-06-2014.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index 94ba1bd575..0000000000 --- a/fiat/mock_data/20-04-2013.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index 2dcf90600b..0000000000 --- a/fiat/mock_data/20-11-2019.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index 14dd021d84..0000000000 --- a/fiat/mock_data/21-11-2019.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index 19ad080829..0000000000 --- a/fiat/mock_data/22-11-2019.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index 94ba1bd575..0000000000 --- a/fiat/mock_data/23-09-2011.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index 94ba1bd575..0000000000 --- a/fiat/mock_data/27-04-2013.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index b9f2a4ad92..0000000000 --- a/fiat/mock_data/28-04-2013.json +++ /dev/null @@ -1 +0,0 @@ -{"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 deleted file mode 100644 index d7f64132d7..0000000000 --- a/fiat/mock_data/29-04-2013.json +++ /dev/null @@ -1 +0,0 @@ -{"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/coinlist.json b/fiat/mock_data/coinlist.json new file mode 100644 index 0000000000..ccf9278733 --- /dev/null +++ b/fiat/mock_data/coinlist.json @@ -0,0 +1,30 @@ +[ + { "id": "01coin", "symbol": "zoc", "name": "01coin", "platforms": {} }, + { + "id": "0-5x-long-algorand-token", + "symbol": "algohalf", + "name": "0.5X Long Algorand Token", + "platforms": { "ethereum": "" } + }, + { "id": "ethereum", "symbol": "eth", "name": "Ethereum", "platforms": {} }, + { + "id": "ethereum-cash-token", + "symbol": "ecash", + "name": "Ethereum Cash Token", + "platforms": { "ethereum": "0x906710835d1ae85275eb770f06873340ca54274b" } + }, + { + "id": "santa-shiba", + "symbol": "santashib", + "name": "Santa Shiba", + "platforms": { + "binance-smart-chain": "0x74c609b16512869b1873f5a9d7999deee386e740" + } + }, + { + "id": "vendit", + "symbol": "vndt", + "name": "Vendit", + "platforms": { "ethereum": "0x5e9997684d061269564f94e5d11ba6ce6fa9528c" } + } +] diff --git a/fiat/mock_data/current.json b/fiat/mock_data/current.json deleted file mode 100644 index 7d66dd3eb8..0000000000 --- a/fiat/mock_data/current.json +++ /dev/null @@ -1 +0,0 @@ -{"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/fiat/mock_data/market_chart_eth_other.json b/fiat/mock_data/market_chart_eth_other.json new file mode 100644 index 0000000000..aa416fdb18 --- /dev/null +++ b/fiat/mock_data/market_chart_eth_other.json @@ -0,0 +1,23 @@ +{ + "prices": [ + [1654560000000, 245991.30610738738], + [1654646400000, 241439.61063702923], + [1654732800000, 241272.47868584536], + [1654819200000, 240402.9616407818], + [1654874261000, 232687.7973743471] + ], + "market_caps": [ + [1654560000000, 29783749062026.934], + [1654646400000, 29309140822797.383], + [1654732800000, 29218371977967.83], + [1654819200000, 29135342816603.11], + [1654874261000, 28322159926577.836] + ], + "total_volumes": [ + [1654560000000, 2198234703995.5186], + [1654646400000, 3139844528072.9595], + [1654732800000, 2381462737920.105], + [1654819200000, 1407275835572.8992], + [1654874261000, 1875231811513.972] + ] +} diff --git a/fiat/mock_data/market_chart_eth_usd_1.json b/fiat/mock_data/market_chart_eth_usd_1.json new file mode 100644 index 0000000000..d70ef2e368 --- /dev/null +++ b/fiat/mock_data/market_chart_eth_usd_1.json @@ -0,0 +1,14 @@ +{ + "prices": [ + [1654819200000, 1788.4182866616045], + [1654871975000, 1741.4106052586249] + ], + "market_caps": [ + [1654819200000, 216720355679.05618], + [1654871975000, 210920939953.81134] + ], + "total_volumes": [ + [1654819200000, 10469080004.414614], + [1654871975000, 13875498345.972267] + ] +} diff --git a/fiat/mock_data/market_chart_eth_usd_max.json b/fiat/mock_data/market_chart_eth_usd_max.json new file mode 100644 index 0000000000..9d1fb3bd02 --- /dev/null +++ b/fiat/mock_data/market_chart_eth_usd_max.json @@ -0,0 +1,17 @@ +{ + "prices": [ + [1654560000000, 1860.1813068416047], + [1654646400000, 1818.3877119829308], + [1654732800000, 1794.539625671828] + ], + "market_caps": [ + [1654560000000, 225224111085.68793], + [1654646400000, 220727955347.00992], + [1654732800000, 217320792647.69748] + ], + "total_volumes": [ + [1654560000000, 16623006597.793545], + [1654646400000, 23647547692.445885], + [1654732800000, 17712874976.607395] + ] +} diff --git a/fiat/mock_data/market_chart_token_other.json b/fiat/mock_data/market_chart_token_other.json new file mode 100644 index 0000000000..2a439f387c --- /dev/null +++ b/fiat/mock_data/market_chart_token_other.json @@ -0,0 +1,23 @@ +{ + "prices": [ + [1654560000000, 43129640.779293984], + [1654646400000, 42170403.75197084], + [1654732800000, 41617340.4960857], + [1654819200000, 41464477.97624925], + [1654893557000, 39012012.89610346] + ], + "market_caps": [ + [1654560000000, 5221982916522588], + [1654646400000, 5118923172979404], + [1654732800000, 5039907336185186], + [1654819200000, 5024661446418917], + [1654893557000, 4722632860950729] + ], + "total_volumes": [ + [1654560000000, 385416357318398.5], + [1654646400000, 548412545554966], + [1654732800000, 410780981662688], + [1654819200000, 242725619902352.5], + [1654893557000, 395315245827820.75] + ] +} diff --git a/fiat/mock_data/simpleprice_base.json b/fiat/mock_data/simpleprice_base.json new file mode 100644 index 0000000000..0f098214e1 --- /dev/null +++ b/fiat/mock_data/simpleprice_base.json @@ -0,0 +1,12 @@ +{ + "ethereum": { + "btc": 0.07531005, + "eth": 1.0, + "ltc": 29.097696, + "usd": 2299.72, + "eur": 2182.99, + "aed": 8447.1, + "ars": 268901, + "aud": 3314.36 + } +} diff --git a/fiat/mock_data/simpleprice_tokens.json b/fiat/mock_data/simpleprice_tokens.json new file mode 100644 index 0000000000..3d8e71dab7 --- /dev/null +++ b/fiat/mock_data/simpleprice_tokens.json @@ -0,0 +1,8 @@ +{ + "ethereum-cash-token": { + "eth": 1.39852e-10 + }, + "vendit": { + "eth": 5.58195e-07 + } +} \ No newline at end of file diff --git a/fiat/mock_data/vs_currencies.json b/fiat/mock_data/vs_currencies.json new file mode 100644 index 0000000000..76cd47dbe0 --- /dev/null +++ b/fiat/mock_data/vs_currencies.json @@ -0,0 +1,10 @@ +[ + "btc", + "eth", + "ltc", + "usd", + "eur", + "aed", + "ars", + "aud" +] diff --git a/server/public.go b/server/public.go index 963056dd57..bb8315c450 100644 --- a/server/public.go +++ b/server/public.go @@ -193,7 +193,7 @@ func (s *PublicServer) ConnectFullPublicInterface() { serveMux.HandleFunc(path+"api/v2/balancehistory/", s.jsonHandler(s.apiBalanceHistory, apiDefault)) serveMux.HandleFunc(path+"api/v2/tickers/", s.jsonHandler(s.apiTickers, apiV2)) serveMux.HandleFunc(path+"api/v2/multi-tickers/", s.jsonHandler(s.apiMultiTickers, apiV2)) - serveMux.HandleFunc(path+"api/v2/tickers-list/", s.jsonHandler(s.apiTickersList, apiV2)) + serveMux.HandleFunc(path+"api/v2/tickers-list/", s.jsonHandler(s.apiAvailableVsCurrencies, apiV2)) // socket.io interface serveMux.Handle(path+"socket.io/", s.socketio.GetHandler()) // websocket interface @@ -1197,21 +1197,21 @@ 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) { +// apiAvailableVsCurrencies returns a list of available versus currencies +func (s *PublicServer) apiAvailableVsCurrencies(r *http.Request, apiVersion int) (interface{}, error) { s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-list"}).Inc() timestampString := strings.ToLower(r.URL.Query().Get("timestamp")) timestamp, err := strconv.ParseInt(timestampString, 10, 64) if err != nil { return nil, api.NewAPIError("Parameter \"timestamp\" is not a valid Unix timestamp.", true) } - result, err := s.api.GetFiatRatesTickersList(timestamp) + result, err := s.api.GetAvailableVsCurrencies(timestamp) return result, err } // apiTickers returns FiatRates ticker prices for the specified block or timestamp. func (s *PublicServer) apiTickers(r *http.Request, apiVersion int) (interface{}, error) { - var result *db.ResultTickerAsString + var result *api.FiatTicker var err error currency := strings.ToLower(r.URL.Query().Get("currency")) @@ -1251,7 +1251,7 @@ func (s *PublicServer) apiTickers(r *http.Request, apiVersion int) (interface{}, // apiMultiTickers returns FiatRates ticker prices for the specified comma separated list of timestamps. func (s *PublicServer) apiMultiTickers(r *http.Request, apiVersion int) (interface{}, error) { - var result []db.ResultTickerAsString + var result []api.FiatTicker var err error currency := strings.ToLower(r.URL.Query().Get("currency")) diff --git a/server/public_test.go b/server/public_test.go index 341a670de3..c643cacaaf 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -14,6 +14,7 @@ import ( "testing" "time" + "github.com/flier/gorocksdb" "github.com/golang/glog" "github.com/gorilla/websocket" "github.com/martinboehm/btcutil/chaincfg" @@ -161,51 +162,56 @@ func newPostRequest(u string, body string) *http.Request { return r } -func insertFiatRate(date string, rates map[string]float64, d *db.RocksDB) error { +func insertFiatRate(date string, rates map[string]float32, d *db.RocksDB) error { convertedDate, err := db.FiatRatesConvertDate(date) if err != nil { return err } ticker := &db.CurrencyRatesTicker{ - Timestamp: convertedDate, + Timestamp: *convertedDate, Rates: rates, } - return d.FiatRatesStoreTicker(ticker) + wb := gorocksdb.NewWriteBatch() + defer wb.Destroy() + if err := d.FiatRatesStoreTicker(wb, ticker); err != nil { + return err + } + return d.WriteBatch(wb) } // initTestFiatRates initializes test data for /api/v2/tickers endpoint func initTestFiatRates(d *db.RocksDB) error { - if err := insertFiatRate("20180320020000", map[string]float64{ + if err := insertFiatRate("20180320020000", map[string]float32{ "usd": 2000.0, "eur": 1300.0, }, d); err != nil { return err } - if err := insertFiatRate("20180320030000", map[string]float64{ + if err := insertFiatRate("20180320030000", map[string]float32{ "usd": 2001.0, "eur": 1301.0, }, d); err != nil { return err } - if err := insertFiatRate("20180320040000", map[string]float64{ + if err := insertFiatRate("20180320040000", map[string]float32{ "usd": 2002.0, "eur": 1302.0, }, d); err != nil { return err } - if err := insertFiatRate("20180321055521", map[string]float64{ + if err := insertFiatRate("20180321055521", map[string]float32{ "usd": 2003.0, "eur": 1303.0, }, d); err != nil { return err } - if err := insertFiatRate("20191121140000", map[string]float64{ + if err := insertFiatRate("20191121140000", map[string]float32{ "usd": 7814.5, "eur": 7100.0, }, d); err != nil { return err } - return insertFiatRate("20191121143015", map[string]float64{ + return insertFiatRate("20191121143015", map[string]float32{ "usd": 7914.5, "eur": 7134.1, }, d) diff --git a/server/websocket.go b/server/websocket.go index ab47f5659e..b84bdf1a8e 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -421,7 +421,7 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs }{} err = json.Unmarshal(req.Params, &r) if err == nil { - rv, err = s.getFiatRatesTickersList(r.Timestamp) + rv, err = s.getAvailableVsCurrencies(r.Timestamp) } return }, @@ -960,7 +960,7 @@ func (s *WebsocketServer) OnNewTx(tx *bchain.MempoolTx) { } } -func (s *WebsocketServer) broadcastTicker(currency string, rates map[string]float64) { +func (s *WebsocketServer) broadcastTicker(currency string, rates map[string]float32) { as, ok := s.fiatRatesSubscriptions[currency] if ok && len(as) > 0 { data := struct { @@ -983,7 +983,7 @@ func (s *WebsocketServer) OnNewFiatRatesTicker(ticker *db.CurrencyRatesTicker) { s.fiatRatesSubscriptionsLock.Lock() defer s.fiatRatesSubscriptionsLock.Unlock() for currency, rate := range ticker.Rates { - s.broadcastTicker(currency, map[string]float64{currency: rate}) + s.broadcastTicker(currency, map[string]float32{currency: rate}) } s.broadcastTicker(allFiatRates, ticker.Rates) } @@ -998,7 +998,7 @@ func (s *WebsocketServer) getFiatRatesForTimestamps(timestamps []int64, currenci return ret, err } -func (s *WebsocketServer) getFiatRatesTickersList(timestamp int64) (interface{}, error) { - ret, err := s.api.GetFiatRatesTickersList(timestamp) +func (s *WebsocketServer) getAvailableVsCurrencies(timestamp int64) (interface{}, error) { + ret, err := s.api.GetAvailableVsCurrencies(timestamp) return ret, err } From 96a09cf478c1eaa5870729c4a3f5987be59b5100 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 15 Jun 2022 13:39:40 +0200 Subject: [PATCH 060/530] Setup fiat rates downloader for Ethereum Archive --- bchain/coins/blockchain.go | 1 + configs/coins/ethereum_archive.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 33b996e9e3..6172bf5508 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -68,6 +68,7 @@ func init() { BlockChainFactories["Zcash"] = zec.NewZCashRPC BlockChainFactories["Zcash Testnet"] = zec.NewZCashRPC BlockChainFactories["Ethereum"] = eth.NewEthereumRPC + BlockChainFactories["Ethereum Archive"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Classic"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Ropsten"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Ropsten Archive"] = eth.NewEthereumRPC diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 313d800d17..d47eaaba0b 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -55,8 +55,8 @@ "processInternalTransactions": true, "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\", \"periodSeconds\": 60}", - "4byteSignatures": "https://www.4byte.directory/api/v1/signatures/" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } }, From e87ffec75cc09a9b155b0dd9fc525dbef1870120 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sat, 11 Jun 2022 22:08:45 +0200 Subject: [PATCH 061/530] Don't use break in switch where it's not needed --- bchain/mq.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/bchain/mq.go b/bchain/mq.go index 8f8e828263..68dcca2596 100644 --- a/bchain/mq.go +++ b/bchain/mq.go @@ -97,10 +97,8 @@ func (mq *MQ) run(callback func(NotificationType)) { switch string(msg[0]) { case "hashblock": nt = NotificationNewBlock - break case "hashtx": nt = NotificationNewTx - break default: nt = NotificationUnknown glog.Infof("MQ: NotificationUnknown %v", string(msg[0])) From 34499406cf36c5407e20418833d74e241d37f010 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sat, 11 Jun 2022 22:09:45 +0200 Subject: [PATCH 062/530] Simplify comparisons len(byte[][]) is defined as zero when the value is nil --- bchain/mq.go | 2 +- tests/dbtestdata/fakechain.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bchain/mq.go b/bchain/mq.go index 68dcca2596..5f91920914 100644 --- a/bchain/mq.go +++ b/bchain/mq.go @@ -92,7 +92,7 @@ func (mq *MQ) run(callback func(NotificationType)) { } else { repeatedError = false } - if msg != nil && len(msg) >= 3 { + if len(msg) >= 3 { var nt NotificationType switch string(msg[0]) { case "hashblock": diff --git a/tests/dbtestdata/fakechain.go b/tests/dbtestdata/fakechain.go index 8e37feb698..ffd02b1ca4 100644 --- a/tests/dbtestdata/fakechain.go +++ b/tests/dbtestdata/fakechain.go @@ -188,7 +188,7 @@ func (c *fakeBlockChain) GetTransactionForMempool(txid string) (v *bchain.Tx, er } func (c *fakeBlockChain) EstimateSmartFee(blocks int, conservative bool) (v big.Int, err error) { - if conservative == false { + if !conservative { v.SetInt64(int64(blocks)*100 - 1) } else { v.SetInt64(int64(blocks) * 100) From e126a7515ae7585cf6c0c2e62a211d96c20634ab Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 20 Jun 2022 18:47:54 +0200 Subject: [PATCH 063/530] =?UTF-8?q?eth=20archive=20(+testnet)=201.10.17=20?= =?UTF-8?q?=E2=86=92=201.10.19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum_archive.json | 6 +++--- configs/coins/ethereum_testnet_ropsten_archive.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index d47eaaba0b..b04ae1afe9 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -21,10 +21,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.17-25c9b49f", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz", + "version": "1.10.19-23bee162", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.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 --gcmode archive --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 8116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index e34437dc34..3e7a5fd048 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -20,10 +20,10 @@ "package_name": "backend-ethereum-testnet-ropsten-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.17-25c9b49f", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz", + "version": "1.10.19-23bee162", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From e927a5bfbbd9644a1575baa55e2002b25ed4c4c9 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 20 Jun 2022 20:11:47 +0200 Subject: [PATCH 064/530] Set up ethereum authrpc port --- configs/coins/ethereum.json | 3 ++- configs/coins/ethereum_archive.json | 3 ++- configs/coins/ethereum_testnet_ropsten.json | 4 +++- configs/coins/ethereum_testnet_ropsten_archive.json | 4 +++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 7c8c702103..fb5c6c28dc 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -10,6 +10,7 @@ "backend_message_queue": 0, "backend_p2p": 38336, "backend_http": 8136, + "backend_authrpc": 8536, "blockbook_internal": 9036, "blockbook_public": 9136 }, @@ -27,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.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 --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug\" --http --http.port 8136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ipcdisable --syncmode full --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug\" --http --http.port 8136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug\" --authrpc.port 8536 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index b04ae1afe9..6714e34844 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -10,6 +10,7 @@ "backend_message_queue": 0, "backend_p2p": 38316, "backend_http": 8116, + "backend_authrpc": 8516, "blockbook_internal": 9016, "blockbook_public": 9116 }, @@ -27,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.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 --gcmode archive --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 8116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ipcdisable --syncmode full --gcmode archive --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 8116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 8516 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 052e0f1c5e..577508e2ba 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -9,6 +9,8 @@ "backend_rpc": 18036, "backend_message_queue": 0, "backend_p2p": 48336, + "backend_http": 18136, + "backend_authrpc": 18536, "blockbook_internal": 19036, "blockbook_public": 19136 }, @@ -26,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18536 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index 3e7a5fd048..ffacc883cd 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -9,6 +9,8 @@ "backend_rpc": 18016, "backend_message_queue": 0, "backend_p2p": 48316, + "backend_http": 18116, + "backend_authrpc": 18516, "blockbook_internal": 19016, "blockbook_public": 19116 }, @@ -26,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18516 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", From 0554a762ee4518613ba2afee3363085237cd62e7 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 24 Jun 2022 17:36:13 +0200 Subject: [PATCH 065/530] Add Ropsten PoS consensus layer services --- configs/coins/ethereum_testnet_ropsten.json | 2 +- .../ethereum_testnet_ropsten_archive.json | 2 +- ...eum_testnet_ropsten_archive_consensus.json | 42 +++++++++++++++++++ .../ethereum_testnet_ropsten_consensus.json | 42 +++++++++++++++++++ 4 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 configs/coins/ethereum_testnet_ropsten_archive_consensus.json create mode 100644 configs/coins/ethereum_testnet_ropsten_consensus.json diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 577508e2ba..439ec7891e 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -28,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18536 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --override.terminaltotaldifficulty 50000000000000000 --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18536 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index ffacc883cd..200d8bda6f 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -28,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18516 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --override.terminaltotaldifficulty 50000000000000000 --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18516 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json new file mode 100644 index 0000000000..13001fa2f3 --- /dev/null +++ b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json @@ -0,0 +1,42 @@ +{ + "coin": { + "name": "Ethereum Testnet Ropsten Archive", + "shortcut": "tROP", + "label": "Ethereum Ropsten", + "alias": "ethereum_testnet_ropsten_archive_consensus", + "execution_alias": "ethereum_testnet_ropsten_archive" + }, + "ports": { + "backend_rpc": 18016, + "backend_message_queue": 0, + "backend_p2p": 48316, + "backend_http": 18116, + "backend_authrpc": 18516, + "blockbook_internal": 19016, + "blockbook_public": 19116 + }, + "backend": { + "package_name": "backend-ethereum-testnet-ropsten-archive-consensus", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "2.1.3-rc.4", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v2.1.3-rc.4/beacon-chain-v2.1.3-rc.4-linux-amd64", + "verification_type": "sha256", + "verification_source": "ea4cdec3854c6265e689648413d3c4357341643d0ca36fbc376c091030719a6e", + "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --http-web3provider=http://localhost:18516 --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "wget https://github.com/eth-clients/merge-testnets/raw/e4a6f0c181d24b28bc8651744f1d0e9ef74bda3f/ropsten-beacon-chain/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "" + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_ropsten_consensus.json b/configs/coins/ethereum_testnet_ropsten_consensus.json new file mode 100644 index 0000000000..dc3b8753b0 --- /dev/null +++ b/configs/coins/ethereum_testnet_ropsten_consensus.json @@ -0,0 +1,42 @@ +{ + "coin": { + "name": "Ethereum Testnet Ropsten", + "shortcut": "tROP", + "label": "Ethereum Ropsten", + "alias": "ethereum_testnet_ropsten_consensus", + "execution_alias": "ethereum_testnet_ropsten" + }, + "ports": { + "backend_rpc": 18036, + "backend_message_queue": 0, + "backend_p2p": 48336, + "backend_http": 18136, + "backend_authrpc": 18536, + "blockbook_internal": 19036, + "blockbook_public": 19136 + }, + "backend": { + "package_name": "backend-ethereum-testnet-ropsten-consensus", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "2.1.3-rc.4", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v2.1.3-rc.4/beacon-chain-v2.1.3-rc.4-linux-amd64", + "verification_type": "sha256", + "verification_source": "ea4cdec3854c6265e689648413d3c4357341643d0ca36fbc376c091030719a6e", + "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --http-web3provider=http://localhost:18536 --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13536 --p2p-udp-port=12536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "wget https://github.com/eth-clients/merge-testnets/raw/e4a6f0c181d24b28bc8651744f1d0e9ef74bda3f/ropsten-beacon-chain/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "" + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} From f5b179d5c258fc1525a708a5cd1557577c5357b9 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 27 Jun 2022 00:29:14 +0200 Subject: [PATCH 066/530] Fix ERC1155 transfer event processing --- bchain/coins/eth/contract.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go index 76a84fb1ed..a3db57efd4 100644 --- a/bchain/coins/eth/contract.go +++ b/bchain/coins/eth/contract.go @@ -78,6 +78,10 @@ func processTransferEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { } func processERC1155TransferSingleEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { + tl := len(l.Topics) + if tl != 4 { + return nil, nil + } from, err := addressFromPaddedHex(l.Topics[2]) if err != nil { return nil, err @@ -109,6 +113,10 @@ func processERC1155TransferSingleEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, } func processERC1155TransferBatchEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { + tl := len(l.Topics) + if tl < 4 { + return nil, nil + } from, err := addressFromPaddedHex(l.Topics[2]) if err != nil { return nil, err @@ -178,7 +186,7 @@ func contractGetTransfersFromLog(logs []*bchain.RpcLog) (bchain.TokenTransfers, signature := l.Topics[0] if signature == tokenTransferEventSignature { tt, err = processTransferEvent(l) - } else if signature == tokenERC1155TransferSingleEventSignature && tl == 4 { + } else if signature == tokenERC1155TransferSingleEventSignature { tt, err = processERC1155TransferSingleEvent(l) } else if signature == tokenERC1155TransferBatchEventSignature { tt, err = processERC1155TransferBatchEvent(l) From 3f5980abdb33275ee418469c78f963fa82ea8b00 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 9 Aug 2022 00:08:31 +0200 Subject: [PATCH 067/530] Return token exchange rates via API --- api/worker.go | 75 ++++++++++++++++-------- blockbook.go | 1 + db/fiat.go | 91 +++++++++++++++++++++++------ db/fiat_test.go | 79 +++++++++++++++++++++++++ fiat/coingecko.go | 43 +++++++++----- fiat/fiat_rates.go | 37 +++++++----- server/public.go | 13 +++-- server/public_ethereumtype_test.go | 94 ++++++++++++++++++++++++++++++ server/public_test.go | 31 +++++----- server/websocket.go | 85 ++++++++++++++++++++------- static/test-websocket.html | 66 +++++++++++++++------ 11 files changed, 485 insertions(+), 130 deletions(-) diff --git a/api/worker.go b/api/worker.go index cc9d1af606..cad14df1d4 100644 --- a/api/worker.go +++ b/api/worker.go @@ -1342,7 +1342,6 @@ func (w *Worker) setFiatRateToBalanceHistories(histories BalanceHistories, curre for i := range histories { bh := &histories[i] t := time.Unix(int64(bh.Time), 0) - // TODO ticker, err := w.db.FiatRatesFindTicker(&t, "", "") if err != nil { glog.Errorf("Error finding ticker by date %v. Error: %v", t, err) @@ -1594,8 +1593,20 @@ func removeEmpty(stringSlice []string) []string { } // getFiatRatesResult checks if CurrencyRatesTicker contains all necessary data and returns formatted result -func (w *Worker) getFiatRatesResult(currencies []string, ticker *db.CurrencyRatesTicker) (*FiatTicker, error) { - currencies = removeEmpty(currencies) +func (w *Worker) getFiatRatesResult(currencies []string, ticker *db.CurrencyRatesTicker, token string) (*FiatTicker, error) { + if token != "" { + if len(currencies) != 1 { + return nil, NewAPIError("Rates for token only for a single currency", true) + } + rate := ticker.TokenRateInCurrency(token, currencies[0]) + if rate <= 0 { + rate = -1 + } + return &FiatTicker{ + Timestamp: ticker.Timestamp.UTC().Unix(), + Rates: map[string]float32{currencies[0]: rate}, + }, nil + } if len(currencies) == 0 { // Return all available ticker rates return &FiatTicker{ @@ -1620,7 +1631,7 @@ func (w *Worker) getFiatRatesResult(currencies []string, ticker *db.CurrencyRate } // GetFiatRatesForBlockID returns fiat rates for block height or block hash -func (w *Worker) GetFiatRatesForBlockID(blockID string, currencies []string) (*FiatTicker, error) { +func (w *Worker) GetFiatRatesForBlockID(blockID string, currencies []string, token string) (*FiatTicker, error) { var ticker *db.CurrencyRatesTicker bi, err := w.getBlockInfoFromBlockID(blockID) if err != nil { @@ -1631,14 +1642,18 @@ func (w *Worker) GetFiatRatesForBlockID(blockID string, currencies []string) (*F } dbi := &db.BlockInfo{Time: bi.Time} // get Unix timestamp from block tm := time.Unix(dbi.Time, 0) // convert it to Time object - // TODO - ticker, err = w.db.FiatRatesFindTicker(&tm, "", "") + vsCurrency := "" + currencies = removeEmpty(currencies) + if len(currencies) == 1 { + vsCurrency = currencies[0] + } + ticker, err = w.db.FiatRatesFindTicker(&tm, vsCurrency, token) 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", tm), true) } - result, err := w.getFiatRatesResult(currencies, ticker) + result, err := w.getFiatRatesResult(currencies, ticker, token) if err != nil { return nil, err } @@ -1646,22 +1661,29 @@ func (w *Worker) GetFiatRatesForBlockID(blockID string, currencies []string) (*F } // GetCurrentFiatRates returns last available fiat rates -func (w *Worker) GetCurrentFiatRates(currencies []string) (*FiatTicker, error) { - // TODO - 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) +func (w *Worker) GetCurrentFiatRates(currencies []string, token string) (*FiatTicker, error) { + vsCurrency := "" + currencies = removeEmpty(currencies) + if len(currencies) == 1 { + vsCurrency = currencies[0] + } + ticker, err := w.db.FiatRatesGetCurrentTicker(vsCurrency, token) + if ticker == nil || err != nil { + ticker, err = w.db.FiatRatesFindLastTicker(vsCurrency, token) + 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(currencies, ticker) + result, err := w.getFiatRatesResult(currencies, ticker, token) if err != nil { return nil, err } return result, nil } -// makeErrorRates returns a map of currrencies, with each value equal to -1 +// makeErrorRates returns a map of currencies, with each value equal to -1 // used when there was an error finding ticker func makeErrorRates(currencies []string) map[string]float32 { rates := make(map[string]float32) @@ -1672,18 +1694,21 @@ func makeErrorRates(currencies []string) map[string]float32 { } // GetFiatRatesForTimestamps returns fiat rates for each of the provided dates -func (w *Worker) GetFiatRatesForTimestamps(timestamps []int64, currencies []string) (*FiatTickers, error) { +func (w *Worker) GetFiatRatesForTimestamps(timestamps []int64, currencies []string, token string) (*FiatTickers, error) { if len(timestamps) == 0 { return nil, NewAPIError("No timestamps provided", true) } + vsCurrency := "" currencies = removeEmpty(currencies) + if len(currencies) == 1 { + vsCurrency = currencies[0] + } ret := &FiatTickers{} for _, timestamp := range timestamps { date := time.Unix(timestamp, 0) date = date.UTC() - // TODO - ticker, err := w.db.FiatRatesFindTicker(&date, "", "") + ticker, err := w.db.FiatRatesFindTicker(&date, vsCurrency, token) if err != nil { glog.Errorf("Error finding ticker for date %v. Error: %v", date, err) ret.Tickers = append(ret.Tickers, FiatTicker{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)}) @@ -1692,8 +1717,13 @@ func (w *Worker) GetFiatRatesForTimestamps(timestamps []int64, currencies []stri ret.Tickers = append(ret.Tickers, FiatTicker{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)}) continue } - result, err := w.getFiatRatesResult(currencies, ticker) + result, err := w.getFiatRatesResult(currencies, ticker, token) if err != nil { + if apiErr, ok := err.(*APIError); ok { + if apiErr.Public { + return nil, err + } + } ret.Tickers = append(ret.Tickers, FiatTicker{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)}) continue } @@ -1703,12 +1733,11 @@ func (w *Worker) GetFiatRatesForTimestamps(timestamps []int64, currencies []stri } // GetAvailableVsCurrencies returns the list of available versus currencies for exchange rates -func (w *Worker) GetAvailableVsCurrencies(timestamp int64) (*AvailableVsCurrencies, error) { +func (w *Worker) GetAvailableVsCurrencies(timestamp int64, token string) (*AvailableVsCurrencies, error) { date := time.Unix(timestamp, 0) date = date.UTC() - // TODO - ticker, err := w.db.FiatRatesFindTicker(&date, "", "") + ticker, err := w.db.FiatRatesFindTicker(&date, "", strings.ToLower(token)) if err != nil { return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false) } else if ticker == nil { diff --git a/blockbook.go b/blockbook.go index 304c6f662e..37db252744 100644 --- a/blockbook.go +++ b/blockbook.go @@ -525,6 +525,7 @@ func onNewFiatRatesTicker(ticker *db.CurrencyRatesTicker) { defer func() { if r := recover(); r != nil { glog.Error("onNewFiatRatesTicker recovered from panic: ", r) + debug.PrintStack() } }() for _, c := range callbacksOnNewFiatRatesTicker { diff --git a/db/fiat.go b/db/fiat.go index e4b57f794e..9347aba00b 100644 --- a/db/fiat.go +++ b/db/fiat.go @@ -26,6 +26,49 @@ type CurrencyRatesTicker struct { TokenRates map[string]float32 // rates of the tokens (identified by the address of the contract) against the base currency } +// Convert converts value in base currency to toCurrency +func (t *CurrencyRatesTicker) Convert(baseValue float64, toCurrency string) float64 { + rate, found := t.Rates[toCurrency] + if !found { + return 0 + } + return baseValue * float64(rate) +} + +// ConvertTokenToBase converts token value to base currency +func (t *CurrencyRatesTicker) ConvertTokenToBase(value float64, token string) float64 { + if t.TokenRates != nil { + rate, found := t.TokenRates[token] + if found { + return value * float64(rate) + } + } + return 0 +} + +// ConvertTokenToBase converts token value to toCurrency currency +func (t *CurrencyRatesTicker) ConvertToken(value float64, token string, toCurrency string) float64 { + baseValue := t.ConvertTokenToBase(value, token) + if baseValue > 0 { + return t.Convert(baseValue, toCurrency) + } + return 0 +} + +// TokenRateInCurrency return token rate in toCurrency currency +func (t *CurrencyRatesTicker) TokenRateInCurrency(token string, toCurrency string) float32 { + if t.TokenRates != nil { + rate, found := t.TokenRates[token] + if found { + baseRate, found := t.Rates[toCurrency] + if found { + return baseRate * rate + } + } + } + return 0 +} + func packTimestamp(t *time.Time) []byte { return []byte(t.UTC().Format(FiatRatesTimeFormat)) } @@ -117,31 +160,38 @@ func (d *RocksDB) FiatRatesStoreTicker(wb *gorocksdb.WriteBatch, ticker *Currenc return nil } -func getTickerFromIterator(it *gorocksdb.Iterator, vsCurrency string, token string) (*CurrencyRatesTicker, error) { - timeObj, err := time.Parse(FiatRatesTimeFormat, string(it.Key().Data())) - if err != nil { - return nil, err - } - ticker, err := unpackCurrencyRatesTicker(it.Value().Data()) - if err != nil { - return nil, err - } +func isSuitableTicker(ticker *CurrencyRatesTicker, vsCurrency string, token string) bool { if vsCurrency != "" { if ticker.Rates == nil { - return nil, nil + return false } if _, found := ticker.Rates[vsCurrency]; !found { - return nil, nil + return false } } if token != "" { if ticker.TokenRates == nil { - return nil, nil + return false } if _, found := ticker.TokenRates[token]; !found { - return nil, nil + return false } } + return true +} + +func getTickerFromIterator(it *gorocksdb.Iterator, vsCurrency string, token string) (*CurrencyRatesTicker, error) { + timeObj, err := time.Parse(FiatRatesTimeFormat, string(it.Key().Data())) + if err != nil { + return nil, err + } + ticker, err := unpackCurrencyRatesTicker(it.Value().Data()) + if err != nil { + return nil, err + } + if !isSuitableTicker(ticker, vsCurrency, token) { + return nil, nil + } ticker.Timestamp = timeObj.UTC() return ticker, nil } @@ -169,8 +219,8 @@ func (d *RocksDB) FiatRatesGetTicker(tickerTime *time.Time) (*CurrencyRatesTicke // FiatRatesFindTicker gets FiatRates data closest to the specified timestamp, of the base currency, vsCurrency or the token if specified func (d *RocksDB) FiatRatesFindTicker(tickerTime *time.Time, vsCurrency string, token string) (*CurrencyRatesTicker, error) { tickersMux.Lock() - if currentTicker != nil && lastTickerInDB != nil { - if tickerTime.After(lastTickerInDB.Timestamp) { + if currentTicker != nil { + if !tickerTime.Before(currentTicker.Timestamp) || (lastTickerInDB != nil && tickerTime.After(lastTickerInDB.Timestamp)) { f := true if token != "" && currentTicker.TokenRates != nil { _, f = currentTicker.TokenRates[token] @@ -224,14 +274,17 @@ func (d *RocksDB) FiatRatesFindLastTicker(vsCurrency string, token string) (*Cur return nil, nil } -// FiatRatesGetCurrentTicker return current ticker -func (d *RocksDB) FiatRatesGetCurrentTicker(tickerTime *time.Time, token string) (*CurrencyRatesTicker, error) { +// FiatRatesGetCurrentTicker returns current ticker +func (d *RocksDB) FiatRatesGetCurrentTicker(vsCurrency string, token string) (*CurrencyRatesTicker, error) { tickersMux.Lock() defer tickersMux.Unlock() - return currentTicker, nil + if currentTicker != nil && isSuitableTicker(currentTicker, vsCurrency, token) { + return currentTicker, nil + } + return nil, nil } -// FiatRatesCurrentTicker return current ticker +// FiatRatesCurrentTicker sets current ticker func (d *RocksDB) FiatRatesSetCurrentTicker(t *CurrencyRatesTicker) { tickersMux.Lock() defer tickersMux.Unlock() diff --git a/db/fiat_test.go b/db/fiat_test.go index b2c2e7cff2..1c90c60696 100644 --- a/db/fiat_test.go +++ b/db/fiat_test.go @@ -157,6 +157,24 @@ func TestRocksTickers(t *testing.T) { t.Errorf("Ticker %v found unexpectedly for aud vsCurrency", ticker) } + ticker, err = d.FiatRatesGetCurrentTicker("", "") + if err != nil { + t.Errorf("TestRocksTickers err: %+v", err) + } else if ticker != nil { + t.Errorf("FiatRatesGetCurrentTicker %v found unexpectedly", ticker) + } + + d.FiatRatesSetCurrentTicker(ticker1) + ticker, err = d.FiatRatesGetCurrentTicker("", "") + 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) + } + + d.FiatRatesSetCurrentTicker(nil) } func Test_packUnpackCurrencyRatesTicker(t *testing.T) { @@ -208,3 +226,64 @@ func Test_packUnpackCurrencyRatesTicker(t *testing.T) { }) } } + +func TestCurrencyRatesTicker_ConvertToken(t *testing.T) { + ticker := &CurrencyRatesTicker{ + Rates: map[string]float32{ + "usd": 2129.987654321, + "eur": 1332.12345678, + }, + TokenRates: map[string]float32{ + "0x82df128257a7d7556262e1ab7f1f639d9775b85e": 0.4092341123, + "0x6b175474e89094c44da98b954eedeac495271d0f": 12.32323232323232, + "0xdac17f958d2ee523a2206206994597c13d831ec7": 1332421341235.51234, + }, + } + type args struct { + baseValue float64 + toCurrency string + } + tests := []struct { + name string + value float64 + token string + toCurrency string + want float64 + }{ + { + name: "usd 0x82df128257a7d7556262e1ab7f1f639d9775b85e", + value: 10, + token: "0x82df128257a7d7556262e1ab7f1f639d9775b85e", + toCurrency: "usd", + want: 8716.635514874506, + }, + { + name: "eur 0xdac17f958d2ee523a2206206994597c13d831ec7", + value: 23.123, + token: "0xdac17f958d2ee523a2206206994597c13d831ec7", + toCurrency: "eur", + want: 4.104216071804417e+16, + }, + { + name: "eur 0xdac17f958d2ee523a2206206994597c13d831ec8", + value: 23.123, + token: "0xdac17f958d2ee523a2206206994597c13d831ec8", + toCurrency: "eur", + want: 0, + }, + { + name: "eur 0xdac17f958d2ee523a2206206994597c13d831ec7", + value: 23.123, + token: "0xdac17f958d2ee523a2206206994597c13d831ec7", + toCurrency: "czk", + want: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ticker.ConvertToken(tt.value, tt.token, tt.toCurrency); got != tt.want { + t.Errorf("CurrencyRatesTicker.ConvertToken() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/fiat/coingecko.go b/fiat/coingecko.go index d800d654bf..ece706d8e5 100644 --- a/fiat/coingecko.go +++ b/fiat/coingecko.go @@ -26,6 +26,7 @@ type Coingecko struct { timeFormat string httpClient *http.Client db *db.RocksDB + updatingCurrent bool updatingTokens bool } @@ -42,13 +43,17 @@ type coinsListItem struct { // coinList https://api.coingecko.com/api/v3/coins/list type coinList []coinsListItem -type marketPoint [2]float32 +type marketPoint [2]float64 type marketChartPrices struct { Prices []marketPoint `json:"prices"` } // NewCoinGeckoDownloader creates a coingecko structure that implements the RatesDownloaderInterface -func NewCoinGeckoDownloader(db *db.RocksDB, url string, coin string, platformIdentifier string, platformVsCurrency string, timeFormat string, throttlingDelayMs int) RatesDownloaderInterface { +func NewCoinGeckoDownloader(db *db.RocksDB, url string, coin string, platformIdentifier string, platformVsCurrency string, timeFormat string, throttleDown bool) RatesDownloaderInterface { + var throttlingDelayMs int + if throttleDown { + throttlingDelayMs = 100 + } httpTimeoutSeconds := 15 * time.Second return &Coingecko{ url: url, @@ -95,13 +100,13 @@ func (cg *Coingecko) makeReq(url string) ([]byte, error) { if err == nil { return resp, err } - if err.Error() != "error code: 1015" { + if err.Error() != "error code: 1015" && !strings.Contains(strings.ToLower(err.Error()), "exceeded the rate limit") { glog.Errorf("Coingecko makeReq %v error %v", url, err) return nil, err } - // if there is a throttling error, wait 70 seconds and retry - glog.Errorf("Coingecko makeReq %v error %v, will retry in 70 seconds", url, err) - time.Sleep(70 * time.Second) + // if there is a throttling error, wait 60 seconds and retry + glog.Errorf("Coingecko makeReq %v error %v, will retry in 60 seconds", url, err) + time.Sleep(60 * time.Second) } } @@ -219,6 +224,9 @@ func (cg *Coingecko) platformIds() error { } func (cg *Coingecko) CurrentTickers() (*db.CurrencyRatesTicker, error) { + cg.updatingCurrent = true + defer func() { cg.updatingCurrent = false }() + var newTickers = db.CurrencyRatesTicker{} if vsCurrencies == nil { @@ -290,13 +298,12 @@ func (cg *Coingecko) getHistoricalTicker(tickersToUpdate map[uint]*db.CurrencyRa warningLogged := false for _, p := range mc.Prices { var timestamp uint - if p[0] > 100000000000 { + timestamp = uint(p[0]) + if timestamp > 100000000000 { // convert timestamp from milliseconds to seconds - timestamp = uint(p[0] / 1000) - } else { - timestamp = uint(p[0]) + timestamp /= 1000 } - rate := p[1] + rate := float32(p[1]) if timestamp%(24*3600) == 0 && timestamp != 0 && rate != 0 { // process only tickers for the whole day with non 0 value var found bool var ticker *db.CurrencyRatesTicker @@ -350,6 +357,15 @@ func (cg *Coingecko) storeTickers(tickersToUpdate map[uint]*db.CurrencyRatesTick return nil } +func (cg *Coingecko) throttleHistoricalDownload() { + // long delay next request to avoid throttling if downloading current tickers at the same time + delay := 1 + if cg.updatingCurrent { + delay = 600 + } + time.Sleep(cg.throttlingDelay * time.Duration(delay)) +} + // UpdateHistoricalTickers gets historical tickers for the main crypto currency func (cg *Coingecko) UpdateHistoricalTickers() error { tickersToUpdate := make(map[uint]*db.CurrencyRatesTicker) @@ -371,7 +387,7 @@ func (cg *Coingecko) UpdateHistoricalTickers() error { glog.Errorf("getHistoricalTicker %s-%s %v", cg.coin, currency, err) } if req { - time.Sleep(cg.throttlingDelay) + cg.throttleHistoricalDownload() } } @@ -413,8 +429,7 @@ func (cg *Coingecko) UpdateHistoricalTokenTickers() error { glog.Infof("Coingecko updated %d of %d token tickers", count, len(platformIds)) } if req { - // long delay next request to avoid throttling - time.Sleep(cg.throttlingDelay * 20) + cg.throttleHistoricalDownload() } } } diff --git a/fiat/fiat_rates.go b/fiat/fiat_rates.go index 8ac8fe0763..37e0d8350d 100644 --- a/fiat/fiat_rates.go +++ b/fiat/fiat_rates.go @@ -56,12 +56,12 @@ func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, callb rd.db = db rd.callbackOnNewTicker = callback if apiType == "coingecko" { - throttlingDelayMs := 50 + throttle := true if callback == nil { // a small hack - in tests the callback is not used, therefore there is no delay slowing the test - throttlingDelayMs = 0 + throttle = false } - rd.downloader = NewCoinGeckoDownloader(db, rdParams.URL, rdParams.Coin, rdParams.PlatformIdentifier, rdParams.PlatformVsCurrency, rd.timeFormat, throttlingDelayMs) + rd.downloader = NewCoinGeckoDownloader(db, rdParams.URL, rdParams.Coin, rdParams.PlatformIdentifier, rdParams.PlatformVsCurrency, rd.timeFormat, throttle) } else { return nil, fmt.Errorf("NewFiatRatesDownloader: incorrect API type %q", apiType) } @@ -74,11 +74,14 @@ func (rd *RatesDownloader) Run() error { for { tickers, err := rd.downloader.CurrentTickers() - if err != nil && tickers != nil { + if err != nil || tickers == nil { glog.Error("FiatRatesDownloader: CurrentTickers error ", err) } else { rd.db.FiatRatesSetCurrentTicker(tickers) glog.Info("FiatRatesDownloader: CurrentTickers updated") + if rd.callbackOnNewTicker != nil { + rd.callbackOnNewTicker(tickers) + } } if time.Now().UTC().YearDay() != lastHistoricalTickers.YearDay() || time.Now().UTC().Year() != lastHistoricalTickers.Year() { err = rd.downloader.UpdateHistoricalTickers() @@ -86,20 +89,24 @@ func (rd *RatesDownloader) Run() error { glog.Error("FiatRatesDownloader: UpdateHistoricalTickers error ", err) } else { lastHistoricalTickers = time.Now().UTC() - glog.Info("FiatRatesDownloader: UpdateHistoricalTickers finished") - } - // UpdateHistoricalTokenTickers in a goroutine, it can take quite some time as there may be many tokens - go func() { - err := rd.downloader.UpdateHistoricalTokenTickers() - if err != nil { - glog.Error("FiatRatesDownloader: UpdateHistoricalTokenTickers error ", err) + ticker, err := rd.db.FiatRatesFindLastTicker("", "") + if err != nil || ticker == nil { + glog.Error("FiatRatesDownloader: FiatRatesFindLastTicker error ", err) } else { - lastHistoricalTickers = time.Now().UTC() - glog.Info("FiatRatesDownloader: UpdateHistoricalTokenTickers finished") + glog.Infof("FiatRatesDownloader: UpdateHistoricalTickers finished, last ticker from %v", ticker.Timestamp) } - }() + // UpdateHistoricalTokenTickers in a goroutine, it can take quite some time as there may be many tokens + go func() { + err := rd.downloader.UpdateHistoricalTokenTickers() + if err != nil { + glog.Error("FiatRatesDownloader: UpdateHistoricalTokenTickers error ", err) + } else { + glog.Info("FiatRatesDownloader: UpdateHistoricalTokenTickers finished") + } + }() + } } - // next run on the + // wait for the next run with a slight random value to avoid too many request at the same time now := time.Now().Unix() next := now + rd.periodSeconds next -= next % rd.periodSeconds diff --git a/server/public.go b/server/public.go index bb8315c450..b92e098353 100644 --- a/server/public.go +++ b/server/public.go @@ -1205,7 +1205,8 @@ func (s *PublicServer) apiAvailableVsCurrencies(r *http.Request, apiVersion int) if err != nil { return nil, api.NewAPIError("Parameter \"timestamp\" is not a valid Unix timestamp.", true) } - result, err := s.api.GetAvailableVsCurrencies(timestamp) + token := strings.ToLower(r.URL.Query().Get("token")) + result, err := s.api.GetAvailableVsCurrencies(timestamp, token) return result, err } @@ -1219,11 +1220,12 @@ func (s *PublicServer) apiTickers(r *http.Request, apiVersion int) (interface{}, if currency != "" { currencies = []string{currency} } + token := strings.ToLower(r.URL.Query().Get("token")) 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, currencies) + result, err = s.api.GetFiatRatesForBlockID(block, currencies, token) } else if timestampString := r.URL.Query().Get("timestamp"); timestampString != "" { // Get tickers for specified timestamp s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-date"}).Inc() @@ -1233,7 +1235,7 @@ func (s *PublicServer) apiTickers(r *http.Request, apiVersion int) (interface{}, return nil, api.NewAPIError("Parameter 'timestamp' is not a valid Unix timestamp.", true) } - resultTickers, err := s.api.GetFiatRatesForTimestamps([]int64{timestamp}, currencies) + resultTickers, err := s.api.GetFiatRatesForTimestamps([]int64{timestamp}, currencies, token) if err != nil { return nil, err } @@ -1241,7 +1243,7 @@ func (s *PublicServer) apiTickers(r *http.Request, apiVersion int) (interface{}, } else { // No parameters - get the latest available ticker s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-last"}).Inc() - result, err = s.api.GetCurrentFiatRates(currencies) + result, err = s.api.GetCurrentFiatRates(currencies, token) } if err != nil { return nil, err @@ -1259,6 +1261,7 @@ func (s *PublicServer) apiMultiTickers(r *http.Request, apiVersion int) (interfa if currency != "" { currencies = []string{currency} } + token := strings.ToLower(r.URL.Query().Get("token")) if timestampString := r.URL.Query().Get("timestamp"); timestampString != "" { // Get tickers for specified timestamp s.metrics.ExplorerViews.With(common.Labels{"action": "api-multi-tickers-date"}).Inc() @@ -1270,7 +1273,7 @@ func (s *PublicServer) apiMultiTickers(r *http.Request, apiVersion int) (interfa return nil, api.NewAPIError("Parameter 'timestamp' does not contain a valid Unix timestamp.", true) } } - resultTickers, err := s.api.GetFiatRatesForTimestamps(t, currencies) + resultTickers, err := s.api.GetFiatRatesForTimestamps(t, currencies, token) if err != nil { return nil, err } diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 93367959e0..9f1ac6f01a 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -85,6 +85,42 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}},"addressAliases":{"0x20cD153de35D469BA46127A0C8F18626b59a256A":{"Type":"ENS","Alias":"address20.eth"},"0x4af4114F73d1c1C903aC9E0361b379D1291808A2":{"Type":"Contract","Alias":"Contract 74"}}}`, }, }, + { + name: "apiFiatRates get rate by timestamp", + r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd×tamp=1574340000"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"ts":1574344800,"rates":{"usd":7814.5}}`, + }, + }, + { + name: "apiFiatRates get token rate by timestamp", + r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd×tamp=1574340000&token=0xA4DD6Bc15Be95Af55f0447555c8b6aA3088562f3"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"ts":1574344800,"rates":{"usd":6251.6}}`, + }, + }, + { + name: "apiFiatRates get token rate by timestamp for all currencies", + r: newGetRequest(ts.URL + "/api/v2/tickers?timestamp=1574340000&token=0xA4DD6Bc15Be95Af55f0447555c8b6aA3088562f3"), + status: http.StatusBadRequest, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"error":"Rates for token only for a single currency"}`, + }, + }, + { + name: "apiFiatRates get token rate for unknown token by timestamp", + r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd×tamp=1574340000&token=0xFFFFFFFFFFe95Af55f0447555c8b6aA3088562f3"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"ts":1574340000,"rates":{"usd":-1}}`, + }, + }, } performHttpTests(tests, t, ts) @@ -103,6 +139,64 @@ func initEthereumTypeDB(d *db.RocksDB) error { return d.WriteBatch(wb) } +// initTestFiatRatesEthereumType initializes test data for /api/v2/tickers endpoint +func initTestFiatRatesEthereumType(d *db.RocksDB) error { + if err := insertFiatRate("20180320020000", map[string]float32{ + "usd": 2000.0, + "eur": 1300.0, + }, map[string]float32{ + "0xdac17f958d2ee523a2206206994597c13d831ec7": 2000.1, + "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": 123.0, + }, d); err != nil { + return err + } + if err := insertFiatRate("20180320030000", map[string]float32{ + "usd": 2001.0, + "eur": 1301.0, + }, map[string]float32{ + "0xdac17f958d2ee523a2206206994597c13d831ec7": 2001.1, + "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": 199.0, + }, d); err != nil { + return err + } + if err := insertFiatRate("20180320040000", map[string]float32{ + "usd": 2002.0, + "eur": 1302.0, + }, map[string]float32{ + "0xdac17f958d2ee523a2206206994597c13d831ec7": 2002.1, + "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": 99.0, + }, d); err != nil { + return err + } + if err := insertFiatRate("20180321055521", map[string]float32{ + "usd": 2003.0, + "eur": 1303.0, + }, map[string]float32{ + "0xdac17f958d2ee523a2206206994597c13d831ec7": 2003.1, + "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": 101.0, + }, d); err != nil { + return err + } + if err := insertFiatRate("20191121140000", map[string]float32{ + "usd": 7814.5, + "eur": 7100.0, + }, map[string]float32{ + "0xdac17f958d2ee523a2206206994597c13d831ec7": 7814.1, + "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": 499.0, + "0xa4dd6bc15be95af55f0447555c8b6aa3088562f3": 0.8, + }, d); err != nil { + return err + } + return insertFiatRate("20191121143015", map[string]float32{ + "usd": 7914.5, + "eur": 7134.1, + }, map[string]float32{ + "0xdac17f958d2ee523a2206206994597c13d831ec7": 7914.1, + "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": 599.0, + "0xa4dd6bc15be95af55f0447555c8b6aa3088562f3": 1.2, + }, d) +} + func Test_PublicServer_EthereumType(t *testing.T) { parser := eth.NewEthereumParser(1, true) chain, err := dbtestdata.NewFakeBlockChainEthereumType(parser) diff --git a/server/public_test.go b/server/public_test.go index c643cacaaf..82f66a0c3d 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -75,14 +75,18 @@ func setupRocksDB(parser bchain.BlockChainParser, chain bchain.BlockChain, t *te if err := d.ConnectBlock(block2); err != nil { t.Fatal(err) } - if err := initTestFiatRates(d); err != nil { - t.Fatal(err) - } is.FinishedSync(block2.Height) if parser.GetChainType() == bchain.ChainEthereumType { + if err := initTestFiatRatesEthereumType(d); err != nil { + t.Fatal(err) + } if err := initEthereumTypeDB(d); err != nil { t.Fatal(err) } + } else { + if err := initTestFiatRates(d); err != nil { + t.Fatal(err) + } } return d, is, tmp } @@ -162,14 +166,15 @@ func newPostRequest(u string, body string) *http.Request { return r } -func insertFiatRate(date string, rates map[string]float32, d *db.RocksDB) error { +func insertFiatRate(date string, rates map[string]float32, tokenRates map[string]float32, d *db.RocksDB) error { convertedDate, err := db.FiatRatesConvertDate(date) if err != nil { return err } ticker := &db.CurrencyRatesTicker{ - Timestamp: *convertedDate, - Rates: rates, + Timestamp: *convertedDate, + Rates: rates, + TokenRates: tokenRates, } wb := gorocksdb.NewWriteBatch() defer wb.Destroy() @@ -184,37 +189,37 @@ func initTestFiatRates(d *db.RocksDB) error { if err := insertFiatRate("20180320020000", map[string]float32{ "usd": 2000.0, "eur": 1300.0, - }, d); err != nil { + }, nil, d); err != nil { return err } if err := insertFiatRate("20180320030000", map[string]float32{ "usd": 2001.0, "eur": 1301.0, - }, d); err != nil { + }, nil, d); err != nil { return err } if err := insertFiatRate("20180320040000", map[string]float32{ "usd": 2002.0, "eur": 1302.0, - }, d); err != nil { + }, nil, d); err != nil { return err } if err := insertFiatRate("20180321055521", map[string]float32{ "usd": 2003.0, "eur": 1303.0, - }, d); err != nil { + }, nil, d); err != nil { return err } if err := insertFiatRate("20191121140000", map[string]float32{ "usd": 7814.5, "eur": 7100.0, - }, d); err != nil { + }, nil, d); err != nil { return err } return insertFiatRate("20191121143015", map[string]float32{ "usd": 7914.5, "eur": 7134.1, - }, d) + }, nil, d) } type httpTests struct { @@ -1332,7 +1337,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { "currencies": []string{"does-not-exist"}, }, }, - want: `{"id":"21","data":{"ts":1574346615,"rates":{"does-not-exist":-1}}}`, + want: `{"id":"21","data":{"error":{"message":"No tickers found!"}}}`, }, { name: "websocket getFiatRatesForTimestamps missing date", diff --git a/server/websocket.go b/server/websocket.go index b84bdf1a8e..17ea76a3d9 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -56,6 +56,11 @@ type websocketChannel struct { addrDescs []string // subscribed address descriptors as strings } +type fiatRatesSubscription struct { + Currency string `json:"currency"` + Tokens []string `json:"tokens"` +} + // WebsocketServer is a handle to websocket server type WebsocketServer struct { socket *websocket.Conn @@ -77,6 +82,7 @@ type WebsocketServer struct { addressSubscriptions map[string]map[*websocketChannel]string addressSubscriptionsLock sync.Mutex fiatRatesSubscriptions map[string]map[*websocketChannel]string + fiatRatesTokenSubscriptions map[*websocketChannel][]string fiatRatesSubscriptionsLock sync.Mutex } @@ -110,6 +116,7 @@ func NewWebsocketServer(db *db.RocksDB, chain bchain.BlockChain, mempool bchain. newTransactionSubscriptions: make(map[*websocketChannel]string), addressSubscriptions: make(map[string]map[*websocketChannel]string), fiatRatesSubscriptions: make(map[string]map[*websocketChannel]string), + fiatRatesTokenSubscriptions: make(map[*websocketChannel][]string), } return s, nil } @@ -378,14 +385,16 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs return s.unsubscribeAddresses(c) }, "subscribeFiatRates": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { - r := struct { - Currency string `json:"currency"` - }{} + var r fiatRatesSubscription err = json.Unmarshal(req.Params, &r) if err != nil { return nil, err } - return s.subscribeFiatRates(c, strings.ToLower(r.Currency), req) + r.Currency = strings.ToLower(r.Currency) + for i := range r.Tokens { + r.Tokens[i] = strings.ToLower(r.Tokens[i]) + } + return s.subscribeFiatRates(c, &r, req) }, "unsubscribeFiatRates": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { return s.unsubscribeFiatRates(c) @@ -397,10 +406,11 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs "getCurrentFiatRates": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { r := struct { Currencies []string `json:"currencies"` + Token string `json:"token"` }{} err = json.Unmarshal(req.Params, &r) if err == nil { - rv, err = s.getCurrentFiatRates(r.Currencies) + rv, err = s.getCurrentFiatRates(r.Currencies, r.Token) } return }, @@ -408,20 +418,22 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs r := struct { Timestamps []int64 `json:"timestamps"` Currencies []string `json:"currencies"` + Token string `json:"token"` }{} err = json.Unmarshal(req.Params, &r) if err == nil { - rv, err = s.getFiatRatesForTimestamps(r.Timestamps, r.Currencies) + rv, err = s.getFiatRatesForTimestamps(r.Timestamps, r.Currencies, r.Token) } return }, "getFiatRatesTickersList": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { r := struct { - Timestamp int64 `json:"timestamp"` + Timestamp int64 `json:"timestamp"` + Token string `json:"token"` }{} err = json.Unmarshal(req.Params, &r) if err == nil { - rv, err = s.getAvailableVsCurrencies(r.Timestamp) + rv, err = s.getAvailableVsCurrencies(r.Timestamp, r.Token) } return }, @@ -799,14 +811,16 @@ func (s *WebsocketServer) doUnsubscribeFiatRates(c *websocketChannel) { delete(s.fiatRatesSubscriptions, fr) } } + delete(s.fiatRatesTokenSubscriptions, c) } // subscribeFiatRates subscribes all FiatRates subscriptions by this channel -func (s *WebsocketServer) subscribeFiatRates(c *websocketChannel, currency string, req *websocketReq) (res interface{}, err error) { +func (s *WebsocketServer) subscribeFiatRates(c *websocketChannel, d *fiatRatesSubscription, req *websocketReq) (res interface{}, err error) { s.fiatRatesSubscriptionsLock.Lock() defer s.fiatRatesSubscriptionsLock.Unlock() // unsubscribe all previous subscriptions s.doUnsubscribeFiatRates(c) + currency := d.Currency if currency == "" { currency = allFiatRates } @@ -816,6 +830,9 @@ func (s *WebsocketServer) subscribeFiatRates(c *websocketChannel, currency strin s.fiatRatesSubscriptions[currency] = as } as[c] = req.ID + if len(d.Tokens) != 0 { + s.fiatRatesTokenSubscriptions[c] = d.Tokens + } s.metrics.WebsocketSubscribes.With((common.Labels{"method": "subscribeFiatRates"})).Set(float64(len(s.fiatRatesSubscriptions))) return &subscriptionResponse{true}, nil } @@ -960,7 +977,7 @@ func (s *WebsocketServer) OnNewTx(tx *bchain.MempoolTx) { } } -func (s *WebsocketServer) broadcastTicker(currency string, rates map[string]float32) { +func (s *WebsocketServer) broadcastTicker(currency string, rates map[string]float32, ticker *db.CurrencyRatesTicker) { as, ok := s.fiatRatesSubscriptions[currency] if ok && len(as) > 0 { data := struct { @@ -969,10 +986,34 @@ func (s *WebsocketServer) broadcastTicker(currency string, rates map[string]floa Rates: rates, } for c, id := range as { - c.DataOut(&websocketRes{ - ID: id, - Data: &data, - }) + var tokens []string + if ticker != nil { + tokens = s.fiatRatesTokenSubscriptions[c] + } + if len(tokens) > 0 { + dataWithTokens := struct { + Rates interface{} `json:"rates"` + TokenRates map[string]float32 `json:"tokenRates,omitempty"` + }{ + Rates: rates, + TokenRates: map[string]float32{}, + } + for _, token := range tokens { + rate := ticker.TokenRateInCurrency(token, currency) + if rate > 0 { + dataWithTokens.TokenRates[token] = rate + } + } + c.DataOut(&websocketRes{ + ID: id, + Data: &dataWithTokens, + }) + } else { + c.DataOut(&websocketRes{ + ID: id, + Data: &data, + }) + } } glog.Info("broadcasting new rates for currency ", currency, " to ", len(as), " channels") } @@ -983,22 +1024,22 @@ func (s *WebsocketServer) OnNewFiatRatesTicker(ticker *db.CurrencyRatesTicker) { s.fiatRatesSubscriptionsLock.Lock() defer s.fiatRatesSubscriptionsLock.Unlock() for currency, rate := range ticker.Rates { - s.broadcastTicker(currency, map[string]float32{currency: rate}) + s.broadcastTicker(currency, map[string]float32{currency: rate}, ticker) } - s.broadcastTicker(allFiatRates, ticker.Rates) + s.broadcastTicker(allFiatRates, ticker.Rates, nil) } -func (s *WebsocketServer) getCurrentFiatRates(currencies []string) (interface{}, error) { - ret, err := s.api.GetCurrentFiatRates(currencies) +func (s *WebsocketServer) getCurrentFiatRates(currencies []string, token string) (interface{}, error) { + ret, err := s.api.GetCurrentFiatRates(currencies, strings.ToLower(token)) return ret, err } -func (s *WebsocketServer) getFiatRatesForTimestamps(timestamps []int64, currencies []string) (interface{}, error) { - ret, err := s.api.GetFiatRatesForTimestamps(timestamps, currencies) +func (s *WebsocketServer) getFiatRatesForTimestamps(timestamps []int64, currencies []string, token string) (interface{}, error) { + ret, err := s.api.GetFiatRatesForTimestamps(timestamps, currencies, strings.ToLower(token)) return ret, err } -func (s *WebsocketServer) getAvailableVsCurrencies(timestamp int64) (interface{}, error) { - ret, err := s.api.GetAvailableVsCurrencies(timestamp) +func (s *WebsocketServer) getAvailableVsCurrencies(timestamp int64, token string) (interface{}, error) { + ret, err := s.api.GetAvailableVsCurrencies(timestamp, strings.ToLower(token)) return ret, err } diff --git a/static/test-websocket.html b/static/test-websocket.html index ec7180b847..37ab4c754f 100644 --- a/static/test-websocket.html +++ b/static/test-websocket.html @@ -7,6 +7,9 @@ Blockbook Websocket Test Page @@ -94,6 +97,12 @@ } }; } + function paramAsArray(name) { + const p = document.getElementById(name).value; + if(p) { + return p.split(",").map(s => s.trim()); + } + } function getInfo() { const method = 'getInfo'; @@ -166,7 +175,7 @@ const descriptor = document.getElementById('getBalanceHistoryDescriptor').value.trim(); const from = parseInt(document.getElementById("getBalanceHistoryFrom").value.trim()); const to = parseInt(document.getElementById("getBalanceHistoryTo").value.trim()); - const currencies = document.getElementById('getBalanceHistoryFiat').value.split(","); + const currencies = paramAsArray('getBalanceHistoryFiat'); const groupBy = parseInt(document.getElementById("getBalanceHistoryGroupBy").value); const method = 'getBalanceHistory'; const params = { @@ -207,7 +216,7 @@ function estimateFee() { try { - var blocks = document.getElementById('estimateFeeBlocks').value.split(","); + var blocks = paramAsArray('estimateFeeBlocks'); var specific = document.getElementById('estimateFeeSpecific').value.trim(); if (specific) { // example for bitcoin type: {"conservative": false,"txsize":1234} @@ -299,8 +308,7 @@ function subscribeAddresses() { const method = 'subscribeAddresses'; - var addresses = document.getElementById('subscribeAddressesName').value.split(","); - addresses = addresses.map(s => s.trim()); + var addresses = paramAsArray('subscribeAddressesName'); const params = { addresses }; @@ -329,12 +337,14 @@ function getFiatRatesForTimestamps() { const method = 'getFiatRatesForTimestamps'; - var timestamps = document.getElementById('getFiatRatesForTimestampsList').value.split(","); - var currencies = document.getElementById('getFiatRatesForTimestampsCurrency').value.split(","); + var timestamps = paramAsArray('getFiatRatesForTimestampsList'); + var currencies = paramAsArray('getFiatRatesForTimestampsCurrency'); + var token = document.getElementById('getFiatRatesForTimestampsToken').value; timestamps = timestamps.map(Number); const params = { timestamps, - 'currencies': currencies + 'currencies': currencies, + token, }; send(method, params, function (result) { document.getElementById('getFiatRatesForTimestampsResult').innerText = JSON.stringify(result).replace(/,/g, ", "); @@ -343,9 +353,11 @@ function getCurrentFiatRates() { const method = 'getCurrentFiatRates'; - var currencies = document.getElementById('getCurrentFiatRatesCurrency').value.split(","); + var currencies = paramAsArray('getCurrentFiatRatesCurrency'); + var token = document.getElementById('getCurrentFiatRatesToken').value; const params = { - "currencies": currencies + "currencies": currencies, + token, }; send(method, params, function (result) { document.getElementById('getCurrentFiatRatesResult').innerText = JSON.stringify(result).replace(/,/g, ", "); @@ -355,9 +367,11 @@ function getFiatRatesTickersList() { const method = 'getFiatRatesTickersList'; var timestamp = document.getElementById('getFiatRatesTickersListDate').value; + var token = document.getElementById('getFiatRatesTickersToken').value; timestamp = parseInt(timestamp); const params = { timestamp, + token, }; send(method, params, function (result) { document.getElementById('getFiatRatesTickersListResult').innerText = JSON.stringify(result).replace(/,/g, ", "); @@ -367,8 +381,10 @@ function subscribeNewFiatRatesTicker() { const method = 'subscribeFiatRates'; var currency = document.getElementById('subscribeFiatRatesCurrency').value; + var tokens = paramAsArray('subscribeFiatRatesTokens'); const params = { - "currency": currency + "currency": currency, + tokens, }; if (subscribeNewFiatRatesTickerId) { delete subscriptions[subscribeNewFiatRatesTickerId]; @@ -473,7 +489,7 @@

Blockbook Websocket Test Page

-
+
@@ -509,7 +525,7 @@

Blockbook Websocket Test Page

-
+
@@ -524,7 +540,7 @@

Blockbook Websocket Test Page

-
+
@@ -573,6 +589,9 @@

Blockbook Websocket Test Page

+
+ +
@@ -584,17 +603,23 @@

Blockbook Websocket Test Page

+
+ +
- +
-
+
+
+ +
@@ -645,14 +670,17 @@

Blockbook Websocket Test Page

-
- +
+
- + +
+
+
- +
From 91ede10a036a92af6c29d1c16d1cffd7735a17e6 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 23 Aug 2022 01:59:45 +0200 Subject: [PATCH 068/530] Bump geth to 1.10.23, prysm to 3.0.0, add goerli and mainnet consensus --- configs/coins/ethereum.json | 8 +-- configs/coins/ethereum_archive.json | 10 +-- configs/coins/ethereum_archive_consensus.json | 42 ++++++++++++ configs/coins/ethereum_consensus.json | 42 ++++++++++++ configs/coins/ethereum_testnet_goerli.json | 12 ++-- .../ethereum_testnet_goerli_archive.json | 68 +++++++++++++++++++ ...reum_testnet_goerli_archive_consensus.json | 42 ++++++++++++ .../ethereum_testnet_goerli_consensus.json | 42 ++++++++++++ configs/coins/ethereum_testnet_ropsten.json | 7 +- .../ethereum_testnet_ropsten_archive.json | 8 +-- ...eum_testnet_ropsten_archive_consensus.json | 8 +-- .../ethereum_testnet_ropsten_consensus.json | 8 +-- 12 files changed, 267 insertions(+), 30 deletions(-) create mode 100644 configs/coins/ethereum_archive_consensus.json create mode 100644 configs/coins/ethereum_consensus.json create mode 100644 configs/coins/ethereum_testnet_goerli_archive.json create mode 100644 configs/coins/ethereum_testnet_goerli_archive_consensus.json create mode 100644 configs/coins/ethereum_testnet_goerli_consensus.json diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index fb5c6c28dc..33abee5799 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,13 +22,13 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.21-67109427", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz", + "version": "1.10.23-d901d853", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.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 --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug\" --http --http.port 8136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug\" --authrpc.port 8536 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 8136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 8536 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 6714e34844..446f5c7e9c 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,13 +22,13 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.19-23bee162", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz", + "version": "1.10.23-d901d853", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.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 --gcmode archive --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 8116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 8516 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 8116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 8516 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -49,7 +49,7 @@ "parse": true, "mempool_workers": 8, "mempool_sub_workers": 2, - "block_addresses_to_keep": 300, + "block_addresses_to_keep": 600, "additional_params": { "address_aliases": true, "mempoolTxTimeoutHours": 48, diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json new file mode 100644 index 0000000000..34eff66ea7 --- /dev/null +++ b/configs/coins/ethereum_archive_consensus.json @@ -0,0 +1,42 @@ +{ + "coin": { + "name": "Ethereum Archive", + "shortcut": "ETH", + "label": "Ethereum", + "alias": "ethereum_archive_consensus", + "execution_alias": "ethereum_archive" + }, + "ports": { + "backend_rpc": 8016, + "backend_message_queue": 0, + "backend_p2p": 38316, + "backend_http": 8116, + "backend_authrpc": 8516, + "blockbook_internal": 9016, + "blockbook_public": 9116 + }, + "backend": { + "package_name": "backend-ethereum-archive-consensus", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "3.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", + "verification_type": "sha256", + "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", + "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:8516 --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "" + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json new file mode 100644 index 0000000000..41b4c3aed7 --- /dev/null +++ b/configs/coins/ethereum_consensus.json @@ -0,0 +1,42 @@ +{ + "coin": { + "name": "Ethereum", + "shortcut": "ETH", + "label": "Ethereum", + "alias": "ethereum_consensus", + "execution_alias": "ethereum" + }, + "ports": { + "backend_rpc": 8036, + "backend_message_queue": 0, + "backend_p2p": 38336, + "backend_http": 8136, + "backend_authrpc": 8536, + "blockbook_internal": 9036, + "blockbook_public": 9136 + }, + "backend": { + "package_name": "backend-ethereum-consensus", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "3.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", + "verification_type": "sha256", + "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", + "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:8536 --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "" + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index 0e70afbeb9..f8a6bc4b53 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -9,6 +9,8 @@ "backend_rpc": 18026, "backend_message_queue": 0, "backend_p2p": 48326, + "backend_http": 18126, + "backend_authrpc": 18526, "blockbook_internal": 19026, "blockbook_public": 19126 }, @@ -20,13 +22,13 @@ "package_name": "backend-ethereum-testnet-goerli", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.21-67109427", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz", + "version": "1.10.23-d901d853", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48326 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48326 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18126 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18526 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -47,7 +49,7 @@ "parse": true, "mempool_workers": 8, "mempool_sub_workers": 2, - "block_addresses_to_keep": 300, + "block_addresses_to_keep": 3000, "additional_params": { "mempoolTxTimeoutHours": 12, "queryBackendOnMempoolResync": false diff --git a/configs/coins/ethereum_testnet_goerli_archive.json b/configs/coins/ethereum_testnet_goerli_archive.json new file mode 100644 index 0000000000..9e67f84bec --- /dev/null +++ b/configs/coins/ethereum_testnet_goerli_archive.json @@ -0,0 +1,68 @@ +{ + "coin": { + "name": "Ethereum Testnet Goerli Archive", + "shortcut": "gGOE", + "label": "Ethereum Goerli", + "alias": "ethereum_testnet_goerli_archive" + }, + "ports": { + "backend_rpc": 18006, + "backend_message_queue": 0, + "backend_p2p": 48306, + "backend_http": 18106, + "backend_authrpc": 18506, + "blockbook_internal": 19006, + "blockbook_public": 19106 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-ethereum-testnet-goerli-archive", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "1.10.23-d901d853", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", + "verification_type": "gpg", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48306 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18106 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18506 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-ethereum-testnet-goerli-archive", + "system_user": "blockbook-ethereum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "-workers=16", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 3000, + "additional_params": { + "address_aliases": true, + "mempoolTxTimeoutHours": 12, + "processInternalTransactions": true, + "queryBackendOnMempoolResync": false, + "fiat_rates-disabled": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_goerli_archive_consensus.json b/configs/coins/ethereum_testnet_goerli_archive_consensus.json new file mode 100644 index 0000000000..31192e4c88 --- /dev/null +++ b/configs/coins/ethereum_testnet_goerli_archive_consensus.json @@ -0,0 +1,42 @@ +{ + "coin": { + "name": "Ethereum Testnet Goerli Archive", + "shortcut": "tROP", + "label": "Ethereum Goerli", + "alias": "ethereum_testnet_goerli_archive_consensus", + "execution_alias": "ethereum_testnet_goerli_archive" + }, + "ports": { + "backend_rpc": 18006, + "backend_message_queue": 0, + "backend_p2p": 48306, + "backend_http": 18106, + "backend_authrpc": 18506, + "blockbook_internal": 19006, + "blockbook_public": 19106 + }, + "backend": { + "package_name": "backend-ethereum-testnet-goerli-archive-consensus", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "3.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", + "verification_type": "sha256", + "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", + "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:18506 --grpc-gateway-port=17506 --rpc-port=17507 --monitoring-port=17508 --p2p-tcp-port=13506 --p2p-udp-port=12506 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "wget https://github.com/eth-clients/eth2-networks/raw/master/shared/prater/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "" + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_goerli_consensus.json b/configs/coins/ethereum_testnet_goerli_consensus.json new file mode 100644 index 0000000000..0c7e5cc732 --- /dev/null +++ b/configs/coins/ethereum_testnet_goerli_consensus.json @@ -0,0 +1,42 @@ +{ + "coin": { + "name": "Ethereum Testnet Goerli", + "shortcut": "tROP", + "label": "Ethereum Goerli", + "alias": "ethereum_testnet_goerli_consensus", + "execution_alias": "ethereum_testnet_goerli" + }, + "ports": { + "backend_rpc": 18026, + "backend_message_queue": 0, + "backend_p2p": 48326, + "backend_http": 18126, + "backend_authrpc": 18526, + "blockbook_internal": 19026, + "blockbook_public": 19126 + }, + "backend": { + "package_name": "backend-ethereum-testnet-goerli-consensus", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "3.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", + "verification_type": "sha256", + "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", + "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:18526 --grpc-gateway-port=17526 --rpc-port=17527 --monitoring-port=17528 --p2p-tcp-port=13526 --p2p-udp-port=12526 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "wget https://github.com/eth-clients/eth2-networks/raw/master/shared/prater/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "" + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 439ec7891e..924d22a938 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-ropsten", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.21-67109427", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz", + "version": "1.10.23-d901d853", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.21-67109427.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --override.terminaltotaldifficulty 50000000000000000 --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18536 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -52,7 +52,6 @@ "block_addresses_to_keep": 3000, "additional_params": { "mempoolTxTimeoutHours": 12, - "processInternalTransactions": true, "queryBackendOnMempoolResync": false } } diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index 200d8bda6f..ae342a3856 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-ropsten-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.19-23bee162", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz", + "version": "1.10.23-d901d853", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --override.terminaltotaldifficulty 50000000000000000 --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18516 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -55,7 +55,7 @@ "mempoolTxTimeoutHours": 12, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, - "fiat_rates": "coingecko", + "fiat_rates-disabled": "coingecko", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } diff --git a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json index 13001fa2f3..962b3f9b35 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json @@ -19,13 +19,13 @@ "package_name": "backend-ethereum-testnet-ropsten-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.1.3-rc.4", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v2.1.3-rc.4/beacon-chain-v2.1.3-rc.4-linux-amd64", + "version": "3.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", "verification_type": "sha256", - "verification_source": "ea4cdec3854c6265e689648413d3c4357341643d0ca36fbc376c091030719a6e", + "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --http-web3provider=http://localhost:18516 --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:18516 --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "wget https://github.com/eth-clients/merge-testnets/raw/e4a6f0c181d24b28bc8651744f1d0e9ef74bda3f/ropsten-beacon-chain/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_ropsten_consensus.json b/configs/coins/ethereum_testnet_ropsten_consensus.json index dc3b8753b0..9cb41393b0 100644 --- a/configs/coins/ethereum_testnet_ropsten_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_consensus.json @@ -19,13 +19,13 @@ "package_name": "backend-ethereum-testnet-ropsten-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.1.3-rc.4", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v2.1.3-rc.4/beacon-chain-v2.1.3-rc.4-linux-amd64", + "version": "3.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", "verification_type": "sha256", - "verification_source": "ea4cdec3854c6265e689648413d3c4357341643d0ca36fbc376c091030719a6e", + "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --http-web3provider=http://localhost:18536 --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13536 --p2p-udp-port=12536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:18536 --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13536 --p2p-udp-port=12536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "wget https://github.com/eth-clients/merge-testnets/raw/e4a6f0c181d24b28bc8651744f1d0e9ef74bda3f/ropsten-beacon-chain/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", "service_type": "simple", From 76b6f93059e58842676f3ad2bcc94a6a66f50387 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 26 Aug 2022 16:48:46 +0200 Subject: [PATCH 069/530] Add additional eth specific ports to config --- build/tools/templates.go | 3 +++ configs/coins/ethereum.json | 2 +- configs/coins/ethereum_archive.json | 2 +- configs/coins/ethereum_archive_consensus.json | 2 +- configs/coins/ethereum_consensus.json | 2 +- configs/coins/ethereum_testnet_goerli.json | 2 +- configs/coins/ethereum_testnet_goerli_archive.json | 2 +- configs/coins/ethereum_testnet_goerli_archive_consensus.json | 2 +- configs/coins/ethereum_testnet_goerli_consensus.json | 2 +- configs/coins/ethereum_testnet_ropsten.json | 2 +- configs/coins/ethereum_testnet_ropsten_archive.json | 2 +- configs/coins/ethereum_testnet_ropsten_archive_consensus.json | 2 +- configs/coins/ethereum_testnet_ropsten_consensus.json | 2 +- 13 files changed, 15 insertions(+), 12 deletions(-) diff --git a/build/tools/templates.go b/build/tools/templates.go index 13f1d5c777..42b16c03ca 100644 --- a/build/tools/templates.go +++ b/build/tools/templates.go @@ -49,6 +49,9 @@ type Config struct { Ports struct { BackendRPC int `json:"backend_rpc"` BackendMessageQueue int `json:"backend_message_queue"` + BackendP2P int `json:"backend_p2p"` + BackendHttp int `json:"backend_http"` + BackendAuthRpc int `json:"backend_authrpc"` BlockbookInternal int `json:"blockbook_internal"` BlockbookPublic int `json:"blockbook_public"` } `json:"ports"` diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 33abee5799..c3c0898c76 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -28,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 8136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 8536 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 446f5c7e9c..ff341ccd53 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -28,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 8116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 8516 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 34eff66ea7..93dd1d578b 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -25,7 +25,7 @@ "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:8516 --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 41b4c3aed7..6d969619b2 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -25,7 +25,7 @@ "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:8536 --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index f8a6bc4b53..f4c3b83ba5 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -28,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48326 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18126 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18526 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_goerli_archive.json b/configs/coins/ethereum_testnet_goerli_archive.json index 9e67f84bec..08deb11d2e 100644 --- a/configs/coins/ethereum_testnet_goerli_archive.json +++ b/configs/coins/ethereum_testnet_goerli_archive.json @@ -28,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48306 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18106 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18506 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_goerli_archive_consensus.json b/configs/coins/ethereum_testnet_goerli_archive_consensus.json index 31192e4c88..52db9f004d 100644 --- a/configs/coins/ethereum_testnet_goerli_archive_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_archive_consensus.json @@ -25,7 +25,7 @@ "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:18506 --grpc-gateway-port=17506 --rpc-port=17507 --monitoring-port=17508 --p2p-tcp-port=13506 --p2p-udp-port=12506 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17506 --rpc-port=17507 --monitoring-port=17508 --p2p-tcp-port=13506 --p2p-udp-port=12506 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "wget https://github.com/eth-clients/eth2-networks/raw/master/shared/prater/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_goerli_consensus.json b/configs/coins/ethereum_testnet_goerli_consensus.json index 0c7e5cc732..0491d85cf2 100644 --- a/configs/coins/ethereum_testnet_goerli_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_consensus.json @@ -25,7 +25,7 @@ "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:18526 --grpc-gateway-port=17526 --rpc-port=17527 --monitoring-port=17528 --p2p-tcp-port=13526 --p2p-udp-port=12526 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17526 --rpc-port=17527 --monitoring-port=17528 --p2p-tcp-port=13526 --p2p-udp-port=12526 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "wget https://github.com/eth-clients/eth2-networks/raw/master/shared/prater/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 924d22a938..41dbd55fd5 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -28,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --override.terminaltotaldifficulty 50000000000000000 --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18136 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18536 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --override.terminaltotaldifficulty 50000000000000000 --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index ae342a3856..1e7d19cbe3 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -28,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --override.terminaltotaldifficulty 50000000000000000 --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48316 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port 18116 -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port 18516 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --override.terminaltotaldifficulty 50000000000000000 --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json index 962b3f9b35..43171dbe90 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json @@ -25,7 +25,7 @@ "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:18516 --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "wget https://github.com/eth-clients/merge-testnets/raw/e4a6f0c181d24b28bc8651744f1d0e9ef74bda3f/ropsten-beacon-chain/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", "service_type": "simple", diff --git a/configs/coins/ethereum_testnet_ropsten_consensus.json b/configs/coins/ethereum_testnet_ropsten_consensus.json index 9cb41393b0..8220aff5c4 100644 --- a/configs/coins/ethereum_testnet_ropsten_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_consensus.json @@ -25,7 +25,7 @@ "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:18536 --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13536 --p2p-udp-port=12536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13536 --p2p-udp-port=12536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "wget https://github.com/eth-clients/merge-testnets/raw/e4a6f0c181d24b28bc8651744f1d0e9ef74bda3f/ropsten-beacon-chain/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", "service_type": "simple", From 835d0e07ba9ebd6bf8d3b1d911563bd34b819029 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 26 Aug 2022 17:42:03 +0200 Subject: [PATCH 070/530] Return ethereum consensus layer node version via API --- api/worker.go | 27 ++++++------ bchain/coins/eth/ethrpc.go | 44 +++++++++++++++++-- bchain/types.go | 25 ++++++----- common/internalstate.go | 27 ++++++------ configs/coins/ethereum.json | 3 +- configs/coins/ethereum_archive.json | 1 + configs/coins/ethereum_testnet_goerli.json | 1 + .../ethereum_testnet_goerli_archive.json | 1 + configs/coins/ethereum_testnet_ropsten.json | 1 + .../ethereum_testnet_ropsten_archive.json | 1 + db/sync.go | 27 ++++++------ server/websocket.go | 14 +++--- static/templates/index.html | 10 +++++ 13 files changed, 120 insertions(+), 62 deletions(-) diff --git a/api/worker.go b/api/worker.go index cad14df1d4..87eb56a26e 100644 --- a/api/worker.go +++ b/api/worker.go @@ -2075,19 +2075,20 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) { About: Text.BlockbookAbout, } backendInfo := &common.BackendInfo{ - BackendError: backendError, - BestBlockHash: ci.Bestblockhash, - Blocks: ci.Blocks, - Chain: ci.Chain, - Difficulty: ci.Difficulty, - Headers: ci.Headers, - ProtocolVersion: ci.ProtocolVersion, - SizeOnDisk: ci.SizeOnDisk, - Subversion: ci.Subversion, - Timeoffset: ci.Timeoffset, - Version: ci.Version, - Warnings: ci.Warnings, - Consensus: ci.Consensus, + BackendError: backendError, + BestBlockHash: ci.Bestblockhash, + Blocks: ci.Blocks, + Chain: ci.Chain, + Difficulty: ci.Difficulty, + Headers: ci.Headers, + ProtocolVersion: ci.ProtocolVersion, + SizeOnDisk: ci.SizeOnDisk, + Subversion: ci.Subversion, + Timeoffset: ci.Timeoffset, + Version: ci.Version, + Warnings: ci.Warnings, + ConsensusVersion: ci.ConsensusVersion, + Consensus: ci.Consensus, } w.is.SetBackendInfo(backendInfo) glog.Info("GetSystemInfo, ", time.Since(start)) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 1e48bc54ae..001a199cc6 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -4,7 +4,9 @@ import ( "context" "encoding/json" "fmt" + "io/ioutil" "math/big" + "net/http" "strconv" "strings" "sync" @@ -46,6 +48,7 @@ type Configuration struct { QueryBackendOnMempoolResync bool `json:"queryBackendOnMempoolResync"` ProcessInternalTransactions bool `json:"processInternalTransactions"` ProcessZeroInternalTransactions bool `json:"processZeroInternalTransactions"` + ConsensusNodeVersionURL string `json:"consensusNodeVersion"` } // EthereumRPC is an interface to JSON-RPC eth service. @@ -335,6 +338,37 @@ func (b *EthereumRPC) GetSubversion() string { return "" } +func (b *EthereumRPC) getConsensusVersion() string { + if b.ChainConfig.ConsensusNodeVersionURL == "" { + return "" + } + httpClient := &http.Client{ + Timeout: 2 * time.Second, + } + resp, err := httpClient.Get(b.ChainConfig.ConsensusNodeVersionURL) + if err != nil || resp.StatusCode != http.StatusOK { + glog.Error("getConsensusVersion ", err) + return "" + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + glog.Error("getConsensusVersion ", err) + return "" + } + type consensusVersion struct { + Data struct { + Version string `json:"version"` + } `json:"data"` + } + var v consensusVersion + err = json.Unmarshal(body, &v) + if err != nil { + glog.Error("getConsensusVersion ", err) + return "" + } + return v.Data.Version +} + // GetChainInfo returns information about the connected backend func (b *EthereumRPC) GetChainInfo() (*bchain.ChainInfo, error) { h, err := b.getBestHeader() @@ -351,11 +385,13 @@ func (b *EthereumRPC) GetChainInfo() (*bchain.ChainInfo, error) { if err := b.rpc.CallContext(ctx, &ver, "web3_clientVersion"); err != nil { return nil, err } + consensusVersion := b.getConsensusVersion() rv := &bchain.ChainInfo{ - Blocks: int(h.Number.Int64()), - Bestblockhash: h.Hash().Hex(), - Difficulty: h.Difficulty.String(), - Version: ver, + Blocks: int(h.Number.Int64()), + Bestblockhash: h.Hash().Hex(), + Difficulty: h.Difficulty.String(), + Version: ver, + ConsensusVersion: consensusVersion, } idi := int(id.Uint64()) if idi == 1 { diff --git a/bchain/types.go b/bchain/types.go index 4d9af1f946..713b30be60 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -192,18 +192,19 @@ type MempoolEntry struct { // ChainInfo is used to get information about blockchain type ChainInfo struct { - Chain string `json:"chain"` - Blocks int `json:"blocks"` - Headers int `json:"headers"` - Bestblockhash string `json:"bestblockhash"` - Difficulty string `json:"difficulty"` - SizeOnDisk int64 `json:"size_on_disk"` - Version string `json:"version"` - Subversion string `json:"subversion"` - ProtocolVersion string `json:"protocolversion"` - Timeoffset float64 `json:"timeoffset"` - Warnings string `json:"warnings"` - Consensus interface{} `json:"consensus,omitempty"` + Chain string `json:"chain"` + Blocks int `json:"blocks"` + Headers int `json:"headers"` + Bestblockhash string `json:"bestblockhash"` + Difficulty string `json:"difficulty"` + SizeOnDisk int64 `json:"size_on_disk"` + Version string `json:"version"` + Subversion string `json:"subversion"` + ProtocolVersion string `json:"protocolversion"` + Timeoffset float64 `json:"timeoffset"` + Warnings string `json:"warnings"` + ConsensusVersion string `json:"consensus_version,omitempty"` + Consensus interface{} `json:"consensus,omitempty"` } // RPCError defines rpc error returned by backend diff --git a/common/internalstate.go b/common/internalstate.go index 3829094d5e..a7090c80bd 100644 --- a/common/internalstate.go +++ b/common/internalstate.go @@ -31,19 +31,20 @@ type InternalStateColumn struct { // BackendInfo is used to get information about blockchain type BackendInfo struct { - BackendError string `json:"error,omitempty"` - Chain string `json:"chain,omitempty"` - Blocks int `json:"blocks,omitempty"` - Headers int `json:"headers,omitempty"` - BestBlockHash string `json:"bestBlockHash,omitempty"` - Difficulty string `json:"difficulty,omitempty"` - SizeOnDisk int64 `json:"sizeOnDisk,omitempty"` - Version string `json:"version,omitempty"` - Subversion string `json:"subversion,omitempty"` - ProtocolVersion string `json:"protocolVersion,omitempty"` - Timeoffset float64 `json:"timeOffset,omitempty"` - Warnings string `json:"warnings,omitempty"` - Consensus interface{} `json:"consensus,omitempty"` + BackendError string `json:"error,omitempty"` + Chain string `json:"chain,omitempty"` + Blocks int `json:"blocks,omitempty"` + Headers int `json:"headers,omitempty"` + BestBlockHash string `json:"bestBlockHash,omitempty"` + Difficulty string `json:"difficulty,omitempty"` + SizeOnDisk int64 `json:"sizeOnDisk,omitempty"` + Version string `json:"version,omitempty"` + Subversion string `json:"subversion,omitempty"` + ProtocolVersion string `json:"protocolVersion,omitempty"` + Timeoffset float64 `json:"timeOffset,omitempty"` + Warnings string `json:"warnings,omitempty"` + ConsensusVersion string `json:"consensus_version,omitempty"` + Consensus interface{} `json:"consensus,omitempty"` } // InternalState contains the data of the internal state diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index c3c0898c76..7a11de2be9 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -51,10 +51,11 @@ "mempool_sub_workers": 2, "block_addresses_to_keep": 300, "additional_params": { + "consensusNodeVersion": "http://localhost:7536/eth/v1/node/version", "mempoolTxTimeoutHours": 48, "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index ff341ccd53..c03bbbc160 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -51,6 +51,7 @@ "mempool_sub_workers": 2, "block_addresses_to_keep": 600, "additional_params": { + "consensusNodeVersion": "http://localhost:7516/eth/v1/node/version", "address_aliases": true, "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index f4c3b83ba5..e9d784212f 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -51,6 +51,7 @@ "mempool_sub_workers": 2, "block_addresses_to_keep": 3000, "additional_params": { + "consensusNodeVersion": "http://localhost:17526/eth/v1/node/version", "mempoolTxTimeoutHours": 12, "queryBackendOnMempoolResync": false } diff --git a/configs/coins/ethereum_testnet_goerli_archive.json b/configs/coins/ethereum_testnet_goerli_archive.json index 08deb11d2e..9bdf5590e9 100644 --- a/configs/coins/ethereum_testnet_goerli_archive.json +++ b/configs/coins/ethereum_testnet_goerli_archive.json @@ -51,6 +51,7 @@ "mempool_sub_workers": 2, "block_addresses_to_keep": 3000, "additional_params": { + "consensusNodeVersion": "http://localhost:17506/eth/v1/node/version", "address_aliases": true, "mempoolTxTimeoutHours": 12, "processInternalTransactions": true, diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 41dbd55fd5..c607f10151 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -51,6 +51,7 @@ "mempool_sub_workers": 2, "block_addresses_to_keep": 3000, "additional_params": { + "consensusNodeVersion": "http://localhost:17536/eth/v1/node/version", "mempoolTxTimeoutHours": 12, "queryBackendOnMempoolResync": false } diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index 1e7d19cbe3..a24e036c8b 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -51,6 +51,7 @@ "mempool_sub_workers": 2, "block_addresses_to_keep": 3000, "additional_params": { + "consensusNodeVersion": "http://localhost:17516/eth/v1/node/version", "address_aliases": true, "mempoolTxTimeoutHours": 12, "processInternalTransactions": true, diff --git a/db/sync.go b/db/sync.go index 895edf28fe..823ec9aa09 100644 --- a/db/sync.go +++ b/db/sync.go @@ -58,19 +58,20 @@ func (w *SyncWorker) updateBackendInfo() { ci = &bchain.ChainInfo{} } w.is.SetBackendInfo(&common.BackendInfo{ - BackendError: backendError, - BestBlockHash: ci.Bestblockhash, - Blocks: ci.Blocks, - Chain: ci.Chain, - Difficulty: ci.Difficulty, - Headers: ci.Headers, - ProtocolVersion: ci.ProtocolVersion, - SizeOnDisk: ci.SizeOnDisk, - Subversion: ci.Subversion, - Timeoffset: ci.Timeoffset, - Version: ci.Version, - Warnings: ci.Warnings, - Consensus: ci.Consensus, + BackendError: backendError, + BestBlockHash: ci.Bestblockhash, + Blocks: ci.Blocks, + Chain: ci.Chain, + Difficulty: ci.Difficulty, + Headers: ci.Headers, + ProtocolVersion: ci.ProtocolVersion, + SizeOnDisk: ci.SizeOnDisk, + Subversion: ci.Subversion, + Timeoffset: ci.Timeoffset, + Version: ci.Version, + Warnings: ci.Warnings, + ConsensusVersion: ci.ConsensusVersion, + Consensus: ci.Consensus, }) } diff --git a/server/websocket.go b/server/websocket.go index 17ea76a3d9..e02793a0aa 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -569,9 +569,10 @@ func (s *WebsocketServer) getInfo() (interface{}, error) { return nil, err } type backendInfo struct { - Version string `json:"version,omitempty"` - Subversion string `json:"subversion,omitempty"` - Consensus interface{} `json:"consensus,omitempty"` + Version string `json:"version,omitempty"` + Subversion string `json:"subversion,omitempty"` + ConsensusVersion string `json:"consensus_version,omitempty"` + Consensus interface{} `json:"consensus,omitempty"` } type info struct { Name string `json:"name"` @@ -594,9 +595,10 @@ func (s *WebsocketServer) getInfo() (interface{}, error) { Block0Hash: s.block0hash, Testnet: s.chain.IsTestnet(), Backend: backendInfo{ - Version: bi.Version, - Subversion: bi.Subversion, - Consensus: bi.Consensus, + Version: bi.Version, + Subversion: bi.Subversion, + ConsensusVersion: bi.ConsensusVersion, + Consensus: bi.Consensus, }, }, nil } diff --git a/static/templates/index.html b/static/templates/index.html index 2dc6a826d2..7708062251 100644 --- a/static/templates/index.html +++ b/static/templates/index.html @@ -72,14 +72,24 @@

Backend

Version {{$be.Version}} + {{- if $be.Subversion -}} Subversion {{$be.Subversion}} + {{- end -}} + {{- if $be.ProtocolVersion -}} Protocol Version {{$be.ProtocolVersion}} + {{- end -}} + {{- if $be.ConsensusVersion -}} + + Consensus Version + {{$be.ConsensusVersion}} + + {{- end -}} Last Block {{$be.Blocks}} From 6edbc2d99b9cf5679262855682bab6f7f16f3954 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 28 Aug 2022 18:53:58 +0200 Subject: [PATCH 071/530] Calculate and return tx vsize for selected coins Coins returning vsize: BTC, TEST, LTC, BTG, NMC, VTC, DGB --- api/types.go | 1 + api/worker.go | 5 + bchain/baseparser.go | 9 +- bchain/coins/bitcore/bitcoreparser_test.go | 2 +- bchain/coins/btc/bitcoinlikeparser.go | 15 + bchain/coins/btc/bitcoinparser.go | 6 +- bchain/coins/btc/bitcoinparser_test.go | 3 + bchain/coins/btg/bgoldparser.go | 4 +- bchain/coins/dash/dashparser_test.go | 4 +- .../coins/deeponion/deeponionparser_test.go | 2 +- bchain/coins/digibyte/digibyteparser.go | 4 +- bchain/coins/digibyte/digibyteparser_test.go | 1 + bchain/coins/divi/diviparser_test.go | 4 +- bchain/coins/firo/testdata/packedtxs.hex | 10 +- bchain/coins/fujicoin/fujicoinparser.go | 4 +- bchain/coins/grs/grsparser_test.go | 4 +- bchain/coins/koto/kotoparser_test.go | 4 +- bchain/coins/liquid/liquidparser_test.go | 2 +- bchain/coins/litecoin/litecoinparser.go | 4 +- bchain/coins/litecoin/litecoinparser_test.go | 3 +- .../monetaryunit/monetaryunitparser_test.go | 2 +- bchain/coins/namecoin/namecoinparser.go | 4 +- .../omotenashicoinparser_test.go | 4 +- bchain/coins/pivx/pivxparser_test.go | 6 +- bchain/coins/qtum/qtumparser.go | 4 +- .../coins/ravencoin/ravencoinparser_test.go | 4 +- bchain/coins/snowgem/snowgemparser_test.go | 4 +- bchain/coins/vertcoin/vertcoinparser.go | 4 +- bchain/coins/vertcoin/vertcoinparser_test.go | 1 + bchain/coins/zec/zcashparser_test.go | 4 +- bchain/tx.pb.go | 462 +++++++++++++----- bchain/tx.proto | 4 +- bchain/types.go | 3 + go.mod | 5 +- go.sum | 17 +- static/templates/tx.html | 6 + tests/rpc/testdata/bitcoin.json | 382 ++++++++------- tests/rpc/testdata/bitcoin_testnet.json | 244 ++++----- tests/rpc/testdata/digibyte.json | 101 ++-- tests/rpc/testdata/litecoin.json | 93 ++-- tests/rpc/testdata/namecoin.json | 137 +++--- tests/rpc/testdata/vertcoin.json | 186 +++---- 42 files changed, 1015 insertions(+), 753 deletions(-) diff --git a/api/types.go b/api/types.go index d1d200e002..c44bab2061 100644 --- a/api/types.go +++ b/api/types.go @@ -211,6 +211,7 @@ type Tx struct { Confirmations uint32 `json:"confirmations"` Blocktime int64 `json:"blockTime"` Size int `json:"size,omitempty"` + VSize int `json:"vsize,omitempty"` ValueOutSat *Amount `json:"value"` ValueInSat *Amount `json:"valueIn,omitempty"` FeesSat *Amount `json:"fees,omitempty"` diff --git a/api/worker.go b/api/worker.go index 87eb56a26e..3011f5de53 100644 --- a/api/worker.go +++ b/api/worker.go @@ -423,6 +423,11 @@ func (w *Worker) getTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe TokenTransfers: tokens, EthereumSpecific: ethSpecific, } + if w.chainParser.SupportsVSize() { + r.Size = len(bchainTx.Hex) >> 1 + r.VSize = int(bchainTx.VSize) + + } return r, nil } diff --git a/bchain/baseparser.go b/bchain/baseparser.go index e45c8f9ec4..f1278cc34d 100644 --- a/bchain/baseparser.go +++ b/bchain/baseparser.go @@ -6,10 +6,10 @@ import ( "math/big" "strings" - "github.com/gogo/protobuf/proto" "github.com/golang/glog" "github.com/juju/errors" "github.com/trezor/blockbook/common" + "google.golang.org/protobuf/proto" ) // BaseParser implements data parsing/handling functionality base for all other parsers @@ -173,6 +173,11 @@ func (p *BaseParser) MinimumCoinbaseConfirmations() int { return 0 } +// SupportsVSize returns true if vsize of a transaction should be computed and returned by API +func (p *BaseParser) SupportsVSize() bool { + return false +} + // PackTx packs transaction to byte array using protobuf func (p *BaseParser) PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, error) { var err error @@ -216,6 +221,7 @@ func (p *BaseParser) PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, err Vin: pti, Vout: pto, Version: tx.Version, + VSize: tx.VSize, } if pt.Hex, err = hex.DecodeString(tx.Hex); err != nil { return nil, errors.Annotatef(err, "Hex %v", tx.Hex) @@ -276,6 +282,7 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) { Vin: vin, Vout: vout, Version: pt.Version, + VSize: pt.VSize, } return &tx, pt.Height, nil } diff --git a/bchain/coins/bitcore/bitcoreparser_test.go b/bchain/coins/bitcore/bitcoreparser_test.go index a7c7d8c9f6..d3330de6c1 100644 --- a/bchain/coins/bitcore/bitcoreparser_test.go +++ b/bchain/coins/bitcore/bitcoreparser_test.go @@ -81,7 +81,7 @@ func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) { var ( testTx1 bchain.Tx - testTxPacked1 = "0a20fcd4f2e45787a33571bc9b2ce939d6e8e51fa053296de9240f05455702bd954012e2010200000001f69bd1fd76e52a426f21332e3b7cfbc3350eacbd21c6e0c11a7ae11919803ef0010000006b483045022100d1fa62b9d7860a03e1dcd4734fe42457cb508ebb49e896d7a77748d997d09fba022005f1657b39451afe97076d8667fe5f6f18ca76391521ab84d09d5b82137d933b0121035aaf032f13761f27465467dc73f1998a80dd4d85a6353d2832a7244d7b591d3effffffff02a87322b3010000001976a914d0c320db3fbd0abe2b6fe31a3bca4fed8ce8669588ac94b94f37000000001976a9145584ee07090af59938e991c9d8e9e945c99a449f88ac0000000018858a8ce205200028f9f3133299010a001220f03e801919e17a1ac1e0c621bdac0e35c3fb7c3b2e33216f422ae576fdd19bf61801226b483045022100d1fa62b9d7860a03e1dcd4734fe42457cb508ebb49e896d7a77748d997d09fba022005f1657b39451afe97076d8667fe5f6f18ca76391521ab84d09d5b82137d933b0121035aaf032f13761f27465467dc73f1998a80dd4d85a6353d2832a7244d7b591d3e28ffffffff0f3a480a0501b32273a810001a1976a914d0c320db3fbd0abe2b6fe31a3bca4fed8ce8669588ac22223259336546797741414673617039757139726942474143684e326858356a6e7268753a470a04374fb99410011a1976a9145584ee07090af59938e991c9d8e9e945c99a449f88ac2222324c6f7a646b704450723562356b6a66445042315a76454c597735734475684139594002" + testTxPacked1 = "0a20fcd4f2e45787a33571bc9b2ce939d6e8e51fa053296de9240f05455702bd954012e2010200000001f69bd1fd76e52a426f21332e3b7cfbc3350eacbd21c6e0c11a7ae11919803ef0010000006b483045022100d1fa62b9d7860a03e1dcd4734fe42457cb508ebb49e896d7a77748d997d09fba022005f1657b39451afe97076d8667fe5f6f18ca76391521ab84d09d5b82137d933b0121035aaf032f13761f27465467dc73f1998a80dd4d85a6353d2832a7244d7b591d3effffffff02a87322b3010000001976a914d0c320db3fbd0abe2b6fe31a3bca4fed8ce8669588ac94b94f37000000001976a9145584ee07090af59938e991c9d8e9e945c99a449f88ac0000000018858a8ce20528f9f3133297011220f03e801919e17a1ac1e0c621bdac0e35c3fb7c3b2e33216f422ae576fdd19bf61801226b483045022100d1fa62b9d7860a03e1dcd4734fe42457cb508ebb49e896d7a77748d997d09fba022005f1657b39451afe97076d8667fe5f6f18ca76391521ab84d09d5b82137d933b0121035aaf032f13761f27465467dc73f1998a80dd4d85a6353d2832a7244d7b591d3e28ffffffff0f3a460a0501b32273a81a1976a914d0c320db3fbd0abe2b6fe31a3bca4fed8ce8669588ac22223259336546797741414673617039757139726942474143684e326858356a6e7268753a470a04374fb99410011a1976a9145584ee07090af59938e991c9d8e9e945c99a449f88ac2222324c6f7a646b704450723562356b6a66445042315a76454c597735734475684139594002" ) func init() { diff --git a/bchain/coins/btc/bitcoinlikeparser.go b/bchain/coins/btc/bitcoinlikeparser.go index 716877ccc7..ba99d6fecc 100644 --- a/bchain/coins/btc/bitcoinlikeparser.go +++ b/bchain/coins/btc/bitcoinlikeparser.go @@ -35,6 +35,7 @@ type BitcoinLikeParser struct { XPubMagicSegwitP2sh uint32 XPubMagicSegwitNative uint32 Slip44 uint32 + VSizeSupport bool minimumCoinbaseConfirmations int } @@ -204,6 +205,14 @@ func (p *BitcoinLikeParser) outputScriptToAddresses(script []byte) ([]string, bo // TxFromMsgTx converts bitcoin wire Tx to bchain.Tx func (p *BitcoinLikeParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx { + var vSize int64 + if p.VSizeSupport { + baseSize := t.SerializeSizeStripped() + totalSize := t.SerializeSize() + weight := int64((baseSize * (blockchain.WitnessScaleFactor - 1)) + totalSize) + vSize = (weight + (blockchain.WitnessScaleFactor - 1)) / blockchain.WitnessScaleFactor + } + vin := make([]bchain.Vin, len(t.TxIn)) for i, in := range t.TxIn { if blockchain.IsCoinBaseTx(t) { @@ -248,6 +257,7 @@ func (p *BitcoinLikeParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bcha Txid: t.TxHash().String(), Version: t.Version, LockTime: t.LockTime, + VSize: vSize, Vin: vin, Vout: vout, // skip: BlockHash, @@ -320,6 +330,11 @@ func (p *BitcoinLikeParser) MinimumCoinbaseConfirmations() int { return p.minimumCoinbaseConfirmations } +// SupportsVSize returns true if vsize of a transaction should be computed and returned by API +func (p *BitcoinLikeParser) SupportsVSize() bool { + return p.VSizeSupport +} + var tapTweakTagHash = sha256.Sum256([]byte("TapTweak")) func tapTweakHash(msg []byte) []byte { diff --git a/bchain/coins/btc/bitcoinparser.go b/bchain/coins/btc/bitcoinparser.go index 29e573bd5f..77cbcf267e 100644 --- a/bchain/coins/btc/bitcoinparser.go +++ b/bchain/coins/btc/bitcoinparser.go @@ -16,9 +16,11 @@ type BitcoinParser struct { // NewBitcoinParser returns new BitcoinParser instance func NewBitcoinParser(params *chaincfg.Params, c *Configuration) *BitcoinParser { - return &BitcoinParser{ + p := &BitcoinParser{ BitcoinLikeParser: NewBitcoinLikeParser(params, c), } + p.VSizeSupport = true + return p } // GetChainParams contains network parameters for the main Bitcoin network, @@ -63,6 +65,7 @@ type Tx struct { Txid string `json:"txid"` Version int32 `json:"version"` LockTime uint32 `json:"locktime"` + VSize int64 `json:"vsize,omitempty"` Vin []bchain.Vin `json:"vin"` Vout []Vout `json:"vout"` BlockHeight uint32 `json:"blockHeight,omitempty"` @@ -88,6 +91,7 @@ func (p *BitcoinParser) ParseTxFromJson(msg json.RawMessage) (*bchain.Tx, error) tx.Txid = bitcoinTx.Txid tx.Version = bitcoinTx.Version tx.LockTime = bitcoinTx.LockTime + tx.VSize = bitcoinTx.VSize tx.Vin = bitcoinTx.Vin tx.BlockHeight = bitcoinTx.BlockHeight tx.Confirmations = bitcoinTx.Confirmations diff --git a/bchain/coins/btc/bitcoinparser_test.go b/bchain/coins/btc/bitcoinparser_test.go index 45edfd7b58..201d7ca9e5 100644 --- a/bchain/coins/btc/bitcoinparser_test.go +++ b/bchain/coins/btc/bitcoinparser_test.go @@ -480,6 +480,7 @@ func init() { Blocktime: 1519053802, Txid: "056e3d82e5ffd0e915fb9b62797d76263508c34fe3e5dbed30dd3e943930f204", LockTime: 512115, + VSize: 189, Version: 1, Vin: []bchain.Vin{ { @@ -510,6 +511,7 @@ func init() { Blocktime: 1235678901, Txid: "474e6795760ebe81cb4023dc227e5a0efe340e1771c89a0035276361ed733de7", LockTime: 0, + VSize: 166, Version: 1, Vin: []bchain.Vin{ { @@ -550,6 +552,7 @@ func init() { Blocktime: 1607805599, Txid: "24551a58a1d1fb89d7052e2bbac7cb69a7825ee1e39439befbec8c32148cf735", LockTime: 15745, + VSize: 208, Version: 2, Vin: []bchain.Vin{ { diff --git a/bchain/coins/btg/bgoldparser.go b/bchain/coins/btg/bgoldparser.go index 2e33380dea..aca095e077 100644 --- a/bchain/coins/btg/bgoldparser.go +++ b/bchain/coins/btg/bgoldparser.go @@ -52,7 +52,9 @@ type BGoldParser struct { // NewBGoldParser returns new BGoldParser instance func NewBGoldParser(params *chaincfg.Params, c *btc.Configuration) *BGoldParser { - return &BGoldParser{BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c)} + p := &BGoldParser{BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c)} + p.VSizeSupport = true + return p } // GetChainParams contains network parameters for the main Bitcoin Cash network, diff --git a/bchain/coins/dash/dashparser_test.go b/bchain/coins/dash/dashparser_test.go index f420b08455..3d6c872689 100644 --- a/bchain/coins/dash/dashparser_test.go +++ b/bchain/coins/dash/dashparser_test.go @@ -159,7 +159,7 @@ var ( }, }, } - testTxPacked1 = "0a20ed732a404cdfd4e0475a7a016200b7eef191f2c9de0ffdef8a20091c0499299c12e2010100000001f85264d11a747bdba77d411e5e4a3d35e3aeb5843b34a95234a2121ac65496bd000000006b483045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6012103093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c2190ffffffff02ec3f8a2a010000001976a91470dcef2a22575d7a8f0779fb1d6cdd48135bd22788ac3116491d000000001976a91471348f7780e955a2a60eba17ecc4c826ebc23a9888ac0000000018f6cad8e305200028c0e03e3299010a001220bd9654c61a12a23452a9343b84b5aee3353d4a5e1e417da7db7b741ad16452f81800226b483045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6012103093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c219028ffffffff0f3a480a05012a8a3fec10001a1976a91470dcef2a22575d7a8f0779fb1d6cdd48135bd22788ac2222586b7963425831796b565858733932704169365a51775a50457265396b5348484b483a470a041d49163110011a1976a91471348f7780e955a2a60eba17ecc4c826ebc23a9888ac2222586d31523974684b426d32455a4b5a657658736d4d5834445677515175546f685a754001" + testTxPacked1 = "0a20ed732a404cdfd4e0475a7a016200b7eef191f2c9de0ffdef8a20091c0499299c12e2010100000001f85264d11a747bdba77d411e5e4a3d35e3aeb5843b34a95234a2121ac65496bd000000006b483045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6012103093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c2190ffffffff02ec3f8a2a010000001976a91470dcef2a22575d7a8f0779fb1d6cdd48135bd22788ac3116491d000000001976a91471348f7780e955a2a60eba17ecc4c826ebc23a9888ac0000000018f6cad8e30528c0e03e3295011220bd9654c61a12a23452a9343b84b5aee3353d4a5e1e417da7db7b741ad16452f8226b483045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6012103093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c219028ffffffff0f3a460a05012a8a3fec1a1976a91470dcef2a22575d7a8f0779fb1d6cdd48135bd22788ac2222586b7963425831796b565858733932704169365a51775a50457265396b5348484b483a470a041d49163110011a1976a91471348f7780e955a2a60eba17ecc4c826ebc23a9888ac2222586d31523974684b426d32455a4b5a657658736d4d5834445677515175546f685a754001" testTx2 = bchain.Tx{ Blocktime: 1551246710, @@ -195,7 +195,7 @@ var ( }, } - testTxPacked2 = "0a2071d6975e3b79b52baf26c3269896a34f3bedfb04561c692ffa31f64dada1f9c412b50103000500010000000000000000000000000000000000000000000000000000000000000000ffffffff170340b00f1291af3c09542bc8349901000000002f4e614effffffff024181f809000000001976a9146a341485a9444b35dc9cb90d24e7483de7d37e0088ac3581f809000000001976a9140d1156f6026bf975ea3553b03fb534d0959c294c88ac0000000026010040b00f00000000000000000000000000000000000000000000000000000000000000000018f6cad8e305200028c0e03e32380a2e30333430623030663132393161663363303935343262633833343939303130303030303030303266346536313465180028ffffffff0f3a470a0409f8814110001a1976a9146a341485a9444b35dc9cb90d24e7483de7d37e0088ac2222586b4e507242534a7472485a5576557162334a46346735724d4233757a614a66454c3a470a0409f8813510011a1976a9140d1156f6026bf975ea3553b03fb534d0959c294c88ac222258627377505868634c716d35414e35677763545479695547535032596e6457776b394003" + testTxPacked2 = "0a2071d6975e3b79b52baf26c3269896a34f3bedfb04561c692ffa31f64dada1f9c412b50103000500010000000000000000000000000000000000000000000000000000000000000000ffffffff170340b00f1291af3c09542bc8349901000000002f4e614effffffff024181f809000000001976a9146a341485a9444b35dc9cb90d24e7483de7d37e0088ac3581f809000000001976a9140d1156f6026bf975ea3553b03fb534d0959c294c88ac0000000026010040b00f00000000000000000000000000000000000000000000000000000000000000000018f6cad8e30528c0e03e32360a2e3033343062303066313239316166336330393534326263383334393930313030303030303030326634653631346528ffffffff0f3a450a0409f881411a1976a9146a341485a9444b35dc9cb90d24e7483de7d37e0088ac2222586b4e507242534a7472485a5576557162334a46346735724d4233757a614a66454c3a470a0409f8813510011a1976a9140d1156f6026bf975ea3553b03fb534d0959c294c88ac222258627377505868634c716d35414e35677763545479695547535032596e6457776b394003" ) func TestBaseParser_ParseTxFromJson(t *testing.T) { diff --git a/bchain/coins/deeponion/deeponionparser_test.go b/bchain/coins/deeponion/deeponionparser_test.go index cb1497922a..2dbb494a11 100644 --- a/bchain/coins/deeponion/deeponionparser_test.go +++ b/bchain/coins/deeponion/deeponionparser_test.go @@ -75,7 +75,7 @@ func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) { var ( testTx1 bchain.Tx - testTxPacked1 = "0a206ba18524d81af732d0226ffdb63d2bcdc0d58a35ac97b5ad731057932d324e1412b401010000001134415d0114caae2bf9a7808aee0798e6245a347405d46c8131dbf55cbbbc689bbee367e902000000484730440220280f3fa80b4e93834fe0a8d9884105310eaa8d36d77b9aff113b6c498138e5bb02204578409f0a14fa1950ea4951314fd495fd503b42a6325efb5c139a6c8253912401ffffffff0200000000000000000005f22f5904000000232102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07ac000000001891e884ea05200028b88a5432760a001220e967e3be9b68bcbb5cf5db31816cd40574345a24e69807ee8a80a7f92baeca14180222484730440220280f3fa80b4e93834fe0a8d9884105310eaa8d36d77b9aff113b6c498138e5bb02204578409f0a14fa1950ea4951314fd495fd503b42a6325efb5c139a6c825391240128ffffffff0f3a0210003a520a0504583af7fb10011a232102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07ac2222446d343835624e4a6169474a6d4556746832426e5a345931796763756644736934454001" + testTxPacked1 = "0a206ba18524d81af732d0226ffdb63d2bcdc0d58a35ac97b5ad731057932d324e1412b401010000001134415d0114caae2bf9a7808aee0798e6245a347405d46c8131dbf55cbbbc689bbee367e902000000484730440220280f3fa80b4e93834fe0a8d9884105310eaa8d36d77b9aff113b6c498138e5bb02204578409f0a14fa1950ea4951314fd495fd503b42a6325efb5c139a6c8253912401ffffffff0200000000000000000005f22f5904000000232102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07ac000000001891e884ea0528b88a5432741220e967e3be9b68bcbb5cf5db31816cd40574345a24e69807ee8a80a7f92baeca14180222484730440220280f3fa80b4e93834fe0a8d9884105310eaa8d36d77b9aff113b6c498138e5bb02204578409f0a14fa1950ea4951314fd495fd503b42a6325efb5c139a6c825391240128ffffffff0f3a003a520a0504583af7fb10011a232102bdb95d89f07e3a29305f3c8de86ec211ed77b7e15cf314c85c532a6b71c2ce07ac2222446d343835624e4a6169474a6d4556746832426e5a345931796763756644736934454001" ) func init() { diff --git a/bchain/coins/digibyte/digibyteparser.go b/bchain/coins/digibyte/digibyteparser.go index 386796d5d9..705fee114f 100644 --- a/bchain/coins/digibyte/digibyteparser.go +++ b/bchain/coins/digibyte/digibyteparser.go @@ -39,7 +39,9 @@ type DigiByteParser struct { // NewDigiByteParser returns new DigiByteParser instance func NewDigiByteParser(params *chaincfg.Params, c *btc.Configuration) *DigiByteParser { - return &DigiByteParser{BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c)} + p := &DigiByteParser{BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c)} + p.VSizeSupport = true + return p } // GetChainParams contains network parameters for the main DigiByte network diff --git a/bchain/coins/digibyte/digibyteparser_test.go b/bchain/coins/digibyte/digibyteparser_test.go index 8f96165fdc..6ffe10cb00 100644 --- a/bchain/coins/digibyte/digibyteparser_test.go +++ b/bchain/coins/digibyte/digibyteparser_test.go @@ -90,6 +90,7 @@ func init() { Blocktime: 1532239774, Txid: "0dcf2530419b9ef525a69f6a15e4d699be1dc9a4ac643c9581b6c57acf25eabf", LockTime: 7000000, + VSize: 226, Version: 1, Vin: []bchain.Vin{ { diff --git a/bchain/coins/divi/diviparser_test.go b/bchain/coins/divi/diviparser_test.go index 5ad6eaccd4..1f95e025ba 100755 --- a/bchain/coins/divi/diviparser_test.go +++ b/bchain/coins/divi/diviparser_test.go @@ -105,11 +105,11 @@ func Test_GetAddressesFromAddrDesc(t *testing.T) { var ( // Mint transaction testTx1 bchain.Tx - testTxPacked1 = "0a20f7a5324866ba18058ab032196f34458d19f7ec5a4ac284670c3ef07bfa724644124201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0603de3d060101ffffffff010000000000000000000000000018aefd9ce905200028defb1832160a0c303364653364303630313031180028ffffffff0f3a0210004000" + testTxPacked1 = "0a20f7a5324866ba18058ab032196f34458d19f7ec5a4ac284670c3ef07bfa724644124201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0603de3d060101ffffffff010000000000000000000000000018aefd9ce90528defb1832140a0c30336465336430363031303128ffffffff0f3a00" // Normal transaction testTx2 bchain.Tx - testTxPacked2 = "0a20eace41778a2940ff423b72a42033990eb5d6092810734a5806da6f3e5b34086412ea010100000001084b029489e1cddf726080c447c8a2b1d4bbe43024db31b8b19bc07585db9555010000006a473044022017422b9e3414d6233fa75f9eb7778469bebbb40686b0f7eb77d90a04c80149610220411f1063086fe205ea821ceb0de89e8158e202aba00f5ebb92b51f97381311fd012102ccb10a2f0603a0624b8708abefb5f4700631fc131c5de38b51e0359e2ffa7d1cffffffff03000000000000000000f260de1a580100001976a9145b1d583a4c270f2f14be77b298f0a9c6df97471388ac009ca6920c0000001976a914cb1196fb1b98d04b0cb8d2ffde3c2de3eb83d9fe88ac0000000018aefd9ce905200028defb183298010a0012205595db8575c09bb1b831db2430e4bbd4b1a2c847c4806072dfcde18994024b081801226a473044022017422b9e3414d6233fa75f9eb7778469bebbb40686b0f7eb77d90a04c80149610220411f1063086fe205ea821ceb0de89e8158e202aba00f5ebb92b51f97381311fd012102ccb10a2f0603a0624b8708abefb5f4700631fc131c5de38b51e0359e2ffa7d1c28ffffffff0f3a0210003a490a0601581ade60f210011a1976a9145b1d583a4c270f2f14be77b298f0a9c6df97471388ac222244445373426368576956667650566e364c6470316e4c376b344c3737635344714d373a480a050c92a69c0010021a1976a914cb1196fb1b98d04b0cb8d2ffde3c2de3eb83d9fe88ac2222445065706e4d6b614e484b436136635169376f425468726469464577535359467a764000" + testTxPacked2 = "0a20eace41778a2940ff423b72a42033990eb5d6092810734a5806da6f3e5b34086412ea010100000001084b029489e1cddf726080c447c8a2b1d4bbe43024db31b8b19bc07585db9555010000006a473044022017422b9e3414d6233fa75f9eb7778469bebbb40686b0f7eb77d90a04c80149610220411f1063086fe205ea821ceb0de89e8158e202aba00f5ebb92b51f97381311fd012102ccb10a2f0603a0624b8708abefb5f4700631fc131c5de38b51e0359e2ffa7d1cffffffff03000000000000000000f260de1a580100001976a9145b1d583a4c270f2f14be77b298f0a9c6df97471388ac009ca6920c0000001976a914cb1196fb1b98d04b0cb8d2ffde3c2de3eb83d9fe88ac0000000018aefd9ce90528defb1832960112205595db8575c09bb1b831db2430e4bbd4b1a2c847c4806072dfcde18994024b081801226a473044022017422b9e3414d6233fa75f9eb7778469bebbb40686b0f7eb77d90a04c80149610220411f1063086fe205ea821ceb0de89e8158e202aba00f5ebb92b51f97381311fd012102ccb10a2f0603a0624b8708abefb5f4700631fc131c5de38b51e0359e2ffa7d1c28ffffffff0f3a003a490a0601581ade60f210011a1976a9145b1d583a4c270f2f14be77b298f0a9c6df97471388ac222244445373426368576956667650566e364c6470316e4c376b344c3737635344714d373a480a050c92a69c0010021a1976a914cb1196fb1b98d04b0cb8d2ffde3c2de3eb83d9fe88ac2222445065706e4d6b614e484b436136635169376f425468726469464577535359467a76" ) func init() { diff --git a/bchain/coins/firo/testdata/packedtxs.hex b/bchain/coins/firo/testdata/packedtxs.hex index 6b42f4ae80..11acad5433 100644 --- a/bchain/coins/firo/testdata/packedtxs.hex +++ b/bchain/coins/firo/testdata/packedtxs.hex @@ -1,6 +1,6 @@ -0a209d9e759dd970d86df9e105a7d4f671543bc16a03b6c5d2b48895f2a00aa7dd2312ce0201000000011687b1470de50d78794fdd86d7d903345f4209497235da14a03646b0662d3a46010000006a47304402205b7d9c9aae790b69017651e10134735928df3b4a4a2feacc9568eb4fa133ed5902203f21a399385ce29dd79831ea34aa535612aa4314c5bd0b002bbbc9bcd2de1436012102b8d462740c99032a00083ac7028879acec244849e54ad0a04ea87f632f54b1d2feffffff0200e1f5050000000086c10280004c80f767f3ee79953c67a7ed386dcccf1243619eb4bbbe414a3982dd94a83c1b69ac52d6ab3b653a3e05c4e4516c8dfe1e58ada40461bc5835a4a0d0387a51c29ac11b72ae25bbcdef745f50ad08f08b3e9bc2c31a35444398a490e65ac090e9f341f1abdebe47e57e8237ac25d098e951b4164a35caea29f30acb50b12e4425df2880faf633000000001976a914c963f917c7f23cb4243e079db33107571b87690588ac6885010018b2dfbadb0520e88a0628a28d063298010a001220463a2d66b04636a014da35724909425f3403d9d786dd4f79780de50d47b187161801226a47304402205b7d9c9aae790b69017651e10134735928df3b4a4a2feacc9568eb4fa133ed5902203f21a399385ce29dd79831ea34aa535612aa4314c5bd0b002bbbc9bcd2de1436012102b8d462740c99032a00083ac7028879acec244849e54ad0a04ea87f632f54b1d228feffffff0f3a91010a0405f5e10010001a8601c10280004c80f767f3ee79953c67a7ed386dcccf1243619eb4bbbe414a3982dd94a83c1b69ac52d6ab3b653a3e05c4e4516c8dfe1e58ada40461bc5835a4a0d0387a51c29ac11b72ae25bbcdef745f50ad08f08b3e9bc2c31a35444398a490e65ac090e9f341f1abdebe47e57e8237ac25d098e951b4164a35caea29f30acb50b12e4425df283a470a0433f6fa8010011a1976a914c963f917c7f23cb4243e079db33107571b87690588ac2222614b354b4b693871714462737063584666446a78385542474d6f75685962595a56704000 +0a209d9e759dd970d86df9e105a7d4f671543bc16a03b6c5d2b48895f2a00aa7dd2312ce0201000000011687b1470de50d78794fdd86d7d903345f4209497235da14a03646b0662d3a46010000006a47304402205b7d9c9aae790b69017651e10134735928df3b4a4a2feacc9568eb4fa133ed5902203f21a399385ce29dd79831ea34aa535612aa4314c5bd0b002bbbc9bcd2de1436012102b8d462740c99032a00083ac7028879acec244849e54ad0a04ea87f632f54b1d2feffffff0200e1f5050000000086c10280004c80f767f3ee79953c67a7ed386dcccf1243619eb4bbbe414a3982dd94a83c1b69ac52d6ab3b653a3e05c4e4516c8dfe1e58ada40461bc5835a4a0d0387a51c29ac11b72ae25bbcdef745f50ad08f08b3e9bc2c31a35444398a490e65ac090e9f341f1abdebe47e57e8237ac25d098e951b4164a35caea29f30acb50b12e4425df2880faf633000000001976a914c963f917c7f23cb4243e079db33107571b87690588ac6885010018b2dfbadb0520e88a0628a28d063296011220463a2d66b04636a014da35724909425f3403d9d786dd4f79780de50d47b187161801226a47304402205b7d9c9aae790b69017651e10134735928df3b4a4a2feacc9568eb4fa133ed5902203f21a399385ce29dd79831ea34aa535612aa4314c5bd0b002bbbc9bcd2de1436012102b8d462740c99032a00083ac7028879acec244849e54ad0a04ea87f632f54b1d228feffffff0f3a8f010a0405f5e1001a8601c10280004c80f767f3ee79953c67a7ed386dcccf1243619eb4bbbe414a3982dd94a83c1b69ac52d6ab3b653a3e05c4e4516c8dfe1e58ada40461bc5835a4a0d0387a51c29ac11b72ae25bbcdef745f50ad08f08b3e9bc2c31a35444398a490e65ac090e9f341f1abdebe47e57e8237ac25d098e951b4164a35caea29f30acb50b12e4425df283a470a0433f6fa8010011a1976a914c963f917c7f23cb4243e079db33107571b87690588ac2222614b354b4b693871714462737063584666446a78385542474d6f75685962595a5670 01000000010000000000000000000000000000000000000000000000000000000000000000fffffffffdb65cc202b25c3200000046551190596d29fb87ee282c1e2204bee5aeb7a1b1c1c28f1d507ca1b5d4f4a351f4af3663d653f8b1061fc77b2b7f72c168414574007b360b3c59f2dddc39519ec1ab30bf290181d1dcd37f4a1e35a24d64937a05be7efbba8c418fe877092be132ec83c77c4098f059ddf947e1aec7e64022acc17bf8cfced88d37da3cb2b2e0105c555a26e42f89f842b219d60ef390a8e998967adf46f06900dd42059810b56112cb23660ed591f4de1eea034fe181a6b1a8285e35212cbc3e0c3f29a138ff6aae9c91ea7abf4e20ce2dd27d7182696963ba53fa57d1eaceafbef2cc814d0b17b19b560a48cfee21fd69025902c23b8ea9fab931a60cf041c09418560020d47a746358826da947e16206a1d35d9879a9d785988bf300a1ee6641d12fea79a3991102d6d8f9b628e5402b0c357de333f9d752df7288ae0e8a60ab910694ee28a04889c52ab6eabc8b890c93fd8129d211357013ead3a8603be4843460cb25856936078045b5b07d1e2570fc2d0f45341827642c3a725a86e07352b2b8f52748e2be7adcfadde26eb9508a93fc5305551b9fda4fa819c1256d868c9b01857bc3a5ef1db57b6351557a53c1409425343abc40754cd121920eb99c92c711c730d838a129b801b2b152ff3b940c83c70addee716160951503eba21720f9859454cab7785cd7f25ecf3846cca6e6c92dd993268c268a3cd1f3d3c3818687f50f5423e658ebb7afdf3f6de96baf2e61b344103c2d16f20e31873d30b38e4a19856a8f510f98e74b819de5f2d208ede4bb3066e8a91d71f4a68f5901755a5faaf54a68316a09fd835f495018f2455f01b6470f8be72360d18baec83e89ed5064a87dd0cee41f57d09f87eecc3dc012f4d2d316544126959484d625a7922f288e1699a5b5b672c44cfaf1ceefd0b4683b1e7a62e9a33bf32412f1a49f1f8a0570dcfee53b9db948e35b9cd545e74e0d024ceb04bf726fe3c323ce002683447beb33788180dcad0a15569e968f185b907b24f0a91a00a237d92a5c2be6d752b27e06fe7238987cf7ee3ed0415a1cd0cc69b8eb586fd6f7b83e01692d9d28b59b9c98c231eb38165d42e62c10cbe4246bfba35cac79f0e002fda3b06941f4ebadba9109d81355ca6d9b0ec463ab4f41542b9cdacbc3c7303b66e5ce54fdb33f1a4e12d069a3154df189ce2f7340d95433de251da4ddf967e000fd69022b80e7bd4378a9be93d9558d63c8b2829c80e9ba75e4603bdcd45a9e100db330dd8017a00cf3d317c770b6d6dcb05cb2cace0e296ce2e8a96b71b0b6ea48be0e2e81cb66e76713a5877020a98acea1230eed97bf80b519b5dca15f724dfc754fd3150d2056ff113c9ffca161e13603f0acdb311614a44a47a2178f46a2017e73fba20d07a1da0a9792080875aafae252a7047154ad590aa34242cc5a76c2bb97c6e1f464d65abb5be84c64589496449f08d066267af9bd40ac5b7b55160f1d2f9933ceec99b3b5a4915776c7d1f5dc2d0226c0742e0c5376bc116aa571cbb692fe53e7bd9c05aa8160d8476d40f5208abf58bae2508bdc5e52ec25fb3a037d17a162646bcf82b6c2dd8560ed86c9a67668a8ade7cce1540d7742400e05d091058fd60396dbd0ac83b54134d64f76303f022da8765a67bd00a0d178a1e97dcf747551decbae17c89c2db17de96220a82f5364504ce7114794de930a35648fbcaeabaf06a329e8e0c3c87f2cae56134acdee0d86b3941d7846e6bbe424e89d8cff510057143547dff7c06ad7326d5bed5de75ec34b3163c3c58a96cca18afe399cef35341d588ff9c15c0c8f5a5a63727ee52311e3f28e3536292ddceb48018b6035113cbb3e838c668b2725f12978e5ab9d8f808dc64ccc0ca48a02c2344e8be8689740c60cd58159e45592c55da593f5f52b1d370a5d6fc364f03fc0ac094f528a67503cbb6fe49513db62596080b728be309f4ada27ead0923de2e89ff8ccea5a00c74f7d106928214e2feeb4ca2bc475cbf3bd7b3458f4d10db64c9abc350e244922519f2d13ddcbeea3f3b2e366eeb00d9d989142faf860823fb5fac1a3e0a72a102c69bfe4ff00fd68023299eb15b9c2892d691c8f439064db72f10d485fb32bc10bedf746bdd83e33f6a56978f66b0f89427a84ffb3f2521841d75a1ef262fbad0547a76deea1151a71b9a39f0d1c8df6c0fa6a66136daafe0b4a205f84df8edb19db8cc069aad6605178c7dd49e9e1af87de1b1ede3fd1ceea73f973ece91ad8ced139754cca4cffa5597bb9fab5fab3d836ee0e04c1ba1077500cf49543bbe5c986a8194b9cb5be63721c4d597c7082d456b23a20ad036c21f416b970a344305217f455925db751f52b0559bd986dd35192f639ee698c9468ba338a7e46ac9e50368eb86e5666af8431e7ae273e14d8202a557d93e3a93cbc1261a4bb13898c9fb15ceb3211f6f7d7adaa30b4baa6c4fea881b84c43f4ee2b9a9111a55fd502fefd95501dedffebebe4fca78fff7c6dd70e90adb7b8f2f611344791968aa3a0bfa06bc759721c622c8f2a4a67851c2acdd586952b84e287f086f60540934d05faf5a267f4ba3f6c17eb15c5fe6f302094247dc9c3d1d42a0017ac8e97400361c94f01c398ad4c9c3f88e21268203e3b52086d796a7147dd039329859e618f7054ca899219485c31bbf460a1b359df1c3a025bff338a365f33f48f71763647e48cc24472edb962d435afd64f394ddab6c6f64e6f54a3568f38ae45ce599fba9314f121eb1c6b8ad3e5964557a058186829a12002b2a9220a1ab55ff478562cb333ef6bb69d4ed4dffd9ebf39ca15f5eecde297afbfd7061e17eda335cf7212389abf1fc13053298cbfd6aa6402a323d5051947347e9fba76b059206a916a4ee84ff1f48c98d9be5ace61a2fef441c44587bae69770f69567ee8f52cd91adcc76250951be53462207cf27746c225e13c2164663cb0ace257902fd5815b878e4f19ff10499acd3700828a051f8c1ec33d421135089001547dc1df5cf9a43da6877472c6496ae65ec1e7b91bc3494769a03cfc6e350c588de0045bf26d0b418e08ffdae019bfb19f510e0e530d66f8173b13826b1281575a5aa703bb86cef598a99b9546e1a241fe86acc5a8f7156542fba23ff41c1db9267708f44dbce1f75465a7befa3e135393b1d5faae4f7d90c480656b0f012d1a66a03c76a58754b22e42f234de46e7f4f05192dc734f497d7d9a1989d657fd1bdb4e2379e4f576c5ee72be808dba602fd3501319e81fe1211176143ac5d9b76a06951a6a0413db2f4ae33d0f7d9a216fe8a5c5828c5af6778cae6464dea07262b1e64f18db9daf24fae038494836e7f96f8056a42f5966ac53f1e3bd7e2a39f129ded3d223908e64e020b7df2fdc275b993ac951921549d0b1cfe6464e8a3600f21714108f5c1aacdeaffd3416e28db6321b761f973ed338e95b559ae9ff6cfcd65e62d5e92b72cb244dda8ab5babaea6b992d7dc5ddb8bcfd189b2f564de4b57e03016f578c3d0adf004232f2f2ee155af2d6d0224799732c61513f10a51405be7b07ccce65f99f0eac9e3ae73a2782e34226508fee3c4effda657412c2bfeae4e4f2b63037db545bb7353b69654dab3f5da6e05e6c801828301e705eed65de092fc7081807643d9d3a84c2c0f00e460e4a7803f8fbc60c1803783f2a2c378e07531ce57bbb700fd3401139803deba8b83a31f7a90a52292c7b44d8c854a7dcdb835a2ee349fd4034792c0e62fe57a845f2927a74f363bf8f01a8a34266c8c3901c32b69f954e08e08e455f19775d92ee0114ead8da754f4403db89cdbf7e2a26d5560b060cfcfca049fc0b4b6a284f3c8b2ca99b0a53e1fbfffe5375cdb81242e758eb5fe13482030b78cf85d1dceb18833fd999d7f2b99a59961c12b8cd5e7cf8b0aa0212334023a28dd3a1211961fc7b7d8583a35d3a89b591e085eb2c63a111dd5ed4fa7b940733658a17e4ebdfb86a9132803d71a9a8b999fd9084a309214eaa5d12c6ade1d5afecf98cdb590d5d67ad79523ab29343643f9d6fe45afb34db61d0d7575f3fa21eac819d3663c5c868b32c0b5fee74ca11dc907de348029cc4f8b9db1008defc55f5f2f7f161d8249f5a5c4e7b643526f176d901a50fd3501be7ca3cbab1bfafd3e532d3cff08a4e43615ccfe9b5c75d661abb778188b62340f9a2f91c7b4e8f921f94fd023695364ce23a1a128cf630a36e69460c732cf514bb3a6512b23878d36505dae42b2680fb5bd293883938fc4964ce807d00a3d5b5bd93eb5328ba05c4ece7a62a6ce579ea0301c8cb04f359d93a68f4752de9641463fa9ae07d1b8ea2c21015539f5687be2977116e4ee99b1230ced94c52486e6ae38badebf88859df164e18ea343305d7153ebf5c6bb8fbbebf3c47cd23411961558edf12b57bf180819412bcc84fbc999fea2535efb01563c48313f12f3f42d3757c5da59e90948878b64f868be2604f8bccc4d103868ad3c9c346049a2c66c590067b890993f7de9b8b229cbe55b7d9c0d3716bb51c53188175fc7bc04bf4b744774ad7dce79d5bd21e4a4c294f8201c1c081602fd3501a925334ef2e47c0890a6a542f8321eef345b2cfd931a0c48c0296b20c1a22f741c3d7a133756ca24ca1455567fb99b6b6da19593a4dcdab7304b5963850e3b79442602217a64245cac37b1aea73afe494057b545324279d70041fe2977232b8a04ec926664ea4c10feb022da5e3ce3ec5a8725192c3d795a614dc479aa0c099f19d13bc97a30cf1ddb36182834deeb42e89b65a6b76cd00b934bd4bacbc9d7aeb0f544059f612d1c8837ebcfc2491fc5e9f1ae8a4b9f08d9877801b8f18c28da4bbcbbaeb8362fb18f6bec531557cdc5231f6ebd4fc73f97eaaeea338c62796b05e0b84b12c8c8de7b0444edd0420c2e5dfe1e6fc5a0c93b7e0ab7f005ae536e9b30a93679b9c5425aced70c1d60ac61d47705744e88b90697694a6b6f32a5eee6b60c4f96d0cfedb03ad96b8172aae6441e01c100a491037d637954ace3da0f416b9364be62df441262e33883df3ba56e9b6f665dbda14a45434e22edc692e0ef977f3d1f902084a3342833ac2ce396859131b64f0cd73bb1be3c22c99fc91dc3ffe07862cae7a34c4384d68d4f729b1b174d55b13e03dfa1fab5af8081d61291da97fd2a00762ae441ee631e242852bc20f5ed8b62a6e4725d977c66b16ebf4daa6511f7070e31b4446339c44d0a90dca22fb29085f2e02884fdd40110ab9262959ff2a85438df9126d869e3d4f7b85044344d4067c7af01979ffcb5598ff17cac8d6b588d9f82d87b8f144bd16149d9277ef00a79fa4d80ea97e7f7e7143246addf1e15e576789c0ad716c44f244d46a02110d413d456f8eb53da3d36589cf777172c14c5d3d56cb7d61471c0a6b22a6dd9f5928fa018ef0577c8dfd5cc5509da86e2a62cab87b5e757e0fbfde1cdf19edccc2d78636ae3ebacf75dbb1121c52ed86dda072db87ddfdabbcaf9b39fdf1fdc072af586e1a091fe00befb4572fac4c8fb4f9ff5f85c13f66f238f4f287c2e8e852729a1aab11188a942d8db8bb8e6483062c8e75166584e8ae11b6685026f8145951f6ac8ca9df676ce965c2f226e5d6c2cb482fd067f50030495d5826cf24d36516ca9894ad2303eda071956582eb6a60e6dbee56d472ec998b3dd3c5d08cf73ced73a7750c2936e23836f36e68544a3b7e02fc576de20e0a76fdb1c13fa6f4090bf91ace61373ccd5e573ee262daed75739f435121df7778313542421441c131cee9cc671fad72b2d1bd5748e6aed813e80f75ed6497522f75f1351ca859a922d1c122fcbd532c82d2a4853a1fb2ec698113421b5d6fc9dd429408c90051f8fab28f03cd7a86c61aefb1b1a833676a33df8ec52b3f697189db992758dfd580115f27596d43332bb625f4cfd5bd5e5545238aa31cc9b706d921f4d8b9184573b9249e3aa6d1d182d86c9a6de8f9b26b71d76d67cdd3638f2c48ade2b47dd60a95d119992c232a14ef05e053601c2a178647da59ad43eb5a4be732e1b8792d8a1d7d9259629ad7f882120b8f4f6984ab464183796bf5980d05bf32d85f61421ca4ff3dfd9c94c5dd3b1b33a0e3b113ab1dda8b2e6fe0daf32f72164a940c9dbbd9db8d460ea919e3f8338257f77ef3e884eb3254b5f60a92e0913d741acf9c173e92e3c0da33af70020649c004845c03018531c5394b3a53668b81eb539981c310270a3c7c4ec25567955eba73d9c37af67abab999f2bce0e14e19e835bda0cc7f5c58851fc4079f704ff8575d44e161f954e835e39ad1c5f9e2a414f890fbbdbfd1a50a1c73fd72ac36e4c2668ffbec8311c76a94340edca158d1acc2c0ea90042149a5b5d198081833bc3f1309fbb7cdf34de6e5dea2b04452f18f8714095ea9c9ab37aa003337a5c5c44a315d77ac8f7e35983106ac5ccee6c21534b87fcc7969e25caf720a6eb4b63cce609aaeec0dc0592340efb93ab426320bc035cfd5901f2ddb66c64b1198d80e619cc73ce127e86ddc9df078d3c71671333c7dad2f0089c65e83070efb0161a3014706337436131cc54e43f0e3484bf24661897bdfc34e64af6d49328f763c164c39e9041cdd3ddf43b1178869d9e4cdebd8e1592acd581a5402f3482c6ae63b34246592a35e9e220055f93c06f704b6484fb7f1b2eb0cc5e587cfa4d4dee683c3d412f4593873ba2191a218d5aadad29d7bea522307be7979158ab102f3e04329846f02793b775c271e7ab66c1d8582e53a2496a438188fde722c48e7f6bb6e91000b05c1553407622bfa2a9fb146dc169b163130baf7802ecbdf0bd059f32bd1a4549fefc9a3a03a99449c9cdbbd45206244fbd9792a69036e8eea32d82ac89694b65887a48308314c0efbf408c689d119ad46ed237c74c322407cc8d499c49bc454dd090802ffc33eff180ca0b3968b39e0df7f8b259cbe95b754ada17686e1530b0a702bca93b1ca42529d68000fd58013c59ca9ff207c4a2d57122e6c374b0c8125176b534bff226a91d7bbea935a07f8602c06eea81ed5ed388524c7a3fbe0dd4c850687652dae368a48bc8ce91711ced188b7da9a7ef1e7d8b96145b39faf8b2e95376cbd173bdeda632b792296dff0df80d4cb3e30fba1960cffb3492159938e0b61a632966284666f50223e3cd14bfb4cc1e95a707677d0ec770751860411b7fe90f4e2c078c11298ba2010c7410594b9de7e6fbe80aea2cb76f8be0c0572defb9d58cceb06dc1c84e197f867452e6a502bb7e0c18d5b1ec9004315563750ccefca4fb65aa1a51aa32773d6519281b7bf6ba826be6f5403b549c3e3646ddff159376c534fcc1e7e339af2ade2e992949d6f2d6362e1c26c70e60ae9669a3a73702afe1c06684794e75966612e9d99cbc7db18acb4a3f37baa1ede7bc419cf655499dac0d126ac3ba833e4aa4822c7bf2c49ed8d94b28055168f4ac738c042b6f21b4dd779539fdd4013688d933c2502cdaed2b4360fcef5c8173ef2c1f5a91604850ec2c81e706d1a2b0c87154380186b812304dcaa7363afe5cb6a52ed235690d746f1a070445fe4ab9a18df19f0d1e87b1a2e9bff724f6c77e2cbaac74a7694366f16620cf4a1d73e3fac311750c406c3fe6c5df4fa5d996d92673571550d694b47b69383e6251171010e3ac21f01f12fe2c764374a3457f34e83ec0c9e87f182f84bf72f1595714c8825a720545a865f223cc3863cb5631c8224bbbf3e082b2c07da33a0b180acb89db94127dbe3c060ef10a8b32298c153aafb1870464eba5414846330f5f274bb6b87e4a2613549853578b7024a249351fc54079737859c559ee066d6186ef6a06a94c19318ae8fd119998b8b8fba2990970a73ace570ae0dfd6a4976c7e240bc1224a410289793d0a97a71b6c60143b2f0163c69cdae4c7dacc707eec9d2de6820b47a6a900aec39f0157e729eece517ce5d1079f88811c6bd1647d32b1375eadd5bcd5b8ef6e9e05b79f4e9fb2497c2d0b1e886ef68b298af6421a7b527357a3cf8a10963d5503a0ed1355ad8e003abd987fb9fe9e26d919ffece2fd1f00fc87188e2a1fd0cfd122c58fab58ba37a61312c68f641908df7043b1b65fe52707eedce969a8a8dd245eb4694e9d01673b1e441d81609b0a91c4ae4f779c7b1838386632fcb1f1dc90d74a3920741c4c0c3ed4ca4b61a0b12195bc5e16f7ea637a38e63f52d0aeb3e4865d1650a2cebe2c14c5a4c2a155975755d0cdd2e65f9ea0dcbde187cad3a88544e0d9b4a4900a590d5a44ab0121ae1f4ac2eb65b5eda140899d5fa527deb95ca4176769f96a68ad3c506723860b0146eaa4360b738ceaf67292a88f4c15f5c91183fab11fa57427a87ccfb1b4214b44c0d2c6d9668e4abe6e4c43934934eb5c621d5b097508411896b343eab7a5acab87607386f907608f6bd3de45fa08183e01037f339cb3905fa8bdd791b8e7d9ee54fc2e424a1537f63e48ad2420d219b14c7025e7d32c0292867d30c023d3900e6aad9c768826c86467b1ebc2ef86774427eb433785f7b5d05db05b056195824d3e40bc2785e40250206fb1680814835100fe5a77ba4cc5816a80b1edd12ee960fc9fc898cc6051d625206d1663c4aad291b5a8b6f9aab95a0e60e9f12f3693f46958ef0fc5ec460d4a5121469a59ebc1b20742c238592976434be70e9406aa2900d31d637dc65fd2de61a80021c54f7dcf90aba4912a73a20038a951127348621ff65add2a75feea07162e63b10021ae0dc0278bcbb2968e8f6f2fa99216a614adcd38433b32b5481ff35082e6f19f002060b1d489bb9b3ee9f5670890d8bf329bdb906955ca9c9b1e23190c4af9b9251320f59505121fcf1a53150766b2b65e55e2b36cc7fc61da94746b17a9b7f97df86e2076dcbe98ccffeac440de898fafa058b7501b07691431b6d32ada652102d55b2820974a8dbc563de8510d65da16fdf79575b59fd2a490177a7f5bd63ff03d48a554201a31ebd30e8c013223a76725afe3d50caa5e1025925a4c03d19dffb17f5d175320e1bf7f439ea8079322a86024e1253cd71604d458c67e09929fe89394402d165020be0d25d6e004b1f86d249a8b4b9e06b5619d165c2057aec4c4bee1a0ee4eb240217beb32c9e29f2dee1bab88aa620d7ed7a7dae80d04f03c1c17ca78e1a9c803b70020b7b27036b274dd398eacccf27a1f8d67fdb3bba2819c5ef0aa94b7c3995464a220487edd3892385c68e0765cf86ac7379a6ba506c3d687615dfd1664a61e0df10620f6e44766be42266c3202569865c8341a8b4a9445769ba336cfacd7b8141f9a9a21f1e28f7a220f0caf78a9ce7a4524d87fb1a8cdccfe6dec364d94ebbba6dd93b2002115b207a64913e0303ec3915a67279f85002410dc25184f06a03b9177f3134695002022c96e73a8fbe87b7755f8f2181f91b5d5348bc861fd6ab35ee71b4ddc5d8a1f210837a3775e5e598150999a4706ec22526e8321f73f7e78d0693595aead84128900219538d13c754a2ac0f1ebbc737d7bd3a4468b7e91636f10bbd980d8253ba5f3a70021182188329afd23ba2916e46880a016b493538ee3ffc4438488fcf6a36d78e48900205add8ddabdab1dbbefb5c2439ff789e158197076ab6b8d99ab37ec4d23e0151f20c876fb7a9976e7b0e8b4fa2d40a26a1f88b5203a992c71f86c863f64409a6c9420ba46a5a64b38094cce0e477fcf526a371f81d98758305173ff85e5af9e9d713520a29a3995c535d10d2de254ce8dc6fdb52a0e6965d5faeec07548aa6b43a91159217f1341316ff39e8dfaffd537063c130f3dd19770d2b911eb407f1c05b42e398e0020d2b3667ad2def5e59fc37b22e196fbda8d2c41b886be1f3cbef4ba78e7fb1b18201d0e660c8294d3550ea90d2e976f0263209275ba6e277ccbec9daba6d361286c2028c77b1955f5cefdec1e35cc2e9121d07651200e90184d7cf32f40dc73432c41217769836ae0d553d95a53b045352d122ac2c489cfb66a172346a3de53801ca99e002094543d995a9f86fb5f49c78fa23d0868faeb3bcca002fd7604fbf81f38c44a712137ffe7281b281b17aa5419276642b8e69eb1b1eabe30ebbefebb022c21f268a300208092e548789ae3e160dbbcc8ad981f80804d9e485003a6c688fdecaeb277b500202d5b57d5d18194fea324bb7c742151f84f9fa7fdb69fac77ed936a56c80cdb5520015264325a4159703b2d38af540c0e680ae700f3b9bf3c069a80696bd322a95521ac7f435a2907331d8dc15dd9dc945807e3ee5ab5295bd574483300431612edbc00209836c6b63d43ee695f135717c85358663d39944bab412134cfd66db5762c9a442145dbfb1f0d944e7f7b6d4b3648659b3b12a4a2c53bd72f9f65e7198957db9f8a0021f8fa17536fd1f70702678ded21a1c7035ca8f088961c04af7c7a4a5df96f0ea60020b7b386e088b3bbb85f1840ff606079ac9ea7f9d0beb62f5c7c5a924913df2c4a20a977ef35ba6c8f89af4b16d5903a1f0d005982c2826797c6fddd0cd2bca1b94c205c0b1932340551606bc9e2602bbfaf633de59ad8fcfe19c4050dac8c664937312028b3e3013ab25c7815169231b9b724e8ae2ca3bdb5fd17487d1fa39046cb77482053b98d7674de0cbba37c37751a7adfbc9c0cbf1b40752921a7d91b08e584fe35205372487e10cc1f1e2d524bb76bc4422d97602f7893c62d28ddae4fc9a896d0372067c5cd6065fa02b76a852744f9cf0b97d32a14ae4cafc94a52087f726693e13921963c3293684a500a48267f5579e77eda8f877d15e4911936d0f8e74b4d38d98700208be980fa8c412eedc13df4b6231e3d2b564296825f490db1e2eac607a355113720397b07219a89c803defc3fc3ac5fc258c8b54b39f53184ee13242feb50a0c62420223706f83565ebce2acd2c18f4cfa79edaf67508da1d2472bfe325e5f20cde47213dee7fcac92a23e8c1c1325e6f086d1c8cd27e47535899399c6e1e4f8784f0bb002014a117fbf97976c0c7af3a56308a4dd19abf6f6a7afb4238e5cd2b41ff3d8b5321bd2e9d0964bab0a1e554eb0a1b350928f2810c4fcdab5ab4e875005cb4a9e69700200e9fee09a4bce859bb38e62a7c74941cb0376d118f1738f06b8a517fb618ec7a20f90381d08f1fa4eca24bccd2e979d0f28710375da371378f74f991439ae08132200b7166584e050832e699ec020e5ae55f07fe8ae4ba7c2c399ef302fb1abf064320eaf6573fcce33c66ea0aab58eef64a3efc1f637b738ee51a95b162eaa9cc476a20e6540cd1e230afdb93aebba474c269c423facf47f2bd500e08961f7c0a4af55320a8fe890159adee60472ed604e73b725c36b2e0a1dc9dc94138a95ab43b38152920d80414b480db1b23a83530d76b6ba4768b612856f328c5d1f481c392bd69f670205cbf3bf512e6647b24098affecb63045ba48ee161913cbea137d89f8c2317e18213ca2d715f1dbf2f7d1cd1843584cee3c6cb663830c2566d2375a8b7d4306a7b3002067451e9fa32a3f67f8940b3d5ed7356e532ab64588a30bc64e68bf0f1754eb6921aab661ffb9e2489a080b5dadf8b66a01b4da585f1d60fb19803d7870aab59f94002009a42d8c17bc201a7683473c104361db25afd272558b431c7205c1ad60e5275720b5259493fe51d34e9e9f13cd027324de99208f62fc7088503d065bbd22eb671e20c2a1ce148baeb48bc4074806162c5081bbc4636a01d2947e2e511a8e23f05010217f78c01cf3b1de88c2b5efe4f1a44da7b7ad1d70de3a9ee75de52c21f5d9dda10021b1e53880cf898cc3304af3330d0dc20424ccefb35751124b925e132d89ffbb840021ddada2ffe00b2b281c447b8d03562bdfaad7248bfd3b82ac74178258c17f629700209f39211210bbb04910304087e2907c3a8a12ed4142aaa866b6916b3f17d1c3232113567eae2f96882409da14e61072dc3941a7592b816b25b3d52f3ccf8ba5499500217e8262ee95708b1b40ea9644b6307ff3886fa0159a3e28d6155e3c4f737e2a9000208a5f96711ed5da026ea1c3e40ec96a8c5860e871ca599c9ea740e3ceaab2480720cb25ebb06c94ad22dc7d529f3296366d4f65781e165de8cab751d4fe464da97220269dcf06c230675b405eec3bd7b1e99d9191242bb0d8f089d31f5d41d61e4768214635add2924737b775e5c252b8ec10a1d072ac4ab941d8745ace8db5ca7c72a4002025a5b0916a1b7e739c6926915cbdba2f4fa3b8b2728a7030cca4946362e3e84d20c5b5d7283e047fd80f2281445463424ac2a6f1aaf2053bbc3c136254bbade21850801b49c2a7eeee02072a84d52810a6e308b5b895f082b83827d566722f46f9dcadcc7437e6a5df1f12cbb56bf34473a0bbd93b18b130a8a3b98a08ff3212094ecf6309aab5bb96fc39e51df0828b70ed423b3ea325d175f412bea1f96c89ae4459987ad12891d24e968ddfa4f4c00e2fd4ee2d08d2c0e6ad48129c32fa7bc99c3681e3f7996a1b93387a10520949c62c2c64a0ec1889c5eb5c1313291a78dd7213244c21eb9a9da1b77c9ea77880305bccd24ffbbad2883c52dc411485b64a291bcc1440f9eba8277d0d8db1ebc00f874f52b126e99fa1d1ea5174c2556085a46a0223466cdbc23a9e217afdd1de8a60be75e11faeda6091a37299745789b6ade6800081d69088cb8d7bb502ead5f1955391de9d7fdb577fb2da28195a81f6902612316ac9f15ae160b2977310cee6660ecdac2fe9f801f9188635c83ae12a89e3aaab5ac05d3b988fb6854f17faa24d0dd9d29d79489ce3d453903f951a6c83bd4c5874482dc6b0e4883ffa65e4a955c45f7fe7ef32f5ec034595c8216cbc62393ea19900818b43280d3245ce70caa22225803eb986dc3353c37d798f84761ef12a56e00ac6dcfb4350a8e6f108b0f10a1975d0e47508730903e94a2ee8d9f36561d1fd2802bcc103367e15e325eec1cb09c86f40d632e9bbde8b2f6006b4981fed1772729c17d1cf3859e4cdefe9246ff6f6285450b520180f04665c25527cfc85da4596bf00804399c22b05bd36cc68e8e7b5c2625bc34806eed211d86887cd37742f1108acf1f06278eb9028eee4673e0cadd2a5e1f5f257422afb0fcc199e65728ccd12fe689ba03b50dde3957bb674b01baad178efa863bcd10de5235f3fbac3062933488e9b4a60b2cb716c5c2a9648aeba59eb3e50ae3be842336355c36231630a918900fd0001c22157003bf3613cb4e60bc0842ef72d03c3927ace3e35f79e7975e6d93593c1727ece0f9734776e3fd8354869dd0c2e36d992e493524f97875a5798e45ad8800d288ff3c5ed1c656298547b3f386690d20d323daa40d684b557ffdc2fd64c2f3f71938ffad426211d4e0fa1ab71bf2eab2095a61868ad51bc622506f95d2186870b9fd55fadcab4734a96bb996948339408559f1ab3d0793b6ff3830c22dcf8387590bfee93005b5baf5890bf9e3c925d40906e714205aeddb42376eda4f4ac7d96bf9a74546ca377bece79b690d870a560c3b1c4416b06bcfba6904392ba19214fe91184b7545019fb8a5c65e0a6919720dd962c91f98992177eeaec4665b6fd000152c7953810a6139a35d9ab44951eacb6d7f88b6a2d0fd1a05cf109d8f9b8092d1e970d6ef12cbfd2f8f901baae01d8830b8cd521e63300bbc1bc623fa5c0e48017333a631d42b0e71d1508e7b8dcc53fb304d4480e2a4e440c9e53204482c72d97b4d8561306d64030846c9027bf218567d607c4a2304df183036f1861fed60942ba64961824b80fd8a828499888f80a11cf91ad2fe187aae73605bff8a4b004a2738d56a5abf11f9b82f8ddd501443545bb4aeb49fe39b64c7a768380892c6f00f8cfb49f4594e1c88ceec1125a3b70e890150dace647307c1cfe715642756d5d2f6c28218274ca5668a3c2a4af4b79e70af8b83de56337b841c94dcef0c89ffd000109c0d936c59e7b389dd374956c37ab4b8978cf0aa5b7050dc50510f381eabac6fa2e91934b72e798eae1f6f43be168ce1ef600e7b9bd1bbc2c2c963cc1c777c41ea9ce7cb85dbb140bb01cd90bef6298783d8c6c056955eb83b7b9df63ba4b9cb4201cfc83897e54e269398be9aea1e293fe7131f92d22b1fe8ecaa22cd934980f4f0e1b8a91dbfbec640010d91623780a2e7647391eadd5a10bedc3efbbdbf189c33057605f1cfee70d8ced664531535abace6d63bcbcd12774d461c91e4c836a9534b35a735f211cfa324d74febc41fc9ea3e6e953ef555deb6ad348e35ef3be21e32d2546006d43765fb7275d10c47618d109fe806a4f94fc67940ee02aabfd00017585f2e215c1912e88aad895e87882da714c625143b5f1a9ecb9764ef1e1a1e654c08c70a2208e371f9c4b2aca734bca273072eb9cf5621c73ed442efc85624c10b0564c96f488cd5ed697fb7ea414c8f49f5668c4b41227cd57df071e004675cfe16914c9e3e018ac7e0b720b9cb9496f2d0e176ab2d611ede6e80ef5803566bf698e09b80a81c0eebe58ecda39093f0c1651fff5aff860c4b2e70460bb95da3a74cae7e26139d1b257ed9aae65dd4d86e240f07ea77f1691be722bf9855ffa759afac8a1e6c91326da71a1120092a914507c2000a167966a74c8e5fa8533078be90087d59fa75405168d72126667458525b6406849bc1bc9a97db49a37d084fd000154e8d56136b6dab9d5fadb3065668dab000eda7d2a8c47b342ee5c95281fa8e2fcebcad5a0943f2ddbc46390eac974b4b27ab9da4fb3747917c22305d3ad91b5694b312dfeb392b55df60cc8d4f6950bfbf4d5dbccee860d9997d2de34bba2335733909110bb273c2e36c15315fb79a93d1bffe33c358e2da4c238e8ae734fda09936a758f0713f720bc556381e41f76c29b7a02bd44926d5b2a7d818c788315c253a90a03b9194fbd581603e03a34bb298d8a6f4021b887ce813f3cccc17a2a7f6bdc5b50a681723890250c4e9050694c9a66fc587187973f209c1962bbc5bc7ff64fed7d6a171981b814a80a1cf3123a8dc622008cfac1baebce0dfbe52ab080209b50f0445fcf9488fe4818e96d8556331f20c211c60e07f1f80e3ab23103281a07c5df8d85c6fa1767aa997ab2dc3bbbf1533ffa8729bc02f6ed3d9ec12441576ea311a1e30af774c92f70f5d4521b1a67d0b7c1571c45e23785e70bcbbae1da98f8e2eeccbc67ec771a30b68e37f8a385820bfdfc7af405bd5375df20557cfd000167f18545407c8f34edfe760a91ca58479b4caaa3964af57568e4fe511cdc94e99919ac76e43c423dd4024457896c2367cb62da0ebe7d8b98cf79981256d870421ef6adafa61bdd61a9fa752a3102bdbe90ec1f9ea1402c855c2a78c5c09ee8a4297dd815aba0b346eb3be92a04301c33c83b0d02ea26a4eebbfba0b71667354bd8e6c825eda303b05207062b3b909397026f469a3dba5dbb851bd28500322b2b898efba194e9c89a97e378691c6c3587f7fe4bf1a3c69d31fb9195ea9ad626406f33bb39e8083452035038c1714d2753ffd79f62643057bff804d7693e014a80a9e32d1db6e9219e55ae5d59ca7f9615b252132a559ae8f0ea9bb70947170fd3806fe1ed3b59b8df259900cb793dd2f745658cb2cae325e988ae4259bf3674b40d952737b874531487ad58a38fd6d01435d4e14a87b0fef4ba40a2c985cc62ea2d3b6c97453c8bfc61ded2026a403939615b94db3ed5388adf92480ebe647d9c209541b9ab97a67f8afa8ba2ddc4f6621eac975806f7a2935a4754ca1281407254fd0001ca584567228a05aff314a6bb8db5d77c64cdc98049e4fc4e8a0dd02e94d65e494a83fd0573bf26071fdfce8455a8586bedcc9ec3912fb93c28c97c76fd2bd8cf7c77eb032f1b3d5f18cacb4d6e46d1d636e5423de333171621ac4ddf00cd140c8a31cfe6e1720b702f5977426ba0f341c5c121fa41e5f9cf72c676d7d8840760047baeef41a85ee0f58650fffa0dfcf4a354b4fd635f65d533afdf68682c062fa1ef3ed0345e0e6a4a03b2dd3fb6c1918fd4c6ea2e88efc1223bf72d33a12ec9f10212abd8e0d323fefe127edc909daf018a59e7be84b92ad9506be6cc080fdaccba9d0e6153e49ebe546afa2c3a5fd37294b035eaeb5a46ffb1020a5fe683b0810df474b799476566c1a4287bbd112cf2fcedd1be2cdb8707c55db9af086106f66a061f2f39e2ea3bfd1cbc18dcc049d9011336b2bcc240f731b3f45955e15d228656e7d41424ed16096607d48dfe0e2456d877645b5ea8f006b797958aad495cf7d57408484038b87ec99653b7fdc5b8a1ebf47b7883218bb9cd52eb8a22e49300fd0001310d818741b56832a1a31e3aecde85d578e6bef95e0d3321278f243dcf81ec7e2e6780e1bd4de223b5835f57184ea2c2edab2b870fb13f620b3124f2fc83740c26aa30b917680eb4a61a3d1e455928a6325ca2c330a74f35c659dab9219fc1ad2dc4fac28a8055bbc1acf272e294b21d1c3083c105b107e9ddd14314926a5067dfad3ea37c54ed50a5ac96391dea5fb553fa689d4166b8547b0af7764d22b31deceb9d8b25bd2edda13de0b952e8c062504896af885bd026edb9708bdb23617f0fc68a726432ea1c929262c82bb2be5f1536c6f88d33b308f8c929560caaf74b8fe5f840706e3e0b81bee0e46cdb134867bf8b11655fa204759bdc88d492eeb18093dce3035bac48a26fee7f6f5ac66adf63876ef22300572f528d5f482480e951befca94ed142ce71d311a3000d7895f2d9f688edd34ca44a68a09cfc4b685ef9f5e8a6d75e956e99a6bd01bdc002c94cf87861518df3a5a0713d7ac072254ac1d68de90d6a521348969bc2d59fbbcdef6918c045701421c92ef733b3b7c4a3678026ef6ecbb093dd60690ab9b7d28280a81598793788de0272eb52423c3b5335c844fae3a69374757a3b41e3cb2250e36d5e185eb2e67950782ecc1d31398965fe54f680c52b1806bd764fa2926377fa6f7f909ade7774b8a91a65049bb6862048d389c3536be88e1800ce95c0ef3477fcb5317ac511b78dde2dee12fe305773188132574bb60a5e68118e2373411b35dd42ca882b7b833c4efc20f1f3bab6cc6ff7036d48b2051bae2ac95dda94cc330ec1d0e3e09f856c7e36c44020b01a5076268aa5ac517cb4c9e936f958b7ac6f8fd67e961e083487b2befc7f923c559f6c52309b677fb090a604a6e9454c2461b2fa1574403fc2438fdaa1318c606707c0c600fd000124940c5f2606ccd649a9988afb6775e60891f95b91773924d7017af430cafcbd0484929b049c7a8372852bb695ce1748bdfbc150a5ca6a1519c06e5982c990e6b22f509a606337a647d9d1643522264b5838390e716cc8bd4f47bb8a0de23577b998855752c434efe432595f63529bda7c7164b321304afaa6a4adb71dc05c25f5e5294b69b21c75a13edec9f8c0a31243aa73ce6592f1bbc84c4705daef99acba57280dc92de02e17f1b28473f200b3e4a8e577312e51f1f79c06ea49f9f1a27eef83ed0749d5eb6534f9d8ce773e94f21407cd17154c644d8099b4edbeebf4401601d3e3667c32186ae79c69abb3c72c0e8220b2ab9304d1307a686c9db992808823b2b219c9f81d5a641e40be3eb71e841db1e43d571d3b225b5d811e9a0101b37891b8a962be19c7b127961ac447a847de4782680d3ced69df0c4f032ddf36d9f7da47aebba193b703598c12c2214dd41953a8fd4c2956d261c989d560d09809e6471d71c5ccefb171e1b84b806e1ebb792b40fba818c40a8ccbd07ccd5301fd00011813eadc77aec30750c84dd27a1dd089ec245bb82d93aed9f343f7cacd9cd49a22a4b516df334c6981cf57d9038700ef0eb610a70bc71dfb1f4d74ae3359835b67090bcf46549a2f8eb5e9d9573d6f2900efa6164528cb2298d488b7ba8df39748f6fe41ae04028fe3e171c68cf7954b228e0e54f266f447d9a93ae944517fbc95d02e898ee7f3619a02abed25e78cdfdfd3ee09521a5a1067790117d5641cde06554e7aff909ad7f7d8f67dbf9fe0ebd1f75856dd0face6d53b10230d2f605b1c1b022376c2f569d9849bf094f7b47e5c1aa5f88d3cba904de9fc2299ce60672c59b6b951ec15809a78e2beb4b64db2768a44d253da8268ba4d6b1517b03ba980ea5c2231b957018bfcb1dcecf26ef89a5338976732dbdb6f7354da85b62d1f0064a9c99ffbc36228487ecd45f4d605bb3a2f0113b756f61bd2d5bd7a75019489fb9b4807f90c78004233c53031f7013ff3fbfe9f37cbd657c61e071dc1e48a5c15f5b1cde2ceae555497228b19d2be443ef59e89067504c76df6197e899aa833fd00016741248c9db871fe238a8fd2a153a21d659c82caa6d0d5597a900979d10862ef7bbb9643b8423a704cdfc787bad8b06f693e0279e399125db92681391a88cd1f8a3b9d620fa3d29071d4b540fdda7d24886beff41e9624955bef1eebb27505487c6b650f941c5487db2d6a9de360fac13754bf6bdcacf8f5162e78e1808c2021468b402d2de932a590a22371ae513e4f8385cc3d54d6d8112d30b5053dee2767bd5d68b1cef07c5dbe79be7a13b4dc761b303625a35ffd50cd1a7a7607c34f76b737cad0a77c991efcc0f48ec0baea05350643839073c4d912d7f6d18ef80f1c42320c5a1949abf9500e5e027f84dd326ddc25b796e885f878be522c987a47a1fd0001892880727994f3ef4cc7bc3b009d3ab0ba12e6fbec400d978a7b094fcbd63826e5a2dbbdddb37042f9d488f82e0f6d64bcf88d327aea5615c6445c13424544d45e12007f26b62408c19eba388bcb27a32b549f048cdf1a9df32817a926ab34e130848792f71cb81f0dc000f5b640972a5d1180b3876e2c170e3ef31e27610e5db6cf50077970504de9b6354284bf12106151876524752dc34024d7c353c8618c1a0f54b958edeb421d5d521470bc1edabdd5106b7f89c1a5d52b36c7491d76d6d52c153e17e692a1ad389a57564aaa352fabff8b65dd9b18b6e76feaece6bd76f09a88d60cc344d1865aa7b97dcd9b7ee5d869943a0aca6189289cbdfd9464cdfd0001ba441f650b1c2d89d985d28731ee44502b189fa4ea4e283e9ccc8f5aa2026a3b761d9c83d2823ac20f780b887d2487f900c5f568f2059f44c2014b08d7886be7d6654dd8c760d82a2f4bc80e06760211321638b0c676bfd4254fccb74b497e866d33885345ef0a990aaf150fc2d36ec3a7a2b21b3e10356b6d7ee5984273f34f295ad3c9ba5ec4af588da45a4b512587181a89d3cf11cccbecaa9a590396b63f27ac3e157a08df9aa19867a7729910a02ef994441cb0a733d957c5e41351f8784776b45246159733c6818c817f4b7f219a68c13dd02b0410b037135025fd5a07f320f44a21926c5c243636bbc3a0f437294bc019a8bc8e9e14795ea712ded2d28092f38d55599ff2c0dd2650de43a589a498fba4d1920cf9557aeef01575efb7139b8cf10b6ea5ab3ef9b40d4a90977ab89c55a5af3fbf0f8a72197abc38dc6d6df406cc7531260a8d5e36d3ec1ddb95486596b45977c1559892fe96ee1ec87d54083c8e88fb75590898be9bb956ad1593009b68285fcd60e29a392130fcdecf3680c4f08fc6aed56784e471ad4dd0d0146e0fd41c4e17d1e660daa6fd01634cc52a48fc71402242ad1b9a1a42f254b433769f44f895ec40119b40a9f731e07d5b5a08b65c2ab225a933a92889e4da908332a35de27bcf88db00ae7f7d4fc3270b4fbe1cddd3934864c12b77d6f109704e9c0835742abcc29dde4bcead8b0c0a1a08fd00019cd781470ffc9915bbffff9b297207280aedf02ae6ce1972e2ee4f5959aa022fe22098b388eb542ca03a1af83a0f526eeafc95b192c2695eb74d1e55f6c61a950402deadb11bab08257124b0ee26ee87e87570aa7c615eed73c12862708012e2ad8775444fda2687455a0c2e79d9c87e2c8b6eabcc3622c1bdf14f94747086ef33aeafb3b282b1cfbfe7e20bd20e57a00cce137c764a07a8f25e96cadf0ac678eb79938cc592b38b299d77d3d2dccf1bc3ad3da77d15bade71085176833fccb4f4d813b43fedfea06496c732f0ff2898fb0a13ccd272a51da2c7e2c92c0b47106deb290f12f1927efd87500483efc37b35bcad9aaac18b7676d4356e9080cead811cd4046723cc636a017890c78502f131ed1c4f2bb1c67f5a3095a1f39362b5b1d769e202127eefb4c36b280265946cb8519af11524abbd4d0d35b83d9516983b7053f3c5a583f7616ffdb271612030cb06448ce5aaa264ffa9dab04c64cb246fb188fd20ab61d2e39695d47564fb8485e003a517b2f6267c9147da7051ed4ec6008140c144235a6b9076e23b97a81e347c8a367d9c12e0d775a378337eb3b78647fee80b99107d47307ae73c15dd22a4210f98a5e4b7ff6ca8ea79286ee5acded439f8633ce730ee68947fb12a323854ae232ce75fdfa99d274926b14f81e93279f5ef42cf7abaf5e4db9dab5e1ae0442e9a4816d9df5fd1c671ffc2ff9886c50ca400807db6e16ac5cb6fabb094429a97f7ae57639537b30b12f634196798cde9c00a85fef093cd983ad3f4d9fa8cc2168a8331f07fbac52c2544defd008d5d31905a6b4f57e2b786c091b019dd4a23167a457f2adef68fdd71a4989921698a451c323faa2870b78f555c3c30a56319393d5ba640ee9b0c43a2daa29c5c080b4dabf229814d08f0c3c7e21b9fa04c76c7d6c3f12509c060015fe82ffff8eaed0caf49974478bd49b94e1f710945a0c233577808d97d435f09f9193b1e7abe8aeacd1f6f142c9eec20d7427cb919262b8a81372af0523fd6c219b427477da7715f7f07d48f890b751206819b8693faf2c8f6b1cd42735ec457051022d063446c58e9e23a940081d24e2fc309cf1390ca944179e6dbcd7cb4e39832f3c8cdec876f8d964cddf3c27f87802eafa33b2ef393e59fdf9028f1add7988eee4140257fd5b420273d9bef73715569b0001ec2cbcc13e70f3be6f0dfae99b504ff2fda3a3bb4974ea056e1fa739eea46d38af1f2cf3ebdf32887e7ecbb570a3633c5e2425f29d24a5ef1cf00fd00016100ce85d6fdca9575406f04fbad2d9cb4d7f6fa1393be3c3d6fbd5eaf7a99a02f42b8c373bdd03f7c76a9de409f943519dabf438788e2d96b34b7743af43a0b01bf8a080615013519a4424a5ebc90cfaf822719f1fe59ae708b726307243bf7cc399be43050e8b9115ddc1cf4c2e0c4b2a6a9674b1b45584d7b4ca779eaac889dd720bc46bfda1ba2af747a8e53c2b3b857e1e5b607ea5fb45ecf8980250056134dcdb481bd915bab49031c6ad7e3faaa58e39952119d8729e317e15e864512f0bbcc6898a71cfa619fd5753a5b32bf98dead20f99284042a0a661297d468445d982d9159c44fa344aa2cde29454c7b08f3ff4671f23a4d062ec8508b67dda681c0fcd0346c255f430aea7066ea78b6571e8493274db67d253968f033f88c42a90f40a8298aa3db289f0e5ec038201892272b636d947f6ae9c6342e5f081db2b188a9d3f50b2e8fb396fe189ec082fa63fefdb33d11dc2771fa1fe04b23438cce2bfedac58a1c6b6819cb7b02fed3d74e8fcfb839df07bc474ccd8ee76cdd35bd0081ea358b44b3840116487e3f85d40ccef06d78c631423996e991df95018dba5db3cd0c639c4e76122db272e4a59cba263a26cf5877481b93714bf9d78b019e7493443c73c5af84cb6e9837b6d809da037a573a6b68cfd8bda5d02da0c50743e889afd72406eccbfb255f957ad00fc76a8c117d182363dc9e914dd3d6cad81e32bf00fd000133a5e5ee9bcd22e54c18d8ac925859144d32339b2bd00c32e8f98b754cdc3495143c425da51b3db805aa3884ea27c2ac815e4f5575491bfff800fb5a3c1fc0120fbc33d53ca941115350be844493da6e3dc40847fab916235bdb3409a356b9528278102b4d96e93c27c88a081bf0bfc4462ae6a2e340832ee0c76aab12f192f58b4fb5fe386cf566a762f83bfbb88d1859a10d08ec78b01536c3dcb69e9a441f9d2e947e898dda40f57fce5f0e5184d93935fbde32cdb750c51d57cf7c2be917fa299a01c41f2a1018d435380632735f9ad2e958cefc8837c21172bfe67a574db3d8223eba4d0327850bd5fff38459d4fb6e00715ba7ee5355605ea0de209ac7fd000112ffd4061db7a6cc8477b6145a93f3e6fba20a368be255f4e435dfa8c746ffeda600b87dc3e5fef1ffb6e917b09319853adea9701d795e244c4937d25e0cb0c62bf4f69168aa82ca022f6a28a7b85eb1e8eebbbaab30a61c785e19f59232d273d936e4ce4b9c62ebb3ee2b96e90a5a4ab633e9b3704fc0f50ecc5b9ad6f3843576aae92928ca7c8d09e3b87a6281328a1482bb642005cec7dd57f3ef9b1a167387511eb339412c109bb94b57c4e46e3f33d6fbbdeee42e60ebc9f44f7530b86a94bd9a9acb974c76782e954e295770716cd216b83036fee452fb2f83ef077d673f1126ed412c8d9df216b0cbc72456ec8e2932de9539cfd562ada45a389a4d8f808cc78aed67e86adc2cabd1bdc0b21969cb52b9b1f1a1d808d67d8e8bd478f293d9b81bdd65949f5ea0bcf48c6fd5995ec992a273f6e335e7e6969d008590a961240af5ae24e4410dbe1c6dbd001f77406731348a0dc4ce2f8a683e4fc7a49c659207ca2bd8fdcb31d39e58a8c75f76031d5b65d96f0f1af90da9306f019332558135064b7f64dbea34cd68c039d4dcb703f63a0a1effa946cd1c9bed59a956cf85b358f4db3118070265f8aad6c540b066f29852e005003666316066b324cb037b9b5fb6ab8b2908b1b5509e9e0ece6345cc571c84f83dd0efc128ff27a1b5dadf0a0921544e9490d13fc82df1939382f1b6170e8ca99b38362228da418dd219970081840ec70b20c096e1be5b162d058c5c6db0b672d4e555effed3ec8ca85dd69afaca09d85cc206d179994b6a0443ceb4e65071ac7a64842c8a6b2eb8f89519dac433829865747586af18ed1d8d864b75baed59daf5d08931cf47dd2c802545505c9ae8d351ae00efb35c5725f8d3cfd87c7792b084096af67c7f63bccced4a038d00fd00013449e73edb7c7fbb2df81482f1d22d02eab854f7e7a1362e9a41a95d0a8ab7bb68816dd19776e0cde728b6cca402b4271b384f71053bf501ec8cf40167b76661d11aca1ddf4c47421be72577dff8be5ca3461dc8cfffcd99fbd7accce7ebfe12ac6c4f8d265b1416464f2b94cb93b40957eab9e01bfaa4e33948fb2de3cb093db74e914c3f048eca8699735e3693752fd59dcf48cc92b63594b8595052ca405ac9191c6ad3cf08c6d92c384a8eb0b643b57e8b9f91ad94ba1c7f5d8aeff0baf1a905ae1d722af5d1d4da2e56694a7857f99c114eb63d6914b0ad466b6ff0970457730cf6ebc607b1064ab3e792833de818ce7f47fd212d98dedc8602d90a08b281fc8f5ebc335769ddebdcc8fdb0507d0d814fe95333b151b6518abc1221bb431aa83abaf371451e4ade47142b1c1159b372fc95380aa1697935bda8ac28ef6bab6c69d6871ed0242087f1e69f4ebc71066bac94040f79e5fba35c0bc9085546634d5b1fd7f5c85577fd7b645845ec87623eefaca134432ab7663dc7f5a66f55a80081cdcb09b132c40a0e33f8f9c47fa993a3d3c78f5b4d8b7c0ccd2e343cfd78819fb9d6556e3ad0acf67c85cf5cdd9335665761a091200ce34bd81172e0bc87efdac66ed1d4d849f9b1e94ed2db44601b8f07d85a173a6ed9a76ead0a21d48421a608e17baea8e9a6b319c0c41fc5917263f6c93208f9fad8ae2b2eea2f702a2fb60080f98b3379369444ffa8fc207955e7b01575c7007ed19ec7291f28b93db7aa7148bffb9f98f7e3ec1e1f21568ad37f91c4603d3f276ff0fa7e9ee8ade05c277775c3ca2440d50d427a23f7aba81b8b1b9bf3ea8fcddcc3c3a7708601688fda8a6f41d0cf7b5aa4a03422f332012ede8fa6a9d8093980f07b87092bc6e48d5ab343fd0001840e370e28d6cfc75778bf5612efae6c33b4dea0c4964e810fec77ef1875956edcd2e22139a485fc4515fe44c57149905efd72b16f4b367735c1e91727632507aeece411a472e4f270e0bde542aeab9961d4fd0898b821a6f193dd42391de664331e33b9244e0598669269c73125b21765f048e0c2b9d17aeb0cb3c112a318040ea4126c43e0f46d1d9c1304d95f35b875cc2fc3964970e3602cc51f2c496f108f904e2dcc8113223a9a074c344b42662c3fa22490db6ac63a1b9abf0dbc97feb0f4447eed2e96f33f854f695ce54e24b9ec180cddc752cbe66fd361d874873ea3733a4ad92870b24efbd22e928deecd7d4293b680843d75127c0eec3f07f894fd0001266e0fc8977335a776a66b44c25dde8ac3f40f2d195dd3845a0019b5062043d1e1f24adaaa3052565db20717cdbd769bc1cbad88c0fc6a205fa4acf472f954d161c3450cfa0c622b3dbdbb2813af60d47870299c1f3d793302c678a2d4cc7705ee51b675dea5955fd7ddf184cad643fea3f1a428b6d49da35ae251744cca95446811aa79d4edbb1399734c3b3d71c7ef455eb73f72ff013938ff65ffe3d8cc8ec321328775db8884cdbf9645cedfb4d86bd54cda89c7a4da2da5dda4d4f459518e058d3614a4a9475426c64a16bb206d2a02decb9e301ede48dd0247d36d5b9fa1e449f44f6a3ea7999f31259bc49ea13b62cddc0f877435901da6f9cde2e4ce816565e8a37e36ddc7eb0d1363f2d0641db79c0da0fc7346cbaada28e859cc7bddea3653151df18260a7609aef925c9de21299857732ca58631eafe768ec58752d63a48b65895e428bb4054b8166e61beb6e8127488941601c0ee1092f695ca0fa9d7b965eaaa4dfdbb9fb2127a75447d02f64bd3c4f4e285e781b02d98b21089000fd000118a3777a8b2c2a8e5e9f7fd85ac71b6e843ae5ac31d3e192b73c830a33eaacd7ed6f9d5aed4754b9b6af55e60dd31ced5d74d2910c2e9a500dd3fd8e282136337919b3e8faf81a96315f04588f7a86786b922c6ada489eb90bbb8b1dcc85e8f6c6d3d5fc561f4ee579e9143fa4726cbf168119fa5e2b1539327327f00ab363eb065aed240f5d120096951933dd3e689118d2262e01e5827c120f53f80dc8c7207f2b633aaa0ebd350e65882d7e9069190163975682eaeb8570c6c297b614a72ab6a6c3276f754a7fec8ef86c50ebec46bf88701ce0c3037db989247de5ee7aa731ca30af5bfe9357e3f0de64160b9ccdbb0eee9885478a6e9aab6902227933c8800ae488d64c37f7e8713e92a1ef4da540c5da96b55f67bcc7e5b3a0950e75c0e75edea568a951e213d8713c034245f6d6e307cde14b2d7160bb2ee6628d3ab486d2a0a9c760478b56003f84f6ec15c6ecc44ceabd1d1007cbb891c005f62fce138af30cc236c0e31e0d93be2f94b9f3a7ecbff058bce5fedd4bd294ef8bbaa0588002c6177026c1525bf82d44c5458f84a9105b3f6eda233d83608b024ab3aa3646a9baa480c983df90f90be078d68a80292b8a609180fa075f93e99590018c49e8eda3098caef604bbb643ea3e4a0ce82407a80a13e5f5c786746571e74883e7548d881a9ad516d0b7ae5018ef44a5a93e227057169302cab4666a35b3b22ac678fd0001df3435a26f04bd17de1cf54277d5e8b52d4bd552f2b27115e6c65a252007b889c3d5178dd15259d2216e0e3fdbe065b38b5f638fbd77ade95f13882aeddcf59d508f0252647a0d705ecada91727ca37541f25aa4061a9ca2e3e3dd2169ac00a508db5661b41cd1b626a5d64e9a093b3509d86c122264b53c5fc95d013c6b8d58a9faf515af46d5d41610ab99555c2c3ab363d604fa147b0f1bc86a3da26ad8a4614e6a27f84f58b02b698c232d6a6d864e49d1fc95aac2fca78e1483c53c6344a731ea31261a19bd5b7190d9ccb8ed161d963d4949d17bdb8edf19d1bbd0fa9311b2d5f2b3febc5e4dd6e3c6f2e169ac81a671aaad0723ff8e6b0228ce57ef8e81423a9b6290dbbabe3ab5e8cba4c4e6453766806e946f6261557c2b23c05e7aace181919a12ac324fb26f709ee0eca8b746eeead2b12c13f01c39d5b117bdcdd39fe102d66bccda50456e8994ef9924e22ce634ae801c9cf7ae24d72fd568379bb6650f77af9dbaeacdee02719fffc07ad660fc01b84dd88f35e6f97ee3c977b40080a08b918b6a42c1e2cbf0231135b5a666a371bce41d2b53ccb47dac374dd8a1b9d0469281570672916c4841692a836200f9d4dc3d69b71fbf6a51ab597c6b11ee6d772fdcf02350817e4d85f79437b5aa21ed0407fe9689128f9166bc391ed499357d91b262930834e96b1fa8505ebd15eaf85ff38db7ff5e9897c1ee9f5f883afd000161acbe21f4e6258c082bcca1879e68a20989c95cd5f7fcd1817b807d6819263f1d32cd7882282db7bc2a94b5ad3ff053198fb7d89b51c0df65493652932b4af07023ac9a84793269798b2850d1296aaacde7fe3eac56b88ea9f6656e4576b58ab9eb13dbffa731c6c4a57c2d06fdae1ca33e1fd07851afcc9d5c6021a1e0b3524c72bac8c9ebce52290043b3596e9bd0639220d164fb41017e08c632fa32c798c61c643af591b15148d585dba6baf61948e53d74a42afe3e45505fa249c6b6429cb40108f107983b50c8acf42c9822527413d866f3605812c3665263b0796e7a0c632464c131963eb1b39eb54b6d117fd25fede83fcfdd5bfa3edcee638196ae80213f7ede12505476e213d56a89224818d5764679824cfb61f0365494e5a4f26901aec701421c83145e75008a4472d66f06ebb3af51b886a2325ec482969d59a86dfadd0073596b1d47545699252a94475324ac07619c4b2664ee6f3b83dfe1c7ef6ece6a73f1040016d887adbe9d8e076de14815f8ad6bbf89d82b11c8de622d8094d122e7cc525f099280b1d3bf5557fae729b8b4287fa3f8a92623ac0cd62ab57a10f3fcab1493385a909c09e80ae7f7f0d8bc7459ee95930455412ddada18aa6bf8f8d98e5b7402605066cb4b6b5ff5cab305771cec6fd000032a289a8722d0a402afc66696798c6106be690d05ada3add2ce5947851c79dbfd323dfeb194718127e1ab87badfedea9cf54d4cccdbabf719b5d4af7f3697343b59974b5e263687aa60953df4a207784484d8867fef51eee561964b4b085ba35e822c22bf33e7fe10480f5a63666a5c654c350390521cc06b63b9b215ab1626ab9bbb8fccfd1076140cc362ef54ac515a2c924781db8d102e9b8b64149ebdd98bb102cbe31305ba0081c572ecc50846a7dd2e812f5965e3b12f571dc933b0bbc7492e8d4a593191e01ff9895d3807afded49d76ebc51e0b2b0072cf3baadea00568925088a1a6b1d5c659fda5a6a9af46329b067f5ef1f80b2b5ac6bce86c909f96801fae3f620b9435f49621a88f47da90ca9d7c8bb3d52a01897b92f78f60c64c9b7d9b2e7a1503a000fd00012328fc8b7f3b07c470ed2a03147afb9dc212ab82e70241794f999e711ecfdc51da0413b06167d6873a91a8335a5b2df0cf067681355bce28f19f5fe2a5c74dbc334d077ceca07c981b94c89f5b6aaa03b70fa4a2691be52d76461356320efb66d1d30a771132b6dba09c35de3ac8d46e9894a44a5e3757f17d2c218962af03aeec834380e2137479343535fd8fce9f21cdc55b3ba54a8830892a1afe865c39804811be558a668764eb6c206306132bbec65d21de4da92d9da947e28bf1fd72dbb246f3d5a32bdb7c2c4c6554c80513858b8b863f66f90df7b84299ab692b4f638beced0e40179b25ca27cadae375e6494298f1af1a9c6c8ca4c27012bd5a578dfd000100a3219c72f3226a3dddbd68ee35facb6b68f03dd8224e56e3f09fb6c1e8a5828471890b83eacb0acb587e1dcada3daa1bcd82e86588a3bc4258cad212f965ab7b7b67f1a201c3fb65fea00edb539524c439bd574a52795c55307dd5c69303e9acbdd82d227a866708e391d39f30a450876879e9e96e1166af40843d022a98b6fed4a3a75768ba478c8c51937b0feb9e714901bfb03625225c8f6079fefef5116d58da16284e292538496390666b292add9794937abb940b69efc3689de9a1d881128212f3da736d62a629ee1b5f0d0b9f14f68619352a190694a31d7cbead5c6e88e92882d58ea2b4b2d7fbd9a21abc42fa72b751c30f4d1dcd2b1d538a9ac580ad45380cf5586d1862e7b5cc1f5406e07ed7c9f8e2d60b51fe478c8cb1d0419eb74699935bda7fd39e1bdf588eada0a34c61b50952c015c3348b140b862124143c5a6bff8a95d55e96af5282d0d6e75de6b4d018cbe8c314e0e6ddc58b3a9769629e7b668119695c7454a83e476dcde3e823545911058b4a3c63e34d1045296dfd0001a2656e2e929437093f84eb1f5bb7128ec028b07421266121318b060f0de4cc8a979142cb6d18ea76af9e768249b046b79e6c134300c686c706266386af960f5139cf50208344458dedf8427753aa31c102e5a9c27fded58c1be68b6eeb1e7486bf7ae4089f189ab4b2f4cea1390749082ad37909c56bbd385a3d0ca67777c50c31b60a9055f4655ea679c3fe517e9c230a83beb74707d7d3237343972259908c0844814e73c838a8f476238a764c89fdb7dcb41ea693d81e4dd679f51cc7563e0b317336eadf517a3835e2d23ced4b898a4263a37ba4c40655240d10cdfbd608e59a960b3bad9f86945c4123ae65fee8cd0b11b8b3e4ce9186fe40a0d184a7b2814d798b5e305448d9efd326751ac2b5135e377165ba43f41dce4d20a469df4d161006f30ebdf5964229057ea556cc9c94da42385223d8d4a9a68c98452eb4eed7efc9ac9aec7e115e6f0ee4dd8a59f0c3af9025c27263a0493704548204fcc9d9100493fab6bef47752ed0c197c7efec06868d5be4c6a7085e44142d0681f9cc600fd00015fbdb2b09918819b56dda812b5213c0b2ad01cf668900c07402e80b28de1286dc3d073e305f0ab61ce03c3b5dc6661e8607985e0db2494ef615d15200cd441a409d6b579352cf40d8afcb386f2d2e6b193a1d4107ec1dc395c5fa542518356a5d2e60281396b45373a88655a899f0440964b6897e59bc6e4975ca80cfab2329bb4ab9e64d55146acbae756fa01de037ba67f1b32c1e3bbe3c897d442d91f2fcec723eb41ad81ce587f89362480642981290750dacf2d89ca70b317520c13d535438e7b3baab43f0f9470750b3bbae829dda95da000ecffe01a4c78d0c62c15218a88a31afc3ef3f8af914c5975cd9d279491c849265822ee1acc28384e5fe0bf8037b0d912da535403f5b695565c69b8b0354f5ef2a7984ede9b647416cdac8e9693dbb793a8dd01624515b5839d8c9df43185e5a534b02cbf5211a5a76901a5f290d235514f7ede4c384033c438aeb13e916b0b3808af47a069d98c7558bb411464d4f6f47b9a27b0c108ee0105accc00990c74dca25dc8670406c9515b6f381d819ee591e0d496a46333f86fee821a2a99c57f033ddf7f5a5ae2ccad12fd802e2466086269dcd30d5bf166d0e75e78ffdb17314b500eecced0eb89fca7b80e7b072c1995b83aed72d5b033c20b31eff559d64541d17a97875da9075fbc3f2ba535b2cef3b7162a395d150634cab96a316c7c53d4ea11314a33afe53001e41ee3c50081cbf279e81c999aeb762f1ac725354d6e4b1fb7c81630dbeff0b04745dc66616ae25a2454e0f0360bb18079c1c76ad458ae27b904e5b878056d0e66ff205eacbecf2abc3742639611e2b638500423dd50fbc25f9972c1e4e9c2d0e84cd048e6c7cb22b11b3a2ff56cea64b7bd061ecc395a4d56c24be981db2975c234be0b2f81008005b631efbb2fa8782f143b7de38c0de7fd23c704c474c76ae0d36e8995db201572252634de4493f1e88f9abe2635e004a84d27294256a6515b003869890d730d1b9201d75b3fcbdbe0732d4c14543d1be2ecfc827546b7be930d1028972715e99693f7daa4a8a240248d07912d9fdeb64ab0683ea51c2f4d3dcc6787bae732528012df83c70e6e3600f7e891987318ad29b9289ea6b2e2bd82ace318eaa84c2fd8a490b6d34bbe6a75655a09a29835a26a53cb859c9930793f1ec0e3b178c58bb860145c5291f5cbed4c3eb998ac512c603964527e1e5a0f5356ed48b3d0a73adec895df93a5ef9284231cfa621ff60b936bebf4aef2a744a0fb7d47ee5f43085e802f80d547703739c5c00e8dc4a72cf3995aab570f821f775544c3901f2dce970d1b4129e4c28b57e8ff266329e0a37b6931fde5e5c54922291fd305a948afbb18f9effd5d0f60a60d35979ea239b80e249ef70b39f36ee312a4167e0aec0aaf23bdfcd7c53d64249c6351a87f066f8f9845901b7f757a05e0a463133e4df6571981a2c26bd1cbcde2cd0690917112f976ecd8be98df9d99fe49f98a11b45f5eabacda1ab4451ef9cd3d1abde3ab3961a3ad111786470e3ae064b5d3b5a16576834cd64ecab08e6749aa502ff3b7057b7ff751c234ec9b08ea5e4e0b55604114ce837b6718e2ebdd73d1b4cdd2307ed0f1a6123f4e3433d3c3fac7f08a89975ee59d00fd00019612c87cccbc8ece8380fb088fe8852362b7702d24848e60213f33efe5ef71b6d76dd868998e2533cb85964221e103dad6049dcaea215a58610ea64eb88bcf4570a5cec9c88ceaf735f4d77d676dd6cd2abdc1c2a9d5e8d625927294a464fabc08ed5a769e640320af871b6c10c0b36ca09c398de5de8932120719d2e71c481536250d2eac410ab88c4970c68b996f15e4fcbd4090ff80f8677d55a8eb1274436ead2f15cd0f0a141d0706fcb3eab92392ccaaa36bd7f4eb816062bf3539a4b5483191667b1db7d13b5d043ff2c4a105cbf74a5f8450e16ef9e56c521865117cee88c49480177b5507d2916af69654cf760c5c5ec886878c27c2d3c8188072c3fd0001dacf4c63a866d5c2bd21ecd5441afaa3767666957dede948dec007df174f22cd53c05bc5cd2a36ca827fcb8446cbbed912759302cd005ee6a2c69454a3477db36bfac8c9cb9c1bfa913cf4c43f72ee57ad9a4c9c8f5551af8385d855226e2f18a55291dfcc1ae5d60b8a79d840cc539b926be1983c2f16067b104e2c63d8253646c89d31ab4c50f166105a04c19a10ff75cb467ee83717ed391c72464a7fb3772eecc36434bf946de0d03a4d4a5de6c4bcf8ef8643322167d73b424828c43a14dd635cd9db1313e4d4ce5254746fe90672606a0ae15ee5dd388732ccc0d3a4aa489480e6ddf7e6e0556d469166a96181cc439500ee7823f496be42c2950808a4810f484d78b6727e793fb2bd80a89e39bd21df6fb17584aeacb871ddc341cc6b7d10e0a476619f4a1380db2776c4a166acb9109a9a4e3e0d1c722b1cfefc135947ad866938137a8d8af919f4010e380f0d4bfd086059207e5a6155a66325355282a7453cc87c698fa58dc8c3575cd81649ba8ec17ac529ca74957e55b9f4bb56bf00815b9bd44f0df8280a75a1c07da606fbaaa180fd975cf0fb680960c3c3e6574f5db9ee72aca6974ab6cd40306bfbaef658a3613045af4e85edb81331919d3b9fbaf742fd7873c20c7ea1460abf68aeb1af6f3cd8efa6e8c072220c3c5aa34f650d1d946e400bc038be346c92d63a2160048aae8acfd9d14b707333ebe2264080b700fd0001d720e0212231342c3102b8025ed9be48f49d2e97999df5268fe485db4b86020262c62548456cea96a3aaae658ec4fd624269d96faa62933bf6da9f251553dc3085f61ef29fa80e09d651423fdb6feb6ec0fbd5a0e5cb370aec10f239d2858111df0053151f4697104af362d12224e069e0efe3d23f3580fa7dbdbbe2021882b7236e1cb6e589c30427f5afcb414b96b5223fef25d08efe1cc1b94727d05abd7b5fd828d8be4658e0f4467314f3623fbce8484fb58258fffbb0dcd775c78eca1ba6d0a125412db9915de16e6a7bfc80ce05108d8a1a29ffad260498f3d7d505a729b6d80f78cd7c84176c02fd37188433a8481b1ccd90239fd7e7041e2e9bacc880eb7e639dfa8733cb68a4cfabfbd894e153b67e939cdd425a17ef4cdb3c77b204dce1479bc7205b856461de37523dfbf9ea529fec53f78e737ae178bdfc1ccf9eebb12eb9fe4dba614698043db45384cc80ea339e16b8ddca87fb472f2a9ccb2b5152cf06306e3fa48824e5a3fd37e9552feeb586f426737baa0cd20b922235638096033eedc83083b149b489f7cc907ebbaf77e0a7145e5aabe02dc425aee3602ee5a8e33a0af6f84cf8acdc541c7a1626974b21a88088e9f42fa879852ecbdae5df0c7be841051f73c86d5e9af4296da7b84756afe65101ad68ce8150a4f44894c9c3883c7db6b9a8cd7080842e712d37180485ab416d4e4a229270337f4c68491f5457ec9b3b6d4ffdd935903f8e4c036db95e032911714af527430baf622582e1fd97f6581822672800bba9ea246ea960aa19bb71102cea154dc463f4a020561e2a40e835f9506e2c1bbd37b14bf169fee9e41d94d6481153999c6486b28361ac1db2896543a42d0e8d1feb8c9f038e3226b19daf72d454dfd6d9928beccc3b3132d1f4e996ca49f276000e9f48d9ef6726c487c0fb0ede75c69464278892803a74fa9ae56044cea55931ab6c214fe440522aa3770963ae910e8656eb8abe6d1ba0f0633644071a1cb7dc1111eeceaa16a2c3ddb66a555db4c412337dcb28895cee7cfc1dc8304a16fedf2f7de4fd1160d528087c2f55ea33a2c7feca95ca24dc71e1a6f0d978bf0d36bded077f5d55490ee150f2f83ed8009cbe76ecf7f922b047052e83da4706ffc1e2a4f19b3ce0f1c1ca1279bb60d8b94069cfb3fb5c328d63bd821e47866b6902a3c85a02cfe1be4664418a32eed422709c5a536648805b726b053da8269fa65e5945c3f0897566fff4a9aabb4df74fc48378fcaf9cc69d7cd820d0dd682d41af39663e3a12ceb63a4f5a875d2a1df3aa924270ae2d4640e53f8edaadb3f53a8a460dd5c7d3c5cb54cafba1911314d809091d593083dcf397a2e4b9258ced80169c0d7728b869be03b5882045a1afbb0275b1073bef509f2db72fe133df00fbb27ee2ef6ce6fd0e2608387175de108b1fb40b74746a337531375bee5563b9b2ee6b9d803be7bf52b7013f87bf4b7481641b2ab5479fe5bab4409858506ebd120c8350a53ac86e15c8c12485ab9f58c218c9e2f44a39633abcc333017635b19af3330dd4dc275e9848912363a85e7cb3e91ec588b171e936af4a63768edffd74fa05a2fa9e281cff6eb2d801b2fbb8e208dbabdffd387331b9239f53a80414cc223a6230ad96143e624f210d541de65584ebee32586c31be681dd2527d2a52576744ebeb91735f4ec6cb2f4bc8f94dc0f810fbab5e14304c370ea8fa4c208376712cfedf6dff2c4cea55968d3316d87cc68f0ea97eb59e79a5822b9e6e69020000000100f2052a010000001976a914b9e262e30df03e88ccea312652bc83ca7290c8fc88ac00000000 -0a2096ae951083651f141d1fb2719c76d47e5a3ad421b81905f679c0edb60f2de0ff12e20101000000015d29bd6aaefc76d42e3f23340324be0d235a35ff6ab80187be75f3c3d9cf8c44010000006b483045022100bdc6b51c114617e29e28390dc9b3ad95b833ca3d1f0429ba667c58a667f9124702204ca2ed362dd9ef723ddbdcf4185b47c28b127a36f46bc4717662be863309b3e601210387e7ff08b953e3736955408fc6ebcd8aa84a04cc4b45758ea29cc2cfe1820535feffffff02002465c7090000001976a91429bef7962c5c65a2f0f4f7d9ec791866c54f851688ac001194fb180000001976a914e2cee7b71c3a4637dbdfe613f19f4b4f2d070d7f88acf8ec010018f5fedae10520f8d90728fad9073299010a001220448ccfd9c3f375be8701b86aff355a230dbe240334233f2ed476fcae6abd295d1801226b483045022100bdc6b51c114617e29e28390dc9b3ad95b833ca3d1f0429ba667c58a667f9124702204ca2ed362dd9ef723ddbdcf4185b47c28b127a36f46bc4717662be863309b3e601210387e7ff08b953e3736955408fc6ebcd8aa84a04cc4b45758ea29cc2cfe182053528feffffff0f3a480a0509c765240010001a1976a91429bef7962c5c65a2f0f4f7d9ec791866c54f851688ac222261345843445137416e5248396f705a3468364c63473367376f635356325362426d533a480a0518fb94110010011a1976a914e2cee7b71c3a4637dbdfe613f19f4b4f2d070d7f88ac2222614d50694b484233453141475069386b4b4c6b6e78366a314c344a6e4b43476b4c774000 -0a20914ccbdb72f593e5def15978cf5891e1384a1b85e89374fc1c440c074c6dd28612b90201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1803a1860104dba36e5b082a00077c00000000052f6d70682f000000000740a9e7a6000000001976a91436e086acf6561a68ba64196e7b92b606d0b8516688ac002f6859000000001976a914381a5dd1a279e8e63e67cde39ecfa61a99dd2ba288ac00e1f505000000001976a9147d9ed014fc4e603fca7c2e3f9097fb7d0fb487fc88ac00e1f505000000001976a914bc7e5a5234db3ab82d74c396ad2b2af419b7517488ac00e1f505000000001976a914ff71b0c9c2a90c6164a50a2fb523eb54a8a6b55088ac00a3e111000000001976a9140654dd9b856f2ece1d56cb4ee5043cd9398d962c88ac00e1f505000000001976a9140b4bfb256ef4bfa360e3b9e66e53a0bd84d196bc88ac0000000018dbc7badb05200028a18d0632360a30303361313836303130346462613336653562303832613030303737633030303030303030303532663664373036383266180028003a470a04a6e7a94010001a1976a91436e086acf6561a68ba64196e7b92b606d0b8516688ac2222613569644363484e385759787646436542585358764d50725a4875426b5a6d71454a3a470a0459682f0010011a1976a914381a5dd1a279e8e63e67cde39ecfa61a99dd2ba288ac2222613571374164346f6b534646566835616479717835445432315254784a796b70554d3a470a0405f5e10010021a1976a9147d9ed014fc4e603fca7c2e3f9097fb7d0fb487fc88ac22226143416754506774596341344579735534554b4338364551643563547448744363723a470a0405f5e10010031a1976a914bc7e5a5234db3ab82d74c396ad2b2af419b7517488ac222261487538393769767a6d6546754c4e4236393536583667794765564e4855425267443a470a0405f5e10010041a1976a914ff71b0c9c2a90c6164a50a2fb523eb54a8a6b55088ac22226151313846425646746e756575635a4b655667347372686d7a6270416562314b6f4e3a470a0411e1a30010051a1976a9140654dd9b856f2ece1d56cb4ee5043cd9398d962c88ac2222613148775464436d5156334e73705032517143477065686f467069384e59345a67333a470a0405f5e10010061a1976a9140b4bfb256ef4bfa360e3b9e66e53a0bd84d196bc88ac222261316b43434764646635704d585369704c564439684247324d4747564e614a3135554000 -0a208d1f32f35c32d2c127a7400dc1ec52049fbf0b8bcdf284cfaa3da59b6169a22d12d60203000600000000000000fd4901010094140000010001eb44148fbbe7c36e1406ea68701f9b9dd5e10f3aeecbbad9885d73846bf0891932000000000000003200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018adf080f70520002894294000 -0a20e5767d3606230a65f150837a6f28b4f0e4c2702a683045df3883d57702739c6112d70203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502b4140101ffffffff07004e725300000000232103fb09a216761d5e7f248294970c2370f7f84ce1ad564b8e7096b1e19116af1d52ac80f0fa02000000001976a914296134d2415bf1f2b518b3f673816d7e603b160088ac80f0fa02000000001976a914e1e1dc06a889c1b6d3eb00eef7a96f6a7cfb884888ac80f0fa02000000001976a914ab03ecfddee6330497be894d16c29ae341c123aa88ac80d1f008000000001976a9144281a58a1d5b2d3285e00cb45a8492debbdad4c588ac80f0fa02000000001976a9141fd264c0bb53bd9fef18e2248ddf1383d6e811ae88ac8017b42c000000001976a91471a3892d164ffa3829078bf9ad5f114a3908ce5588ac00000000260100b414000037ff812f8ad1814d4acff9c45457873553fa2ef12602c1386ec6894e7fbb9b951881b981f705200028b42932140a0a30326234313430313031180028ffffffff0f3a510a0453724e0010001a232103fb09a216761d5e7f248294970c2370f7f84ce1ad564b8e7096b1e19116af1d52ac222254416e3947686b7033316d79585267656a436a3131775756485431344c736a3334393a470a0402faf08010011a1976a914296134d2415bf1f2b518b3f673816d7e603b160088ac222254446b313977504b59713931693138716d593655394665546454787750655376656f3a470a0402faf08010021a1976a914e1e1dc06a889c1b6d3eb00eef7a96f6a7cfb884888ac222254575a5a6344476b4e697854414d745242717a5a6b6b4d4862713147367655546b353a470a0402faf08010031a1976a914ab03ecfddee6330497be894d16c29ae341c123aa88ac222254525a5446644e434b434b624c4d515638635a446b514e3956777575713467447a543a470a0408f0d18010041a1976a9144281a58a1d5b2d3285e00cb45a8492debbdad4c588ac222254473272756a353945356231753947334637485156733670436356444278725176653a470a0402faf08010051a1976a9141fd264c0bb53bd9fef18e2248ddf1383d6e811ae88ac2222544373547a515a4b566e3466616f386a446d42397a51426b3959514e455a335866533a470a042cb4178010061a1976a91471a3892d164ffa3829078bf9ad5f114a3908ce5588ac2222544c4c354751554c58347542667a3779584c3656635a79767a644b5676315247786d4000 \ No newline at end of file +0a2096ae951083651f141d1fb2719c76d47e5a3ad421b81905f679c0edb60f2de0ff12e20101000000015d29bd6aaefc76d42e3f23340324be0d235a35ff6ab80187be75f3c3d9cf8c44010000006b483045022100bdc6b51c114617e29e28390dc9b3ad95b833ca3d1f0429ba667c58a667f9124702204ca2ed362dd9ef723ddbdcf4185b47c28b127a36f46bc4717662be863309b3e601210387e7ff08b953e3736955408fc6ebcd8aa84a04cc4b45758ea29cc2cfe1820535feffffff02002465c7090000001976a91429bef7962c5c65a2f0f4f7d9ec791866c54f851688ac001194fb180000001976a914e2cee7b71c3a4637dbdfe613f19f4b4f2d070d7f88acf8ec010018f5fedae10520f8d90728fad9073297011220448ccfd9c3f375be8701b86aff355a230dbe240334233f2ed476fcae6abd295d1801226b483045022100bdc6b51c114617e29e28390dc9b3ad95b833ca3d1f0429ba667c58a667f9124702204ca2ed362dd9ef723ddbdcf4185b47c28b127a36f46bc4717662be863309b3e601210387e7ff08b953e3736955408fc6ebcd8aa84a04cc4b45758ea29cc2cfe182053528feffffff0f3a460a0509c76524001a1976a91429bef7962c5c65a2f0f4f7d9ec791866c54f851688ac222261345843445137416e5248396f705a3468364c63473367376f635356325362426d533a480a0518fb94110010011a1976a914e2cee7b71c3a4637dbdfe613f19f4b4f2d070d7f88ac2222614d50694b484233453141475069386b4b4c6b6e78366a314c344a6e4b43476b4c77 +0a20914ccbdb72f593e5def15978cf5891e1384a1b85e89374fc1c440c074c6dd28612b90201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1803a1860104dba36e5b082a00077c00000000052f6d70682f000000000740a9e7a6000000001976a91436e086acf6561a68ba64196e7b92b606d0b8516688ac002f6859000000001976a914381a5dd1a279e8e63e67cde39ecfa61a99dd2ba288ac00e1f505000000001976a9147d9ed014fc4e603fca7c2e3f9097fb7d0fb487fc88ac00e1f505000000001976a914bc7e5a5234db3ab82d74c396ad2b2af419b7517488ac00e1f505000000001976a914ff71b0c9c2a90c6164a50a2fb523eb54a8a6b55088ac00a3e111000000001976a9140654dd9b856f2ece1d56cb4ee5043cd9398d962c88ac00e1f505000000001976a9140b4bfb256ef4bfa360e3b9e66e53a0bd84d196bc88ac0000000018dbc7badb0528a18d0632320a303033613138363031303464626133366535623038326130303037376330303030303030303035326636643730363832663a450a04a6e7a9401a1976a91436e086acf6561a68ba64196e7b92b606d0b8516688ac2222613569644363484e385759787646436542585358764d50725a4875426b5a6d71454a3a470a0459682f0010011a1976a914381a5dd1a279e8e63e67cde39ecfa61a99dd2ba288ac2222613571374164346f6b534646566835616479717835445432315254784a796b70554d3a470a0405f5e10010021a1976a9147d9ed014fc4e603fca7c2e3f9097fb7d0fb487fc88ac22226143416754506774596341344579735534554b4338364551643563547448744363723a470a0405f5e10010031a1976a914bc7e5a5234db3ab82d74c396ad2b2af419b7517488ac222261487538393769767a6d6546754c4e4236393536583667794765564e4855425267443a470a0405f5e10010041a1976a914ff71b0c9c2a90c6164a50a2fb523eb54a8a6b55088ac22226151313846425646746e756575635a4b655667347372686d7a6270416562314b6f4e3a470a0411e1a30010051a1976a9140654dd9b856f2ece1d56cb4ee5043cd9398d962c88ac2222613148775464436d5156334e73705032517143477065686f467069384e59345a67333a470a0405f5e10010061a1976a9140b4bfb256ef4bfa360e3b9e66e53a0bd84d196bc88ac222261316b43434764646635704d585369704c564439684247324d4747564e614a313555 +0a208d1f32f35c32d2c127a7400dc1ec52049fbf0b8bcdf284cfaa3da59b6169a22d12d60203000600000000000000fd4901010094140000010001eb44148fbbe7c36e1406ea68701f9b9dd5e10f3aeecbbad9885d73846bf0891932000000000000003200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018adf080f705289429 +0a20e5767d3606230a65f150837a6f28b4f0e4c2702a683045df3883d57702739c6112d70203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502b4140101ffffffff07004e725300000000232103fb09a216761d5e7f248294970c2370f7f84ce1ad564b8e7096b1e19116af1d52ac80f0fa02000000001976a914296134d2415bf1f2b518b3f673816d7e603b160088ac80f0fa02000000001976a914e1e1dc06a889c1b6d3eb00eef7a96f6a7cfb884888ac80f0fa02000000001976a914ab03ecfddee6330497be894d16c29ae341c123aa88ac80d1f008000000001976a9144281a58a1d5b2d3285e00cb45a8492debbdad4c588ac80f0fa02000000001976a9141fd264c0bb53bd9fef18e2248ddf1383d6e811ae88ac8017b42c000000001976a91471a3892d164ffa3829078bf9ad5f114a3908ce5588ac00000000260100b414000037ff812f8ad1814d4acff9c45457873553fa2ef12602c1386ec6894e7fbb9b951881b981f70528b42932120a0a3032623431343031303128ffffffff0f3a4f0a0453724e001a232103fb09a216761d5e7f248294970c2370f7f84ce1ad564b8e7096b1e19116af1d52ac222254416e3947686b7033316d79585267656a436a3131775756485431344c736a3334393a470a0402faf08010011a1976a914296134d2415bf1f2b518b3f673816d7e603b160088ac222254446b313977504b59713931693138716d593655394665546454787750655376656f3a470a0402faf08010021a1976a914e1e1dc06a889c1b6d3eb00eef7a96f6a7cfb884888ac222254575a5a6344476b4e697854414d745242717a5a6b6b4d4862713147367655546b353a470a0402faf08010031a1976a914ab03ecfddee6330497be894d16c29ae341c123aa88ac222254525a5446644e434b434b624c4d515638635a446b514e3956777575713467447a543a470a0408f0d18010041a1976a9144281a58a1d5b2d3285e00cb45a8492debbdad4c588ac222254473272756a353945356231753947334637485156733670436356444278725176653a470a0402faf08010051a1976a9141fd264c0bb53bd9fef18e2248ddf1383d6e811ae88ac2222544373547a515a4b566e3466616f386a446d42397a51426b3959514e455a335866533a470a042cb4178010061a1976a91471a3892d164ffa3829078bf9ad5f114a3908ce5588ac2222544c4c354751554c58347542667a3779584c3656635a79767a644b5676315247786d \ No newline at end of file diff --git a/bchain/coins/fujicoin/fujicoinparser.go b/bchain/coins/fujicoin/fujicoinparser.go index 2132554394..7c4a72d90c 100644 --- a/bchain/coins/fujicoin/fujicoinparser.go +++ b/bchain/coins/fujicoin/fujicoinparser.go @@ -43,7 +43,9 @@ type FujicoinParser struct { // NewFujicoinParser returns new FujicoinParser instance func NewFujicoinParser(params *chaincfg.Params, c *btc.Configuration) *FujicoinParser { - return &FujicoinParser{BitcoinParser: btc.NewBitcoinParser(params, c)} + p := &FujicoinParser{BitcoinParser: btc.NewBitcoinParser(params, c)} + p.VSizeSupport = false + return p } // GetChainParams contains network parameters for the main Fujicoin network, diff --git a/bchain/coins/grs/grsparser_test.go b/bchain/coins/grs/grsparser_test.go index 8494ece22a..29ac6c5663 100644 --- a/bchain/coins/grs/grsparser_test.go +++ b/bchain/coins/grs/grsparser_test.go @@ -18,8 +18,8 @@ import ( var ( testTx1, testTx2 bchain.Tx - testTxPacked1 = "0a20f56521b17b828897f72b30dd21b0192fd942342e89acbb06abf1d446282c30f512bf0101000000014a9d1fdba915e0907ab02f04f88898863112a2b4fdcf872c7414588c47c874cb000000006a47304402201fb96d20d0778f54520ab59afe70d5fb20e500ecc9f02281cf57934e8029e8e10220383d5a3e80f2e1eb92765b6da0f23d454aecbd8236f083d483e9a7430236876101210331693756f749180aeed0a65a0fab0625a2250bd9abca502282a4cf0723152e67ffffffff01a0330300000000001976a914fe40329c95c5598ac60752a5310b320cb52d18e688ac0000000018ffff87da05200028a6f383013298010a001220cb74c8478c5814742c87cffdb4a21231869888f8042fb07a90e015a9db1f9d4a1800226a47304402201fb96d20d0778f54520ab59afe70d5fb20e500ecc9f02281cf57934e8029e8e10220383d5a3e80f2e1eb92765b6da0f23d454aecbd8236f083d483e9a7430236876101210331693756f749180aeed0a65a0fab0625a2250bd9abca502282a4cf0723152e6728ffffffff0f3a460a030333a010001a1976a914fe40329c95c5598ac60752a5310b320cb52d18e688ac222246744d347a416e39615659674867786d616d5742675750795a7362365268766b41394000" - testTxPacked2 = "0a209b5c4859a8a31e69788cb4402812bb28f14ad71cbd8c60b09903478bc56f79a312e00101000000000101d1613f483f2086d076c82fe34674385a86beb08f052d5405fe1aed397f852f4f0000000000feffffff02404b4c000000000017a9147a55d61848e77ca266e79a39bfc85c580a6426c987a8386f0000000000160014cc8067093f6f843d6d3e22004a4290cd0c0f336b02483045022100ea8780bc1e60e14e945a80654a41748bbf1aa7d6f2e40a88d91dfc2de1f34bd10220181a474a3420444bd188501d8d270736e1e9fe379da9970de992ff445b0972e3012103adc58245cf28406af0ef5cc24b8afba7f1be6c72f279b642d85c48798685f862d9ed090018caa384da0520d9db2728dadb27322c0a0012204f2f857f39ed1afe05542d058fb0be865a387446e32fc876d086203f483f61d1180028feffffff0f3a450a034c4b4010001a17a9147a55d61848e77ca266e79a39bfc85c580a6426c9872223324e345135466855323439374272794666556762716b414a453837614b4476335633653a4d0a036f38a810011a160014cc8067093f6f843d6d3e22004a4290cd0c0f336b222c746772733171656a7178777a666c64377a72366d663779677179357335736535787137766d74396c6b6435374000" + testTxPacked1 = "0a20f56521b17b828897f72b30dd21b0192fd942342e89acbb06abf1d446282c30f512bf0101000000014a9d1fdba915e0907ab02f04f88898863112a2b4fdcf872c7414588c47c874cb000000006a47304402201fb96d20d0778f54520ab59afe70d5fb20e500ecc9f02281cf57934e8029e8e10220383d5a3e80f2e1eb92765b6da0f23d454aecbd8236f083d483e9a7430236876101210331693756f749180aeed0a65a0fab0625a2250bd9abca502282a4cf0723152e67ffffffff01a0330300000000001976a914fe40329c95c5598ac60752a5310b320cb52d18e688ac0000000018ffff87da0528a6f383013294011220cb74c8478c5814742c87cffdb4a21231869888f8042fb07a90e015a9db1f9d4a226a47304402201fb96d20d0778f54520ab59afe70d5fb20e500ecc9f02281cf57934e8029e8e10220383d5a3e80f2e1eb92765b6da0f23d454aecbd8236f083d483e9a7430236876101210331693756f749180aeed0a65a0fab0625a2250bd9abca502282a4cf0723152e6728ffffffff0f3a440a030333a01a1976a914fe40329c95c5598ac60752a5310b320cb52d18e688ac222246744d347a416e39615659674867786d616d5742675750795a7362365268766b4139" + testTxPacked2 = "0a209b5c4859a8a31e69788cb4402812bb28f14ad71cbd8c60b09903478bc56f79a312e00101000000000101d1613f483f2086d076c82fe34674385a86beb08f052d5405fe1aed397f852f4f0000000000feffffff02404b4c000000000017a9147a55d61848e77ca266e79a39bfc85c580a6426c987a8386f0000000000160014cc8067093f6f843d6d3e22004a4290cd0c0f336b02483045022100ea8780bc1e60e14e945a80654a41748bbf1aa7d6f2e40a88d91dfc2de1f34bd10220181a474a3420444bd188501d8d270736e1e9fe379da9970de992ff445b0972e3012103adc58245cf28406af0ef5cc24b8afba7f1be6c72f279b642d85c48798685f862d9ed090018caa384da0520d9db2728dadb27322812204f2f857f39ed1afe05542d058fb0be865a387446e32fc876d086203f483f61d128feffffff0f3a430a034c4b401a17a9147a55d61848e77ca266e79a39bfc85c580a6426c9872223324e345135466855323439374272794666556762716b414a453837614b4476335633653a4d0a036f38a810011a160014cc8067093f6f843d6d3e22004a4290cd0c0f336b222c746772733171656a7178777a666c64377a72366d663779677179357335736535787137766d74396c6b643537" ) func init() { diff --git a/bchain/coins/koto/kotoparser_test.go b/bchain/coins/koto/kotoparser_test.go index c7008f2075..780ba1c8c6 100644 --- a/bchain/coins/koto/kotoparser_test.go +++ b/bchain/coins/koto/kotoparser_test.go @@ -18,8 +18,8 @@ import ( var ( testTx1, testTx2 bchain.Tx - testTxPacked1 = "0a2097f944e3558cc784f4013b3753ce9570fe4707893eda724b12eb4c69686113a612970f020000000001036b2048020000001976a9142df466d79cf4be0f7d1091512f1c297e4988fdd188ac000000000100000000000000001392204802000000a8a3d9a54a3fe3b5ae208fa2d96faea96dac6cb76b03bb2e32c5dd892a5d6f6490f05d8f6fb4401228df1be9f22c5ad69706c461bbc9253ffbc3531770e5075e149ec6af7f2cda0cb87862620792340bc425095115adbf0f16f4d3ece3f5c467cbcb02b01ced7192ab644f96fa01f04a7a450a42da2bfad1748634f7bc141467d3c94961ed6250dd23fca62b30b4a2ca6fbad0724372a429bcb97a28954e1681c0bb974877dac26eb2b994eaa23d56ecfdedd93f8331f9432f12adc37da9f049585179b7bbb370b76c6c37a438a20bc3b410a6a72ff8d11408a337a37bbc73dd15df8a34f2c878dc6d6db4f0cee504680fe53e0a158f1e1c82b84e065a4764fc57f8bf75d28899126917a05bf3230036f2d6b38f8a51214d1c2d0588a95e82f0032c2dfa6916c689f8daa648c01517bbf0826d2d4082067b0d17071920eb6dab6c0307603b825f3aae347db349628d4fcaa97a155ef1a2c601170fe825609efa964f0a06700afc135542ea7b06fc989424d7e100652c0ad5be4ca01c1fd676530e6f60606c5feb7de5da0d69544d8b7be8de06b27ba96a1bfa6bc07cc0269982acb722032a938ab36089c0eeda7a4076cd258a1752486a3d52af16db8dff072bcc61f17503185b5bc8aa0ea3a181663bb0ddd3cca1a19293764b01569b9878a60c0ce82e21020751a4ecc1a2a9b9a123042ee8d8a5d4d3f4764b1cdb13d57d2a77c3b56bd89102302d118ebc14969ade27bf83f0e707a97b26c7292a6c20e850abce5fad0ab59d032fec0d6278bbe0f2fb3fbc61697ba10b6ec3f2c4196e46e98dfb65bb28ec6afff81de5a4be7be8c4f56ab4c03043a3cf9987b630ac4b6d8aa74ce8ed5b61040262239172c6450f9ab642e2f2d258c200c3bb4ad69011ef2dfe1dd63d758c86270ac4925b248b9bb4b6c9ff7bf2e56260cba02b2429648dc20eb034c8f9b18e1f6a38b8651c236554546585b4dd0f07fd5ee1696bf792527ce84b22012439300797103f22d3969df725e4414d899ec32a2ebbb857cc911e374e84738f4e007ee5260ff666a286e45c465525e2e3fc5e5e0e9ad82e53d364e4fd355619711c616d508470b997af44f62f283871cd892552128135aadb40c6f8cf69ee72acf349e9f4d33e8673450b9f69d4022a8d886b0cdbaba0798e0bf57b42dedfafb0bbd5495ca1c0030bbf460b48f9a138f6ab748df9046d9995f895062583ce0818e40afb9704653e11d58ca42bd3f60f4e908589ad9144c76067dc433cad13a5bbd9c168691b8c6cddb19d812f3e3f98e2cdd20dbb170936fd5cd2ef0bca72af8931a1b01d6081ffbef5be4416e696a7c762a375b368f71dc31362a4005750992a48e55311dde8d2013180d62e507ffc3e468c4a27acc763a9651b19f37e1ffca7e656225617368e79c1d18f9b14d770993d3d1dc42dcfd9adfa02a8ddf0ebc8fbb850fab307fd1d239cf6ad4e5ff40992dd974bc43fa351ce807cc0036c2f7d80bbd052f496216304fbc63e8d728bf129acaedb0073aff077e584ce04bc1ccf9c91f41f3c8804dd65da4ecdfdba32590e04b4d1b6895dea8edacd1f40313e8a1d4900d0dba54056eee72e3d155e9c67e7a51df581c33cbd39f16549d590ae5387fe2c5ad3484ad5c7da320066b79083c49879e45938b3bbb063726008a2ebb8847c9e57be6ec489c7aaa80f5e8e430040cb8d60298363df850cb7b4e98e97192882d10d2fd96cc490dd18b263d96aac6aa4f5583770e0917fa9b566dd0e0b218c6684007ec10cf11747e8f039fac5250170de2835ab88fea356b6a7d0f5e81ffe9b78d191a745e0237a256a2a840880689d83503b72462e3955b61e22afde947c1f1527ea94151c5b7d3a72ce68979603911c08bcec01097899fd30347be7f2e246f70d2af6a1e29b54988978a91f79b2ed8be76ffd62f79de5418933ed166be919d9bbb7524347d87d31afaed05e71a82b09c18c196b3ba6e226939b375903f7d889422863567203814484af89fd223ce1c959b1fdffaf26461630c630d2bbf99228a096ea6cb0d61df70d24414c76bf9371c4abff0ad257098189af6ea32200fbe092d875aa4d3f72a7ec138439e4b08fb1dcde6a90f25fde1498773e693c9b21c40505d42edcbcaed8a2dc4642750e9df73e169f9986ddb3a57991ac2cb3b540d788e2c2c22c2c51b2d74a98ed59a8cec89ba54342fb9660449a116f8691da60cb447afe4d5e80f37b4669e6007c1cadc41933fb27bab41afd312c37e5cb43715cb4013efcd91221ed06249540b733c05e81131aba75ba0f427d9bd975554b2d49a8048f0b3a84477e75290235fd3bbcaee6c4438ba72299dd960f3f6ee9241f7e399684e894d7bb1c302ecbe24d0f19dca982a82ee44f36211d23b0ea623c9c9f4f527f4e452fd06ebb943cadeea3d7fa42cabd25324bc5851e40f9952823f56b50b97729e6561f2100c2b5922860c6cf447a668324ca931f2f35a5edb7d306f8b8802f98cf67140a3fe73099ab86bb65c439a8593e64816bcd46aa4c254918f4a3a0f3f47b4ebcfb2824703f9a7d163824484ce6fe1852c4ba131ee2635de14822a8cb3782697fecc6f69514edd3f42fcf2751075b838bf14ae91e9dcff517bf3cec4db1b986b4c966a4fa40d38f2ebb7fc60218c397a2d705200028d09a0c3a490a050248206b0310001a1976a9142df466d79cf4be0f7d1091512f1c297e4988fdd188ac22236b31323257767a46415565444b3667353238563376726547673870614a5137624248364000" - testTxPacked2 = "0a203aebcf5a223450bca3c0312d3d87b6070447e795d09a266a3a01c70e44c7cc4812e1010100000001cbc2c0b14b26f563ceee8201971b2caae2a4f964d0fd91267290c51a6a171411010000006a473044022032dd5d573c3a7f729da1cb9d9ba02a08e05d50b4f74d5aeb7cb22284526f70340220661ca4a192d02684f0b6b52768b9e9ae5fad41b962aa918537b91bba275e92e70121024e98e62782ba44e5677b52b1e4e973a027c7d873915a6d62ba967b2c07467224ffffffff02c0c62d00000000001976a914dd985697513887236c484acc605ece839e2204ac88ac989e8ce0000000001976a91482bfe75940a6d46238f55e258fcae5bef4e847ea88ac0000000018ff98a2d705200028d49a0c3298010a0012201114176a1ac590722691fdd064f9a4e2aa2c1b970182eece63f5264bb1c0c2cb1801226a473044022032dd5d573c3a7f729da1cb9d9ba02a08e05d50b4f74d5aeb7cb22284526f70340220661ca4a192d02684f0b6b52768b9e9ae5fad41b962aa918537b91bba275e92e70121024e98e62782ba44e5677b52b1e4e973a027c7d873915a6d62ba967b2c0746722428ffffffff0f3a470a032dc6c010001a1976a914dd985697513887236c484acc605ece839e2204ac88ac22236b314a334461347236356653616b6571555953616a6f506f74656376633768384861513a480a04e08c9e9810011a1976a91482bfe75940a6d46238f55e258fcae5bef4e847ea88ac22236b31396b7355666462355139584b556a3565645570314451686e6343503868396845374000" + testTxPacked1 = "0a2097f944e3558cc784f4013b3753ce9570fe4707893eda724b12eb4c69686113a612970f020000000001036b2048020000001976a9142df466d79cf4be0f7d1091512f1c297e4988fdd188ac000000000100000000000000001392204802000000a8a3d9a54a3fe3b5ae208fa2d96faea96dac6cb76b03bb2e32c5dd892a5d6f6490f05d8f6fb4401228df1be9f22c5ad69706c461bbc9253ffbc3531770e5075e149ec6af7f2cda0cb87862620792340bc425095115adbf0f16f4d3ece3f5c467cbcb02b01ced7192ab644f96fa01f04a7a450a42da2bfad1748634f7bc141467d3c94961ed6250dd23fca62b30b4a2ca6fbad0724372a429bcb97a28954e1681c0bb974877dac26eb2b994eaa23d56ecfdedd93f8331f9432f12adc37da9f049585179b7bbb370b76c6c37a438a20bc3b410a6a72ff8d11408a337a37bbc73dd15df8a34f2c878dc6d6db4f0cee504680fe53e0a158f1e1c82b84e065a4764fc57f8bf75d28899126917a05bf3230036f2d6b38f8a51214d1c2d0588a95e82f0032c2dfa6916c689f8daa648c01517bbf0826d2d4082067b0d17071920eb6dab6c0307603b825f3aae347db349628d4fcaa97a155ef1a2c601170fe825609efa964f0a06700afc135542ea7b06fc989424d7e100652c0ad5be4ca01c1fd676530e6f60606c5feb7de5da0d69544d8b7be8de06b27ba96a1bfa6bc07cc0269982acb722032a938ab36089c0eeda7a4076cd258a1752486a3d52af16db8dff072bcc61f17503185b5bc8aa0ea3a181663bb0ddd3cca1a19293764b01569b9878a60c0ce82e21020751a4ecc1a2a9b9a123042ee8d8a5d4d3f4764b1cdb13d57d2a77c3b56bd89102302d118ebc14969ade27bf83f0e707a97b26c7292a6c20e850abce5fad0ab59d032fec0d6278bbe0f2fb3fbc61697ba10b6ec3f2c4196e46e98dfb65bb28ec6afff81de5a4be7be8c4f56ab4c03043a3cf9987b630ac4b6d8aa74ce8ed5b61040262239172c6450f9ab642e2f2d258c200c3bb4ad69011ef2dfe1dd63d758c86270ac4925b248b9bb4b6c9ff7bf2e56260cba02b2429648dc20eb034c8f9b18e1f6a38b8651c236554546585b4dd0f07fd5ee1696bf792527ce84b22012439300797103f22d3969df725e4414d899ec32a2ebbb857cc911e374e84738f4e007ee5260ff666a286e45c465525e2e3fc5e5e0e9ad82e53d364e4fd355619711c616d508470b997af44f62f283871cd892552128135aadb40c6f8cf69ee72acf349e9f4d33e8673450b9f69d4022a8d886b0cdbaba0798e0bf57b42dedfafb0bbd5495ca1c0030bbf460b48f9a138f6ab748df9046d9995f895062583ce0818e40afb9704653e11d58ca42bd3f60f4e908589ad9144c76067dc433cad13a5bbd9c168691b8c6cddb19d812f3e3f98e2cdd20dbb170936fd5cd2ef0bca72af8931a1b01d6081ffbef5be4416e696a7c762a375b368f71dc31362a4005750992a48e55311dde8d2013180d62e507ffc3e468c4a27acc763a9651b19f37e1ffca7e656225617368e79c1d18f9b14d770993d3d1dc42dcfd9adfa02a8ddf0ebc8fbb850fab307fd1d239cf6ad4e5ff40992dd974bc43fa351ce807cc0036c2f7d80bbd052f496216304fbc63e8d728bf129acaedb0073aff077e584ce04bc1ccf9c91f41f3c8804dd65da4ecdfdba32590e04b4d1b6895dea8edacd1f40313e8a1d4900d0dba54056eee72e3d155e9c67e7a51df581c33cbd39f16549d590ae5387fe2c5ad3484ad5c7da320066b79083c49879e45938b3bbb063726008a2ebb8847c9e57be6ec489c7aaa80f5e8e430040cb8d60298363df850cb7b4e98e97192882d10d2fd96cc490dd18b263d96aac6aa4f5583770e0917fa9b566dd0e0b218c6684007ec10cf11747e8f039fac5250170de2835ab88fea356b6a7d0f5e81ffe9b78d191a745e0237a256a2a840880689d83503b72462e3955b61e22afde947c1f1527ea94151c5b7d3a72ce68979603911c08bcec01097899fd30347be7f2e246f70d2af6a1e29b54988978a91f79b2ed8be76ffd62f79de5418933ed166be919d9bbb7524347d87d31afaed05e71a82b09c18c196b3ba6e226939b375903f7d889422863567203814484af89fd223ce1c959b1fdffaf26461630c630d2bbf99228a096ea6cb0d61df70d24414c76bf9371c4abff0ad257098189af6ea32200fbe092d875aa4d3f72a7ec138439e4b08fb1dcde6a90f25fde1498773e693c9b21c40505d42edcbcaed8a2dc4642750e9df73e169f9986ddb3a57991ac2cb3b540d788e2c2c22c2c51b2d74a98ed59a8cec89ba54342fb9660449a116f8691da60cb447afe4d5e80f37b4669e6007c1cadc41933fb27bab41afd312c37e5cb43715cb4013efcd91221ed06249540b733c05e81131aba75ba0f427d9bd975554b2d49a8048f0b3a84477e75290235fd3bbcaee6c4438ba72299dd960f3f6ee9241f7e399684e894d7bb1c302ecbe24d0f19dca982a82ee44f36211d23b0ea623c9c9f4f527f4e452fd06ebb943cadeea3d7fa42cabd25324bc5851e40f9952823f56b50b97729e6561f2100c2b5922860c6cf447a668324ca931f2f35a5edb7d306f8b8802f98cf67140a3fe73099ab86bb65c439a8593e64816bcd46aa4c254918f4a3a0f3f47b4ebcfb2824703f9a7d163824484ce6fe1852c4ba131ee2635de14822a8cb3782697fecc6f69514edd3f42fcf2751075b838bf14ae91e9dcff517bf3cec4db1b986b4c966a4fa40d38f2ebb7fc60218c397a2d70528d09a0c3a470a050248206b031a1976a9142df466d79cf4be0f7d1091512f1c297e4988fdd188ac22236b31323257767a46415565444b3667353238563376726547673870614a513762424836" + testTxPacked2 = "0a203aebcf5a223450bca3c0312d3d87b6070447e795d09a266a3a01c70e44c7cc4812e1010100000001cbc2c0b14b26f563ceee8201971b2caae2a4f964d0fd91267290c51a6a171411010000006a473044022032dd5d573c3a7f729da1cb9d9ba02a08e05d50b4f74d5aeb7cb22284526f70340220661ca4a192d02684f0b6b52768b9e9ae5fad41b962aa918537b91bba275e92e70121024e98e62782ba44e5677b52b1e4e973a027c7d873915a6d62ba967b2c07467224ffffffff02c0c62d00000000001976a914dd985697513887236c484acc605ece839e2204ac88ac989e8ce0000000001976a91482bfe75940a6d46238f55e258fcae5bef4e847ea88ac0000000018ff98a2d70528d49a0c32960112201114176a1ac590722691fdd064f9a4e2aa2c1b970182eece63f5264bb1c0c2cb1801226a473044022032dd5d573c3a7f729da1cb9d9ba02a08e05d50b4f74d5aeb7cb22284526f70340220661ca4a192d02684f0b6b52768b9e9ae5fad41b962aa918537b91bba275e92e70121024e98e62782ba44e5677b52b1e4e973a027c7d873915a6d62ba967b2c0746722428ffffffff0f3a450a032dc6c01a1976a914dd985697513887236c484acc605ece839e2204ac88ac22236b314a334461347236356653616b6571555953616a6f506f74656376633768384861513a480a04e08c9e9810011a1976a91482bfe75940a6d46238f55e258fcae5bef4e847ea88ac22236b31396b7355666462355139584b556a3565645570314451686e634350386839684537" ) func init() { diff --git a/bchain/coins/liquid/liquidparser_test.go b/bchain/coins/liquid/liquidparser_test.go index 6e1401b8e5..d76f15a294 100644 --- a/bchain/coins/liquid/liquidparser_test.go +++ b/bchain/coins/liquid/liquidparser_test.go @@ -137,7 +137,7 @@ func Test_GetAddressesFromAddrDesc(t *testing.T) { var ( testTx1 bchain.Tx - testTxPacked1 = "0a207aa1af9481f2d744c96015b1baea6ba753790971ff265adfd93775de0234bfd612b51a020000000101a99547d213b005f355da348de54f5eb370fbc6a5687e412897ef0ec4ce237d75020000006b483045022100ae926c96c746308e7488e022f4ad1db94d5d0c8683f6fa6ded3afb13d8e20578022074aa8ebfe20adaf25beed70c60cfb5007278bec17875f11f7a5b3c86eb60a96901210391abdcd113c40b56f13a548e8624d6ad8d7a162b33ef81020d046cadbda26637feffffff030af62b535fc393152f6d575708b271e3a53514cdcf65508c7d12e8fd06709ce24208e192e1315aa94c3bc0eddbb0ae195aff0a1ba2e19773b79e5d1b29fdd8df211b02f8b255b83fa13f40745fb5054b89dc7dcba497c850086a8726bac66ac22d4be31976a914d79c1c8b67a0275c60e33b67bbd0e19a79b9276388ac016d521c38ec1ea15734ae22b7c46064412829c0d0579f0a713d1c04ede979026f01000000000000000000256a23426c6f636b73747265616d2e696e666f206973206120636f6f6c206578706c6f726572016d521c38ec1ea15734ae22b7c46064412829c0d0579f0a713d1c04ede979026f0100000000000004800000000000000000000043010001db986007e38ccb7bdef1661fcf633cbde3d8850cd116736d9b0b0b027901921694954b26036eb51d9424c38083aa3d937998ea81077843925513523eb6ec3337fd4d0b602300000000000000011486003b49c8ab1a6aa09a8eaec4ed83dbda4cb4ef9651114513823a6eefb1dbf971631d88a229a3b027a94f9f15966765f6f5ce3245057c57aedf8f4c69e355f4264a10e2c7dad691879844074ae5e37152c828aba89035e928c63b1c1590638333d05ed08442538ebf7dff9c94ce11ab6bb6d5c9535fce99fab023aabc7215b52eed15454333428b065fb5d8b03aa2fe1de5004f9d8fca717bcd682f1f9caa6561bdafbe69c8423166f7e33867f0bc4fdd85224ff1533839762530d47a1f053c40c7e38f84a1431ad03398bc9d634384aec0f22e75ac4a94e3703ec8715d0791564a8b1509eab4bf7543ef2e5c20fe9fb6c2a85afe42fcdba64c628103ea5693287a28ded79f517a3e877fe287ca6f3d1229295abaf5c1b3b43ab54ec4697c941da47e0934718b697a414d8fd1a722eb23ceb554afb1b4c807a94507a35b153f19da10c6688c71efa0d1ad15c8ac2f3d786ae8c43136cacf333c2643e521322314346b201427f1ac975340cf3caf102245d8cf45709bb51e41fd357f1fb7316138f1992e5646842eb4fe18ef6a7096f7e4c1e69950285f30c64dc1da661c055944356f140d6298f5eaf733799bef034a8afb05f6f74239e572acf5d1c2f9e028b806caab13eb19148456b3db0f719ad76818eaea11e7989e74d858743ffba738a5f5c7d12ab1b049c594656821620e20817c6acc5b3d3725f9183d3c01a0190d5d991d1603682418f4a9c55b59cab8787f463123e1efd7d3e7f75dfb9ccf931ae2dc965b82cabf2af6b93293a4b00d7145d97a24679076157bade5ba4d7577922052c719ebf2493f5e9d0cf8e6f82666e8732dd91068395e528d0ea532e27ae8a84ed516dabd30dd990a1539d93f7b775039d53f1a12d7e7747a64f6aca9d7589330c8adf8854dce0fee0be212716d6c1b075fabb9d0c815e613c772f7c57e7efdb4e64a5ef47c9cf384deab55191d7b4b0cb27986649eca2c4286a01a7d3c304f008c6d3fe53abf320afc7602654cf4b69b87fd2b9b6794fe7cc77e92bbef572dc8ddb1530da6d82268cd32db112afb8c2db69651959c1ad39d804178cae05f856539559e3dd0e869d9985f41262c30058d1d3c2cd336649b3a893bd4b29c206c53eeb337025a7585fc4bc05d8fe71035ea082ea543cbc15f8e6f0658815c7553795917958b503779cf6a18f92c391769e846327d3cd8458c089cb1e342a590eb57a583365c4bd5de34dc17992f5309fb69b67aa8436c763183a8f11a543fdb8a376a30836013fff3a2d21b72e22f1d9d031a3cd467365256e120894e1489c238df5ce65183b93d68024697245be8c0a9d60e31452d247a98136ca559622e2f4b35569ec8ab8539528a09214f1d3862bc97b21b7bb2b66c654ebee00eb26ec57988ff174cab65d4864eea14f15b65252fe534750bea3b9b4400c811ccf3e6517ecf1f1585d908f21b91bfd223eddbb9b980bb65133934c8027fde78865ed4d969bc6de3be613730d3a0f179f0ed734c67900da53c230045d60d31980401e7fa1396891d42555af8152bb0e6557d3c7d718f7ba85b6848ab052fcf709c7aea676d8c00c787a7fb2a2b1972d245aa51a24ceb13d7dd38cd0c214727a1000561828a62c969afa60d36cfab21b8e6837a0e0ac325c6c20e3ccd09fd1f5400dd6a7dc861e050bcb47ee89670d807b514e5b59ea830ef885ac6a0371efdcbb09e676fd9790e00ab9686f59b2ce173896d4d451e6fd61f3b95b82e63a494d7853c78c2f03e45cbabd46a4586364c131000d9efdc71d30d3caf25cdfad63a6628e67d5f219a0cafc5509f556cec7ac110ce0b52b552be6780e8d0c31a068dc6607ebc9cac4471cb1e85e5b6a0bc2330062f6930e2ef623da059da02902fa28586614a053625ea5901dc6151e7fae3a63b444b9d53630dea6b90d3c2ca7dd8db69f39a2ad6bc2eec08a85c1c0d0a9b079825278cf0b6f415344b0b6e4c7a51947e98ada9149c8f427914d8245b152f8f3558178d16e35498649a34ceb2fdaafc0d303829ddd9412c9a2b5ed1ce060472a9a85ffa19a4ddfbdb89437e72b261472d2b8c70a01a96562b753692c7329d75057a9918dbb3a39a90c86b66ec745a14e9909b2eae6c46dcd8c8a666973ba356124d6444353f1a34f88f907af897ea8e642f8603aed400d3d8a75568baa96b26b8d04f5fb0d1ca1256e7057f93464d95d1994ac5189ca607c013637fa35879a6c67c4806b6c9f00ccedb953103bbec21f054d304b621e0eca771accf5181409642d58ea8d032e06c27295e57092b4e3a89f47f47f16dc82f07dfad44ca7077385f62b4ad2efecb97955759c31977719316545b6fc6a78b7e35206719fcba4c1dca5d0f7f9959f7bd1c117532a2f7fc3ca87820e38cfab558dc48adb1058964b6ea9dfde5e06bff6b5d7af19fe6b8a46d0ec76f8902e525082591b82c2f86a30341787a1da86ab517063b39ae076b3f10e78f6c79dd58041b99ab40824b598f6c815733d62d262abeb96b8224b56e4109d4d5c445cd055a9cd2aea7753fc0f6a55bcf4291551a0d9c852e19e0f0603eefc9a79a38bc07a73f6e0f88a9d3eac01ca57d5d55e2ef870743522c15841b4ea7a0d278922cc977724571c75b5039a20f5dad843941d91fb421944b54923ed8c5f71edafbdfaf4c12dbe1c2b2a9642bbc2bd07017a44cb402d6ae09248eb24b70d43f751d3bf761ecef51f1f15239de25222852f95607335b3410050e5bd65f8c9657f0a95185e48dcb2f711641c0994e352b0039d569ddbd27182c263649d3e27b3816acd5822b31c98661adf01ee62db361be1b0469de4fc1c6e10185203eb44abe3d077fcbc52bef6095ca30c81a22b6e7a64c1385e7dd11209fc073a29ef656769fbb24d4292d98379884a1a2b2c7f6fd895577d739a2f0411e8afcee4de40927443f28e72aebc763fd0315ee85d29a23bb235ce82a5a621c0d9021965673720be9057a26b1ff0380263b54777f6cc7b7531d284f5041d4480108e4b4986f1ddec9e65dd2392c7fa0349c2a39954e4aeec8fe8f40e66db57d29beaf0046d9387482bab71a9397d90611cf767637c8666da87d5f1d798558cbb7228844010510cb95b073cec3878893ba70549eb6d3428b8db6944118f6de2ee7107b593ef85441cba46238f7843c4e6e7497f14cc64c25653c87da756226ce774c2b5e43294f2ebab2f601e9fe3f2d1d3172cfcc7e6eb7ea9b237e5f093443b02f42b4ea85673a6a0000ef9a6ddf2263a1a75eb7b78d3f90b1de91a22d78aa06d9626b0f9090ee63d92418b084db647132e3b0b7f3ed583eec280d06b94a358daffcd333233fb390ba8bf2da3921b18adf6cd4901cbabf4f4e3c90f21eb3190c8c0e4f16ac25dd546859bc640354ab9769553aeda466191ba4b10f52da5342347685e52af5d20ba8c113e65663bcedc12c99e576c4e1bdde013017d16fc26e3f30418c21d72ad6507ef1c8d437342d0fc20ad102e6c49eb9a8e7a3df5366c9a75b6d95ab007a3d93bca0086414ac5bc44a872659f43f0f703b415ac0e9aeeedb2c0cb945938923dc0865c5d3ff673e068d2865b11c68774cd6c0be1caa40627425bdc4ecbbd0a642f9c6953464e2f20681994be8483d64ed6d2d8efa79c5b12776900e58b45bea18c2e0220ea27e9670485e2c6ce9f52ac08cad61ca57b839710209db8a5d4fe95a846960b126271e519545e5d4d15300fc0ef2f35b8def7ba6f639d85404b57bac35140bef1c4f3b455773bf2a2eae62118dcdc5474ec0900aca49300833417786d1fb0d76a56570cfa10d23dbab0305e1c9c48032b19fd2ec2e00b1528a248036590e26e8c75209d004e20bc7730b29cf3ed860848b83ab91ad6a635f2cc1eca89e16814f34f2c1c2766a28b2901170bb4839f08f5685e593b8a5ce2a801194818b4aeb0a01794f92c7bc4144808e997cfd1711485b60483cf4a310b23e0210c5c73e6956b9e1ae696f1ea79b3fab788bf229a349fd9caedebd5db99821b1cebdceafb011cfcd1c78f93774b35f25c7e69952b0bca1a0d40791b812614e996ca31548bc0000000018cfdaa0e0052000288781053299010a001220757d23cec40eef9728417e68a5c6fb70b35e4fe58d34da55f305b013d24795a91802226b483045022100ae926c96c746308e7488e022f4ad1db94d5d0c8683f6fa6ded3afb13d8e20578022074aa8ebfe20adaf25beed70c60cfb5007278bec17875f11f7a5b3c86eb60a96901210391abdcd113c40b56f13a548e8624d6ad8d7a162b33ef81020d046cadbda2663728feffffff0f3a4110001a1976a914d79c1c8b67a0275c60e33b67bbd0e19a79b9276388ac222251477652524658424d32666558755533394577484e75595953726e4d33486155794d3a2910011a256a23426c6f636b73747265616d2e696e666f206973206120636f6f6c206578706c6f7265723a060a02048010024002" + testTxPacked1 = "0a207aa1af9481f2d744c96015b1baea6ba753790971ff265adfd93775de0234bfd612b51a020000000101a99547d213b005f355da348de54f5eb370fbc6a5687e412897ef0ec4ce237d75020000006b483045022100ae926c96c746308e7488e022f4ad1db94d5d0c8683f6fa6ded3afb13d8e20578022074aa8ebfe20adaf25beed70c60cfb5007278bec17875f11f7a5b3c86eb60a96901210391abdcd113c40b56f13a548e8624d6ad8d7a162b33ef81020d046cadbda26637feffffff030af62b535fc393152f6d575708b271e3a53514cdcf65508c7d12e8fd06709ce24208e192e1315aa94c3bc0eddbb0ae195aff0a1ba2e19773b79e5d1b29fdd8df211b02f8b255b83fa13f40745fb5054b89dc7dcba497c850086a8726bac66ac22d4be31976a914d79c1c8b67a0275c60e33b67bbd0e19a79b9276388ac016d521c38ec1ea15734ae22b7c46064412829c0d0579f0a713d1c04ede979026f01000000000000000000256a23426c6f636b73747265616d2e696e666f206973206120636f6f6c206578706c6f726572016d521c38ec1ea15734ae22b7c46064412829c0d0579f0a713d1c04ede979026f0100000000000004800000000000000000000043010001db986007e38ccb7bdef1661fcf633cbde3d8850cd116736d9b0b0b027901921694954b26036eb51d9424c38083aa3d937998ea81077843925513523eb6ec3337fd4d0b602300000000000000011486003b49c8ab1a6aa09a8eaec4ed83dbda4cb4ef9651114513823a6eefb1dbf971631d88a229a3b027a94f9f15966765f6f5ce3245057c57aedf8f4c69e355f4264a10e2c7dad691879844074ae5e37152c828aba89035e928c63b1c1590638333d05ed08442538ebf7dff9c94ce11ab6bb6d5c9535fce99fab023aabc7215b52eed15454333428b065fb5d8b03aa2fe1de5004f9d8fca717bcd682f1f9caa6561bdafbe69c8423166f7e33867f0bc4fdd85224ff1533839762530d47a1f053c40c7e38f84a1431ad03398bc9d634384aec0f22e75ac4a94e3703ec8715d0791564a8b1509eab4bf7543ef2e5c20fe9fb6c2a85afe42fcdba64c628103ea5693287a28ded79f517a3e877fe287ca6f3d1229295abaf5c1b3b43ab54ec4697c941da47e0934718b697a414d8fd1a722eb23ceb554afb1b4c807a94507a35b153f19da10c6688c71efa0d1ad15c8ac2f3d786ae8c43136cacf333c2643e521322314346b201427f1ac975340cf3caf102245d8cf45709bb51e41fd357f1fb7316138f1992e5646842eb4fe18ef6a7096f7e4c1e69950285f30c64dc1da661c055944356f140d6298f5eaf733799bef034a8afb05f6f74239e572acf5d1c2f9e028b806caab13eb19148456b3db0f719ad76818eaea11e7989e74d858743ffba738a5f5c7d12ab1b049c594656821620e20817c6acc5b3d3725f9183d3c01a0190d5d991d1603682418f4a9c55b59cab8787f463123e1efd7d3e7f75dfb9ccf931ae2dc965b82cabf2af6b93293a4b00d7145d97a24679076157bade5ba4d7577922052c719ebf2493f5e9d0cf8e6f82666e8732dd91068395e528d0ea532e27ae8a84ed516dabd30dd990a1539d93f7b775039d53f1a12d7e7747a64f6aca9d7589330c8adf8854dce0fee0be212716d6c1b075fabb9d0c815e613c772f7c57e7efdb4e64a5ef47c9cf384deab55191d7b4b0cb27986649eca2c4286a01a7d3c304f008c6d3fe53abf320afc7602654cf4b69b87fd2b9b6794fe7cc77e92bbef572dc8ddb1530da6d82268cd32db112afb8c2db69651959c1ad39d804178cae05f856539559e3dd0e869d9985f41262c30058d1d3c2cd336649b3a893bd4b29c206c53eeb337025a7585fc4bc05d8fe71035ea082ea543cbc15f8e6f0658815c7553795917958b503779cf6a18f92c391769e846327d3cd8458c089cb1e342a590eb57a583365c4bd5de34dc17992f5309fb69b67aa8436c763183a8f11a543fdb8a376a30836013fff3a2d21b72e22f1d9d031a3cd467365256e120894e1489c238df5ce65183b93d68024697245be8c0a9d60e31452d247a98136ca559622e2f4b35569ec8ab8539528a09214f1d3862bc97b21b7bb2b66c654ebee00eb26ec57988ff174cab65d4864eea14f15b65252fe534750bea3b9b4400c811ccf3e6517ecf1f1585d908f21b91bfd223eddbb9b980bb65133934c8027fde78865ed4d969bc6de3be613730d3a0f179f0ed734c67900da53c230045d60d31980401e7fa1396891d42555af8152bb0e6557d3c7d718f7ba85b6848ab052fcf709c7aea676d8c00c787a7fb2a2b1972d245aa51a24ceb13d7dd38cd0c214727a1000561828a62c969afa60d36cfab21b8e6837a0e0ac325c6c20e3ccd09fd1f5400dd6a7dc861e050bcb47ee89670d807b514e5b59ea830ef885ac6a0371efdcbb09e676fd9790e00ab9686f59b2ce173896d4d451e6fd61f3b95b82e63a494d7853c78c2f03e45cbabd46a4586364c131000d9efdc71d30d3caf25cdfad63a6628e67d5f219a0cafc5509f556cec7ac110ce0b52b552be6780e8d0c31a068dc6607ebc9cac4471cb1e85e5b6a0bc2330062f6930e2ef623da059da02902fa28586614a053625ea5901dc6151e7fae3a63b444b9d53630dea6b90d3c2ca7dd8db69f39a2ad6bc2eec08a85c1c0d0a9b079825278cf0b6f415344b0b6e4c7a51947e98ada9149c8f427914d8245b152f8f3558178d16e35498649a34ceb2fdaafc0d303829ddd9412c9a2b5ed1ce060472a9a85ffa19a4ddfbdb89437e72b261472d2b8c70a01a96562b753692c7329d75057a9918dbb3a39a90c86b66ec745a14e9909b2eae6c46dcd8c8a666973ba356124d6444353f1a34f88f907af897ea8e642f8603aed400d3d8a75568baa96b26b8d04f5fb0d1ca1256e7057f93464d95d1994ac5189ca607c013637fa35879a6c67c4806b6c9f00ccedb953103bbec21f054d304b621e0eca771accf5181409642d58ea8d032e06c27295e57092b4e3a89f47f47f16dc82f07dfad44ca7077385f62b4ad2efecb97955759c31977719316545b6fc6a78b7e35206719fcba4c1dca5d0f7f9959f7bd1c117532a2f7fc3ca87820e38cfab558dc48adb1058964b6ea9dfde5e06bff6b5d7af19fe6b8a46d0ec76f8902e525082591b82c2f86a30341787a1da86ab517063b39ae076b3f10e78f6c79dd58041b99ab40824b598f6c815733d62d262abeb96b8224b56e4109d4d5c445cd055a9cd2aea7753fc0f6a55bcf4291551a0d9c852e19e0f0603eefc9a79a38bc07a73f6e0f88a9d3eac01ca57d5d55e2ef870743522c15841b4ea7a0d278922cc977724571c75b5039a20f5dad843941d91fb421944b54923ed8c5f71edafbdfaf4c12dbe1c2b2a9642bbc2bd07017a44cb402d6ae09248eb24b70d43f751d3bf761ecef51f1f15239de25222852f95607335b3410050e5bd65f8c9657f0a95185e48dcb2f711641c0994e352b0039d569ddbd27182c263649d3e27b3816acd5822b31c98661adf01ee62db361be1b0469de4fc1c6e10185203eb44abe3d077fcbc52bef6095ca30c81a22b6e7a64c1385e7dd11209fc073a29ef656769fbb24d4292d98379884a1a2b2c7f6fd895577d739a2f0411e8afcee4de40927443f28e72aebc763fd0315ee85d29a23bb235ce82a5a621c0d9021965673720be9057a26b1ff0380263b54777f6cc7b7531d284f5041d4480108e4b4986f1ddec9e65dd2392c7fa0349c2a39954e4aeec8fe8f40e66db57d29beaf0046d9387482bab71a9397d90611cf767637c8666da87d5f1d798558cbb7228844010510cb95b073cec3878893ba70549eb6d3428b8db6944118f6de2ee7107b593ef85441cba46238f7843c4e6e7497f14cc64c25653c87da756226ce774c2b5e43294f2ebab2f601e9fe3f2d1d3172cfcc7e6eb7ea9b237e5f093443b02f42b4ea85673a6a0000ef9a6ddf2263a1a75eb7b78d3f90b1de91a22d78aa06d9626b0f9090ee63d92418b084db647132e3b0b7f3ed583eec280d06b94a358daffcd333233fb390ba8bf2da3921b18adf6cd4901cbabf4f4e3c90f21eb3190c8c0e4f16ac25dd546859bc640354ab9769553aeda466191ba4b10f52da5342347685e52af5d20ba8c113e65663bcedc12c99e576c4e1bdde013017d16fc26e3f30418c21d72ad6507ef1c8d437342d0fc20ad102e6c49eb9a8e7a3df5366c9a75b6d95ab007a3d93bca0086414ac5bc44a872659f43f0f703b415ac0e9aeeedb2c0cb945938923dc0865c5d3ff673e068d2865b11c68774cd6c0be1caa40627425bdc4ecbbd0a642f9c6953464e2f20681994be8483d64ed6d2d8efa79c5b12776900e58b45bea18c2e0220ea27e9670485e2c6ce9f52ac08cad61ca57b839710209db8a5d4fe95a846960b126271e519545e5d4d15300fc0ef2f35b8def7ba6f639d85404b57bac35140bef1c4f3b455773bf2a2eae62118dcdc5474ec0900aca49300833417786d1fb0d76a56570cfa10d23dbab0305e1c9c48032b19fd2ec2e00b1528a248036590e26e8c75209d004e20bc7730b29cf3ed860848b83ab91ad6a635f2cc1eca89e16814f34f2c1c2766a28b2901170bb4839f08f5685e593b8a5ce2a801194818b4aeb0a01794f92c7bc4144808e997cfd1711485b60483cf4a310b23e0210c5c73e6956b9e1ae696f1ea79b3fab788bf229a349fd9caedebd5db99821b1cebdceafb011cfcd1c78f93774b35f25c7e69952b0bca1a0d40791b812614e996ca31548bc0000000018cfdaa0e005288781053297011220757d23cec40eef9728417e68a5c6fb70b35e4fe58d34da55f305b013d24795a91802226b483045022100ae926c96c746308e7488e022f4ad1db94d5d0c8683f6fa6ded3afb13d8e20578022074aa8ebfe20adaf25beed70c60cfb5007278bec17875f11f7a5b3c86eb60a96901210391abdcd113c40b56f13a548e8624d6ad8d7a162b33ef81020d046cadbda2663728feffffff0f3a3f1a1976a914d79c1c8b67a0275c60e33b67bbd0e19a79b9276388ac222251477652524658424d32666558755533394577484e75595953726e4d33486155794d3a2910011a256a23426c6f636b73747265616d2e696e666f206973206120636f6f6c206578706c6f7265723a060a02048010024002" ) func init() { diff --git a/bchain/coins/litecoin/litecoinparser.go b/bchain/coins/litecoin/litecoinparser.go index 6780054047..e95ca8d443 100644 --- a/bchain/coins/litecoin/litecoinparser.go +++ b/bchain/coins/litecoin/litecoinparser.go @@ -45,10 +45,12 @@ type LitecoinParser struct { // NewLitecoinParser returns new LitecoinParser instance func NewLitecoinParser(params *chaincfg.Params, c *btc.Configuration) *LitecoinParser { - return &LitecoinParser{ + p := &LitecoinParser{ BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c), baseparser: &bchain.BaseParser{}, } + p.VSizeSupport = true + return p } // GetChainParams contains network parameters for the main Litecoin network, diff --git a/bchain/coins/litecoin/litecoinparser_test.go b/bchain/coins/litecoin/litecoinparser_test.go index 94ed68ba59..9db208692a 100644 --- a/bchain/coins/litecoin/litecoinparser_test.go +++ b/bchain/coins/litecoin/litecoinparser_test.go @@ -224,7 +224,7 @@ func TestGetAddressesFromAddrDesc_Mainnet(t *testing.T) { var ( testTx1 bchain.Tx - testTxPacked1 = "0a201c50c1770374d7de2f81a87463a5225bb620d25fd467536223a5b715a47c9e3212c90102000000031e1977dc524bec5929e95d8d0946812944b7b5bda12f5b99fdf557773f2ee65e0100000000ffffffff8a398e44546dce0245452b90130e86832b21fd68f26662bc33aeb7c6c115d23c1900000000ffffffffb807ab93a7fcdff7af6d24581a4a18aa7c1db1ebecba2617a6805b009513940f0c00000000ffffffff020001a04a000000001976a9141ae882e788091732da6910595314447c9e38bd8d88ac27440f00000000001976a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac000000001890d5abd405200028d3c807322c0a0012205ee62e3f7757f5fd995b2fa1bdb5b744298146098d5de92959ec4b52dc77191e180128ffffffff0f322c0a0012203cd215c1c6b7ae33bc6266f268fd212b83860e13902b454502ce6d54448e398a181928ffffffff0f322c0a0012200f941395005b80a61726baecebb11d7caa184a1a58246daff7dffca793ab07b8180c28ffffffff0f3a470a044aa0010010001a1976a9141ae882e788091732da6910595314447c9e38bd8d88ac22224c4d67454e4e587a7a755078703776664d6a44724355343462736d72454d677176633a460a030f442710011a1976a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac22224c563142796a624a4e46544879465171777177644a584b4a7a6e59447a587a6734424002" + testTxPacked1 = "0a201c50c1770374d7de2f81a87463a5225bb620d25fd467536223a5b715a47c9e3212c90102000000031e1977dc524bec5929e95d8d0946812944b7b5bda12f5b99fdf557773f2ee65e0100000000ffffffff8a398e44546dce0245452b90130e86832b21fd68f26662bc33aeb7c6c115d23c1900000000ffffffffb807ab93a7fcdff7af6d24581a4a18aa7c1db1ebecba2617a6805b009513940f0c00000000ffffffff020001a04a000000001976a9141ae882e788091732da6910595314447c9e38bd8d88ac27440f00000000001976a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac000000001890d5abd40528d3c807322a12205ee62e3f7757f5fd995b2fa1bdb5b744298146098d5de92959ec4b52dc77191e180128ffffffff0f322a12203cd215c1c6b7ae33bc6266f268fd212b83860e13902b454502ce6d54448e398a181928ffffffff0f322a12200f941395005b80a61726baecebb11d7caa184a1a58246daff7dffca793ab07b8180c28ffffffff0f3a450a044aa001001a1976a9141ae882e788091732da6910595314447c9e38bd8d88ac22224c4d67454e4e587a7a755078703776664d6a44724355343462736d72454d677176633a460a030f442710011a1976a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac22224c563142796a624a4e46544879465171777177644a584b4a7a6e59447a587a6734424002489101" ) func init() { @@ -235,6 +235,7 @@ func init() { Txid: "1c50c1770374d7de2f81a87463a5225bb620d25fd467536223a5b715a47c9e32", LockTime: 0, Version: 2, + VSize: 145, Vin: []bchain.Vin{ { ScriptSig: bchain.ScriptSig{ diff --git a/bchain/coins/monetaryunit/monetaryunitparser_test.go b/bchain/coins/monetaryunit/monetaryunitparser_test.go index 47b5a42333..c4dc8b7e55 100644 --- a/bchain/coins/monetaryunit/monetaryunitparser_test.go +++ b/bchain/coins/monetaryunit/monetaryunitparser_test.go @@ -150,7 +150,7 @@ func Test_GetAddressesFromAddrDesc(t *testing.T) { var ( testTx1 bchain.Tx - testTxPacked1 = "0a20f05ba72a05c4900ff2a00a0403697750201e41267aeea8a589a7dc7bcc57076e12d30101000000010c396f3768565c707addf85ecf47e04cefb2721d95afd977e13f25904de8336a0100000049483045022100fe1b79f38ca4b9dc2fbc50eac6c9bf050ae5a3ee37da05b950918230eb0a8c7c0220477173b60ec00a8b4b28d5db9fc5d8259eea35f91bbdd60089c5ec1be7b05f3b01ffffffff03000000000000000000dd400def140000002321025d145b77df04c40ceb88ea36828755f8275dc5fecf19d3ecccce2d8198c3407cac00d2496b000000001976a914afe70b2e1bf4199298ed8281767bae22970b415088ac0000000018e6b092e605200028f48d1b32770a0012206a33e84d90253fe177d9af951d72b2ef4ce047cf5ef8dd7a705c5668376f390c18012249483045022100fe1b79f38ca4b9dc2fbc50eac6c9bf050ae5a3ee37da05b950918230eb0a8c7c0220477173b60ec00a8b4b28d5db9fc5d8259eea35f91bbdd60089c5ec1be7b05f3b0128ffffffff0f3a04100022003a520a0514ef0d40dd10011a2321025d145b77df04c40ceb88ea36828755f8275dc5fecf19d3ecccce2d8198c3407cac22223764396a4e79716835694b555a566e516a6d7a6d4541513878444b777a4536536e673a470a046b49d20010021a1976a914afe70b2e1bf4199298ed8281767bae22970b415088ac22223769536a6e57436f41556d4a347656584d6b61457561736e5658537a5a7166504c324001" + testTxPacked1 = "0a20f05ba72a05c4900ff2a00a0403697750201e41267aeea8a589a7dc7bcc57076e12d30101000000010c396f3768565c707addf85ecf47e04cefb2721d95afd977e13f25904de8336a0100000049483045022100fe1b79f38ca4b9dc2fbc50eac6c9bf050ae5a3ee37da05b950918230eb0a8c7c0220477173b60ec00a8b4b28d5db9fc5d8259eea35f91bbdd60089c5ec1be7b05f3b01ffffffff03000000000000000000dd400def140000002321025d145b77df04c40ceb88ea36828755f8275dc5fecf19d3ecccce2d8198c3407cac00d2496b000000001976a914afe70b2e1bf4199298ed8281767bae22970b415088ac0000000018e6b092e60528f48d1b327512206a33e84d90253fe177d9af951d72b2ef4ce047cf5ef8dd7a705c5668376f390c18012249483045022100fe1b79f38ca4b9dc2fbc50eac6c9bf050ae5a3ee37da05b950918230eb0a8c7c0220477173b60ec00a8b4b28d5db9fc5d8259eea35f91bbdd60089c5ec1be7b05f3b0128ffffffff0f3a0222003a520a0514ef0d40dd10011a2321025d145b77df04c40ceb88ea36828755f8275dc5fecf19d3ecccce2d8198c3407cac22223764396a4e79716835694b555a566e516a6d7a6d4541513878444b777a4536536e673a470a046b49d20010021a1976a914afe70b2e1bf4199298ed8281767bae22970b415088ac22223769536a6e57436f41556d4a347656584d6b61457561736e5658537a5a7166504c324001" ) func init() { diff --git a/bchain/coins/namecoin/namecoinparser.go b/bchain/coins/namecoin/namecoinparser.go index 9ec24c0826..9cf89e335e 100644 --- a/bchain/coins/namecoin/namecoinparser.go +++ b/bchain/coins/namecoin/namecoinparser.go @@ -34,7 +34,9 @@ type NamecoinParser struct { // NewNamecoinParser returns new NamecoinParser instance func NewNamecoinParser(params *chaincfg.Params, c *btc.Configuration) *NamecoinParser { - return &NamecoinParser{BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c)} + p := &NamecoinParser{BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c)} + p.VSizeSupport = true + return p } // GetChainParams contains network parameters for the main Namecoin network, diff --git a/bchain/coins/omotenashicoin/omotenashicoinparser_test.go b/bchain/coins/omotenashicoin/omotenashicoinparser_test.go index 7359753224..1bf03c097f 100755 --- a/bchain/coins/omotenashicoin/omotenashicoinparser_test.go +++ b/bchain/coins/omotenashicoin/omotenashicoinparser_test.go @@ -151,11 +151,11 @@ func Test_GetAddressesFromAddrDesc(t *testing.T) { var ( // Block Height 600 testTx1 bchain.Tx - testTxPacked_testnet_1 = "0a2054af08185cf5c5d312ebd9865b4b224c6120801b209343cfb9dc3332af28a2a5126401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff050258020101ffffffff0100e87648170000002321024a9c0d55966c7a46d8ac15830c6c26555a2b570a3e78c51534ccc8dadc7943c8ac000000001894e38ff105200028d80432140a0a30323538303230313031180028ffffffff0f3a520a05174876e80010001a2321024a9c0d55966c7a46d8ac15830c6c26555a2b570a3e78c51534ccc8dadc7943c8ac2222616e667766545642725934795a4e54543167625a61584e664854377951544856674d4000" + testTxPacked_testnet_1 = "0a2054af08185cf5c5d312ebd9865b4b224c6120801b209343cfb9dc3332af28a2a5126401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff050258020101ffffffff0100e87648170000002321024a9c0d55966c7a46d8ac15830c6c26555a2b570a3e78c51534ccc8dadc7943c8ac000000001894e38ff10528d80432120a0a3032353830323031303128ffffffff0f3a500a05174876e8001a2321024a9c0d55966c7a46d8ac15830c6c26555a2b570a3e78c51534ccc8dadc7943c8ac2222616e667766545642725934795a4e54543167625a61584e664854377951544856674d" // Block Height 135001 testTx2 bchain.Tx - testTxPacked_mainnet_1 = "0a20a2eedb3990bddcace3a5211332e86f70d0195a2a7efaad2de18698172ff9fc6d128f0102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1803590f020445b8075e088104e0b22c0000007969696d70000000000002807c814a000000001976a91487bac515ab40891b58a05c913f908194c9d73bd588ac807584df000000001976a914a1441e207bd13f80b2142026ad39a58b5f47434d88ac0000000018c4f09ef005200028d99e0832360a30303335393066303230343435623830373565303838313034653062323263303030303030373936393639366437303030180028003a470a044a817c8010001a1976a91487bac515ab40891b58a05c913f908194c9d73bd588ac2222535a66667a6a666454486f7a394675684a5444453847704378455762544c433654743a470a04df84758010001a1976a914a1441e207bd13f80b2142026ad39a58b5f47434d88ac222253627a685264475855475245556b70557a67716877615847666f4244447565366b364000" + testTxPacked_mainnet_1 = "0a20a2eedb3990bddcace3a5211332e86f70d0195a2a7efaad2de18698172ff9fc6d128f0102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1803590f020445b8075e088104e0b22c0000007969696d70000000000002807c814a000000001976a91487bac515ab40891b58a05c913f908194c9d73bd588ac807584df000000001976a914a1441e207bd13f80b2142026ad39a58b5f47434d88ac0000000018c4f09ef00528d99e0832320a303033353930663032303434356238303735653038383130346530623232633030303030303739363936393664373030303a450a044a817c801a1976a91487bac515ab40891b58a05c913f908194c9d73bd588ac2222535a66667a6a666454486f7a394675684a5444453847704378455762544c433654743a450a04df8475801a1976a914a1441e207bd13f80b2142026ad39a58b5f47434d88ac222253627a685264475855475245556b70557a67716877615847666f4244447565366b36" ) func init() { diff --git a/bchain/coins/pivx/pivxparser_test.go b/bchain/coins/pivx/pivxparser_test.go index 3931f2947d..94e3f1e164 100644 --- a/bchain/coins/pivx/pivxparser_test.go +++ b/bchain/coins/pivx/pivxparser_test.go @@ -122,15 +122,15 @@ func Test_GetAddressesFromAddrDesc(t *testing.T) { var ( // regular transaction testTx1 bchain.Tx - testTxPacked1 = "0a2052b116d26f7c8b633c284f8998a431e106d837c0c5888f9ea5273d36c4556bec12f501010000000188557c816acd0a61579b701278c7dde85ea25d57877f9dbc65d3b2df2feacc42320000006b483045022100f5d0e98d064d5256852e420a4a3779527fb182c5edbfecf6143fc70eeba8eeef02202f0b2445185fbf846cca07c56c317733a9a4e46f960615f541da7aa27c33cfa201210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73ffffffff03000000000000000000f06832fa0100000023210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73aca038370e000000001976a914b4aa56c103b398f875bb8d15c3bb4136aa62725f88ac000000001883a8aacd0520002880ea303299010a00122042ccea2fdfb2d365bc9d7f87575da25ee8ddc77812709b57610acd6a817c55881832226b483045022100f5d0e98d064d5256852e420a4a3779527fb182c5edbfecf6143fc70eeba8eeef02202f0b2445185fbf846cca07c56c317733a9a4e46f960615f541da7aa27c33cfa201210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae7328ffffffff0f3a0210003a520a0501fa3268f010011a23210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73ac2222444b4c33517a43624a71724870524b4148764571736f6d7344686b515076567a5a673a470a040e3738a010021a1976a914b4aa56c103b398f875bb8d15c3bb4136aa62725f88ac2222444d634e45393855667571454b32674746664b4234597057415771627748524154484000" + testTxPacked1 = "0a2052b116d26f7c8b633c284f8998a431e106d837c0c5888f9ea5273d36c4556bec12f501010000000188557c816acd0a61579b701278c7dde85ea25d57877f9dbc65d3b2df2feacc42320000006b483045022100f5d0e98d064d5256852e420a4a3779527fb182c5edbfecf6143fc70eeba8eeef02202f0b2445185fbf846cca07c56c317733a9a4e46f960615f541da7aa27c33cfa201210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73ffffffff03000000000000000000f06832fa0100000023210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73aca038370e000000001976a914b4aa56c103b398f875bb8d15c3bb4136aa62725f88ac000000001883a8aacd052880ea30329701122042ccea2fdfb2d365bc9d7f87575da25ee8ddc77812709b57610acd6a817c55881832226b483045022100f5d0e98d064d5256852e420a4a3779527fb182c5edbfecf6143fc70eeba8eeef02202f0b2445185fbf846cca07c56c317733a9a4e46f960615f541da7aa27c33cfa201210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae7328ffffffff0f3a003a520a0501fa3268f010011a23210251c5555ff3c684aebfca92f5329e2f660da54856299da067060a1bcf5e8fae73ac2222444b4c33517a43624a71724870524b4148764571736f6d7344686b515076567a5a673a470a040e3738a010021a1976a914b4aa56c103b398f875bb8d15c3bb4136aa62725f88ac2222444d634e45393855667571454b32674746664b423459705741577162774852415448" // transaction with OP_ZEROCOINMINT testTx2 bchain.Tx - testTxPacked2 = "0a20599d5d797a4575eb25e1c291c0e7630bd6fdc0e6ec5fa9b14147f929a4e41bf212ae020100000001b56a0fe242a8de7dcbb58ae1009e44e7f2ec25a65eeb8b815cf53393309741ca0100000049483045022100cc208a59341dca98207ec8a4a42c014d435192694a77c69d40e51467800c0a0802205ac1782d4ecefa260b33340d92c2ab2396b43c1073a67b4180aa8ef2aede8af801ffffffff0200e876481700000087c10281004c816f5ce1eeda911203319a256e8560c8dbfd47b569ff32c27559bda78854e63e49718ce43036e5120dce357b5630afd745d399f91e675a921adbb45224a6661656217fcfe32396fb25609b724646759116326964f2f1f7ddb7c340dc24be2b75a0a9dc05ca2fdf805c03c7a04d972456beb82a51de73d8842b39a553919dfa5d8e003e98dab7210000001976a914dda91c0396050d660f9c0e38f78064486bbfcb2c88ac00000000189dab96cf05200028b8dc3432770a001220ca4197309333f55c818beb5ea625ecf2e7449e00e18ab5cb7ddea842e20f6ab518012249483045022100cc208a59341dca98207ec8a4a42c014d435192694a77c69d40e51467800c0a0802205ac1782d4ecefa260b33340d92c2ab2396b43c1073a67b4180aa8ef2aede8af80128ffffffff0f3a93010a05174876e80010001a8701c10281004c816f5ce1eeda911203319a256e8560c8dbfd47b569ff32c27559bda78854e63e49718ce43036e5120dce357b5630afd745d399f91e675a921adbb45224a6661656217fcfe32396fb25609b724646759116326964f2f1f7ddb7c340dc24be2b75a0a9dc05ca2fdf805c03c7a04d972456beb82a51de73d8842b39a553919dfa5d8e003a480a0521b7da983e10011a1976a914dda91c0396050d660f9c0e38f78064486bbfcb2c88ac222244524d38546169593338716348626764797470386f455472656f62424c48747065454000" + testTxPacked2 = "0a20599d5d797a4575eb25e1c291c0e7630bd6fdc0e6ec5fa9b14147f929a4e41bf212ae020100000001b56a0fe242a8de7dcbb58ae1009e44e7f2ec25a65eeb8b815cf53393309741ca0100000049483045022100cc208a59341dca98207ec8a4a42c014d435192694a77c69d40e51467800c0a0802205ac1782d4ecefa260b33340d92c2ab2396b43c1073a67b4180aa8ef2aede8af801ffffffff0200e876481700000087c10281004c816f5ce1eeda911203319a256e8560c8dbfd47b569ff32c27559bda78854e63e49718ce43036e5120dce357b5630afd745d399f91e675a921adbb45224a6661656217fcfe32396fb25609b724646759116326964f2f1f7ddb7c340dc24be2b75a0a9dc05ca2fdf805c03c7a04d972456beb82a51de73d8842b39a553919dfa5d8e003e98dab7210000001976a914dda91c0396050d660f9c0e38f78064486bbfcb2c88ac00000000189dab96cf0528b8dc3432751220ca4197309333f55c818beb5ea625ecf2e7449e00e18ab5cb7ddea842e20f6ab518012249483045022100cc208a59341dca98207ec8a4a42c014d435192694a77c69d40e51467800c0a0802205ac1782d4ecefa260b33340d92c2ab2396b43c1073a67b4180aa8ef2aede8af80128ffffffff0f3a91010a05174876e8001a8701c10281004c816f5ce1eeda911203319a256e8560c8dbfd47b569ff32c27559bda78854e63e49718ce43036e5120dce357b5630afd745d399f91e675a921adbb45224a6661656217fcfe32396fb25609b724646759116326964f2f1f7ddb7c340dc24be2b75a0a9dc05ca2fdf805c03c7a04d972456beb82a51de73d8842b39a553919dfa5d8e003a480a0521b7da983e10011a1976a914dda91c0396050d660f9c0e38f78064486bbfcb2c88ac222244524d38546169593338716348626764797470386f455472656f62424c4874706545" // transaction with OP_ZEROCOINSPEND testTx3 bchain.Tx - testTxPacked3 = "0a20b65181decb00e684fef238776a0a129db4e1ffdfc454f6ef323e5f7a8deae6a812e1ab0101000000010000000000000000000000000000000000000000000000000000000000000000fffffffffd8a55c2028655e8030000dc8ce67bfe1851477371a9ac40b6ae0cb8571f6e2d5285855288f6079f1ce7239ee8c85f465b1820058b79554f41af297e9caf95ce0084b7c35dea0b95e15a2fb9f8e62c5427c1c36120cbc1fc11ff344909079335209c6b84b45a9211cac960f64e9432ba5eb6e4ecb2068223dfe3d85b345da17bf374f9140c9577c148bcc431c9ec3c7d13bd2363dba821381ed9fa0614416261e88330b3e74c40e6561310eab3f26f092e72f3cab761f373d02680dfb52937bd9515be242f6573754f8f665523cce3bd606c8ad190954f8181577fd0efe7cc64b711d03774958df4a5211e44870302056557777951d7ff8c002161a6a59e979f05469cb31770bd484be6525625359979220eb7e9912e835065fb00fd000216aefbae3525166510814d1636b76b0d48ea3cd54a3a17b136a84340989d75f74ff952966830e4c0d59daa006d5a7190978270ee9475a0778afaf002cdce7efdbfad630f72838b5c4a3b538ba61b94bbd9e353437a50725af5f16fbcbf36bb34e7da54e5c24dfc90b545f95c973877bfadc2703ee10585a1fc1c97d7377bf41c9cbcd5a313849a3c826e7c1301083694e6dc05f46899a901ab4a8d7f6b3600df280157fbef6eca4c28fc610957a42a9acf7c4d7f9846ab6b9b04fa6abb5fefc168d45f10078b97d4d6a39638588a1c19e1bfc472657861a902c2d52cd32fb0463746f649ae88bc0602dbf35816fbccf91dc249be809160cbc7f8b6702d6cc5b81fdebd283231f40758afc899f6fecedc51dc4e5d09cb8961092220541f75ddad45680ea92b4ee78c29f58c197a68420bbb25b450c72d02d7249f7facf9927378620eb36fbf9b4ccbcb55627eb9cf905b4a4c65fcb77a537f642f10901b6e94afa37e4afb0d6d91194454a9c2dd8ef8fe4316f8594c7822a7d58cab09657cf501da5be5a44f947bb957b71e4291a7fc60cd5cef9f0676f7c89123c7ff1ae2e6dc001b6f19785534e207fed2bade8597541b13714284f67d6986bc616ef1b0adbe415242fee85acbf482a6a48b3f142ef7ddb5dd1c97a4b0c53c6ac7aceb8c042d9c9ada1bc986b8c276d07fbf8512a3dae6a357fc02b167eb85000040e8693e45fd000200e23abd27b258f9827ed58545a507bd465e255e1156610da314bf7df68b6b55129df84c7b19e362751ebb9beba10790c9c26c5ddc7f087258d81b006c0d2e92be0178bf5edf6e78e89f73cd97746afbb2551dbc97eafe32ae62e7f9ebcd14ad69faf74d2011d16f2c50775f4f499c87c3c50d9d5d486394c2a7f462675d2a4885493332e0610a78fc0c8b08eda42e4bfe93b8c7f80a911a7992a1deb7cca2e40933e1559815688d4e5ae5e58d706bc513e5108449a8393928b5b77ae73cb03fe212c6375b6c5e61fce9db16360a147e7f7fcd49e05a99711d4d5799be77f7e39d9d1397388d6680d4931b48798ce013256a586781ba80168bae63bed4d150b64a73f7d0ab0c9ebb42f5d4db40eeee303783249af4bbf334c660f8c084ed9a2e5fff8be230940a4a08b59418676ef005192365e4e67757288791ce4992b903a31537596cf6dad0be2af2418a6b9cc2c33e99d874168f6a29df189a869b16eb5d24400ab30e4eca9274114d646aaaa8bad45832b6c0ded2bfa698939a8af9d0af380d2afc58966afe0f45483ecad0f114b904cc2fafcf470dd4fb8f193795e8afc3243b4c946d5eac82babac6feaf4ff10bf53acdf8347fb9fb7a5ac4efcf160f0a7ef3576f439404a3078ea092f46f408a955c965344023d847fad7374cf145cadbc8348eeb2c5aa999ebeeb8a5548bb14e0092b184caee354020c19d66cb213fd0002729bdb186c4c494e17fd6effe29cbe7fbc539caca738d54f9ffc6e52a35a27da134192e2f7f4ee2a86af281b78670e662677e97a1ef008f10f42349fa83bf7841d88b1a457d38164a383ae9c6b974137d58216f22d135d30b9d6e7a74952a7e905f385141f4df088415d704cc3b03b2cd600cb5507f8ea1b53fc0e73031a3946c0d6269f020c9c26a3be3bfdcd37f8d3b9fd42538ebd72029fa0bd8eb57a4fe6769e1b43b5d5d7be311e12e52ee9dad67aa988dedc80ad616d7540381993d9de91a7fac6d08e4414254b9d1d72940fec032833a6b1a5605f4b62c47a86d70dbec5ec913d0a613d438cad385fedf24566bf79edd17238e55421520b95772224623f145100e663b2ba20161784f688afcf07a900ade1d48060d21be9ba9297697891c2584cb99a44868efbdf65178592ecfbadc92f4883662d6b21b7f266eb21815c7401b8e7da061e3258dd685f8cf65f2c2e407c913f85d053b05f6f92ed1299186632ddcf175ccbbc933044bdac5e10916917dea1146f77a8ba4b4fc8ce260b5deed395ecae9b81baa6b385fecca5d2982041c131ce02a1dec517ad2d459434aa3a514e7a4c6c1362401b1ab62b4c89bd7705d5072e0be5250c60c2fdd946bc73050d3b8bcfaa73165eee3660063f279e824d1e15f87307a40bc9e1ccc0f7d7087ba84fe9275742455241b61d3687d23eb9d7a9cc18072ed8be1492db46a454464090750eb0a393499a73d23f4c552ec6ae425a77f97d4285a7f287066b2198bfaa99073da6f4009755e59838e48cd5fe692962b87da3ea7e14b34b352fb2a4673eaaaa594a094610bd0cc566acafc21891b7b0c2470bbb338f579231e01c064f275c5ac9c2748cc50e7f2e36f2768d2a59c22d14b6b9a431f7772e716731c55ccbf086187fb15bd07a5af3040d468d4088ba9e6383f1df6dead9384758f2da81ee96370d9055ce5a0db56bbdccb57ab490f42a01e083b61c5157b3c00e2011dda865c7294cdfd2be5c03a1a36a4deda9cf03b500fd0101b3c97046a717a2f38fe2265185f4411cc68cce6cf9885a7a8fe6292eb9e3eca69fb6249774a8c82b888d22d5fcdd549846c3ebcf054c6bf07aa4d6b1c0d4d9bd8e16501f65373ceda249e9c760848fc86ea92fae7142d211c8bb4a287c91eb08cef7678ef1f445f76f81d464eb1d29ce5c6d6286d73d49cd0ef03b65376eb146a3ff69a487ec90b53c11cce613a586f22cd56fd34df6f7ad64fa3c68a6ae5c9dad99d4a3d2b0974ea1f627be4f0153c7b5fe472d0c562556c4d8d1c7c592bea45bb7886b74f8639f9487b2f6aebf4848b5718f2ce65b8f5a4efcf140e2857bc9c503f0058f9b6af16e75f2d530fbba8979f81569e6cc0bc04a54e30de4e03a9300fd00019b881d73a78b86771d41c0c2a9ebdb3899727f33d2d4d81dd27b6a00b4c7db265999b8442800750abde7cd97be0c691ed06b5d40da115546d90d4803e82c61d43eb5e2bc684adcf180be47660870921fdedb2ce43f33564541fc0debe175c6c49bdfb51378902dc709594b9b7d34d0af70b67c3c608aeb5185e78c1c39cc3080b71a36115f623a07a4e1a3e1f3e17b6f9f695f1a1acd9dd1319d0a0d67b337e64f720e5168c09196244bc71b083f302e042be19b6aa1f8ad61755f4883c3a1ae615252b884ca3cca5e18a023ad6725f08f9e0ffd60e7a73ccd29afc910d60dc99c06f5953c3e398ef615fca45f6a83f8a0be653d31a7e1a1666cc7334d9cad51fd00016f7efa38c8f9f478a6ea1217d8be9b7f0b01c5d1fa483c26e403eb3a4875aeebbd0e7eb8aab8472a5bd80e8be38df13526042952f813b71f8aeb4a2281bfe5e5d9ba70f7e4c9f477706da922899f505dd172e260ce5f008b59c0590d498ac50810e9a38d35f1a2ea4e9ce8f77e46d0b5604dbf94629cfb8b65453a0295eeca9d992365309b7956481a8c5080510a09183bd3358fb26933e15c83fe2ca6d186e631d72889a09464f5dafdc8a93dd100329071e52beb522bef1af0fbae0516ad3b02011e19a2b2924791b3f22679b78039c8356c0e6676e2451487f056d0cff064f55a992afba08f59af7606a809394772ff85c4c40673dd54eb30020c6a5cde3cfd0001ecbc47845e4a1f3c05c4e9d0a47e5e8996326f48d7ee1bd0e432c5f33fecf8a94feaf03f2da65525bbcb119c7928456c28f31c183a21af1acfcd9615669cf47a077f861f694bfc1831f1a71ab66540906c62b85274f63abcc53a37e3fccdc8563ca2153818b6b796473848d765d1c81d4e6f78ef8fce804184e04664c5c6af7c3abe4d92aac3f6ea99b84a3e53a7bf7679c26dc96804ac9e1443b054e3c55fe89315106a14646d06b84122861ac0ac27fe64fbee3c9807b0264a90eb9602187f4df2cc0c62b025ba70bbb30a7e43d533f32546d2e6f97537bc2d623a7f545676a37cb511614037d77fe21a35ce4a2275e7ea1d85b7bc19e477c61033f5effe7fa5d8ff48e2873845157b7a29718e6692704a9d864301fa254798555172c6277cce4ed5aebdff9c27a1bb37071677c16da15d6c1547c5b708e1988eba68befff1f1743342ad7cb89ec8731b567198953d965ef6e395d38b410a326ef3e262937c763179f22a076d17f848dc4e36be38d838d5788f121d771f23209a0d50fb3d016c5cbc47cec31069f7d22bd78691662d8b609932b9533bf828e213e5ed4e61f2b80f05acbc00fda0017bdc59c8a6a23d261cdf1dd10e91523503c3375a803755c29b6a84bac626708b7db937ed39f4053b5c6376b3988fc9388cc64dc07466c67704a32b703d5dd86d8fb8597cc2ff7ca190044c66638028855f29fdf2a0943c64125e3ad2487de6928bc34088957eb32d843613e6b588a91cbfd8c37c24ba655739cefdb203bc3061ff93498160c7e949823b0947c68b6c51dcdf038c538f50413266680ee23817ed9cb840e0094f6fd2277012aa2c6f82b086242e6332a4e00bb9c12c153dfea9340e681e63d72551f8830b2bb2587e5937685252928894bf90bfa174f62bf7ccd43415a094bb4142fcfe639c62f6eda0e4d7ee033f49f51eaa6a35b0f7f400992d8367a275e9d018a547430083c41a35ef543bb92e159efad39c5b84d127bc68dd581c308afd5f81654ffcdb3317dd6e21d5251916214e873c83b6ac197dbac6c1b93d79b9dd5da090be204b9765fdcf662c9296295610e42a0570a1503dd67c44e17936946f3a6ce61f82b13cf00a0b47d06f2cd28651b6af32ffc58304593d5ce81159ccc952ed980f93182fb468ebccadae4dab4565d64a5bcb3aec7a09d681fd24016a4905a3a143e4d61b16898fddbb0f9d7602ce865716b62ae4f9a37e2f6ab89de930e066db6f4a8667ccc4e79e7ac760642c33e5f24266540c9b4fbadda4c0aa1b6a74d02bdf2324ebf9598d8ba918438de1e3343be0b057925bdd52304581ef621fd085dc55cf8b45d605cb0b60047bfa935c2968d554753a615e75f24086b4e40508ecdeb411ed26c007a1110f3e73f504d7fdefb275cbb59cf9cd68bf4784b8845467fac90275f3bbcc2c14a87fbbd7d111441d6ba0833b9045db43975317aee170242b291f8b07254d395472bd4b67db7576bcf2460bf0c182f745a6cbbec3f680b7c6e0a85308bc3af8af3302355757a77a2fe3f98350e4ea1b3074e37a638c630d529141843583ba4b802e089e0a7ecaeeeb42079e072e64fa5251782cbc67ba9d46e4c7f502d44a06e5212d09f4dc5bef1f1dfc376d4a042f608b860971c44caeb3735cd57e19401314af06a73180918af7693ec5204b3f858806e6919af05a1a8c6daea2ee3f08fd2401c082f09f7042a7a0b6b484287050a15f5d8c1011c200d42eb51aff5a484fe1de9feaa264ed3022b6f5b1b54a4a316d3d7e2635210ad83e2d3c497bed46f417ed22804682529b034925dc785a2d74361d1db395d1681d71bc1b0635908ec3e92f850577f35912fe5c173402e6e2ed8003c64a57bb65a2013014a3ce14ccc725f733a50457a696396cac0a551cdda03a2ec81597031feaddd801a9bceaeb5f862a4cdb3eda06dc317a96b29c27d78dd977cc6f25d62bb967814fd1d7e87c675042522c904fadf1cff80289374de8f98df511de975011d877058aa7ea9cdf186ccfa5cfa5258e581ee7ee73e16dcfaa82f079a16c95e6ca4f49c037b2423c12006b549a0b80c5dc9b93685fd2a4bda99e5aea74eafe9d28149e94adc538198d459c7a9a45a1fd24011c69aaa26aa94954fb7dfae2d63eac286b4979e7d513eac8144a7bccc34fb298b7c556a82e7450bf590f1ae658c474ac7c12b3fccaec877b6bdd11fd9655a27b7b69b922b1629a24a8d7f81a6827dd22cbab62d121f5cf96d59be904022360c5bf04d2435c52541ea4d7f932e19fc471a0afb38f2e84668d974c57c963346d790a670a699ff53b5557def1ce9650a8624bb2065f9ce99d6b5361a1b39629040fc897a5d0a07816618840f86601508e90198de64d7091a8bda406009948fa9ebbf2f56adc66e057ab23152504438062867c4237ce2b99c020add16bf66a0c06a072c4fabd9b9fd1146b73366535d934328188798ccc18ad37d30e06a39b8e14468d32820d912359323b7d474dbf507e894a448a4e921f70d1fb4d4ac03b178ae157814004fda0015c202cbc492bde212955236761fcad7de9ec8932946b5352f6c35a242d39b354a1f96a706bcb84174a5b11a7e51cf0adeec9c1e5c88a3f8f32ca81977db668650734398e6feb45cbe0ed5c5d8f77cfa67b78f8c7c856629e6731321126d12ebe70603fe28d4281f5c9165e60749bc8688d7ff3cd509d958dcd1ac7b068a3548af0a3e20b984bf4406a6c6d55c674bc83240757e3a0515bbb626b6b6b863fa7029c5043d67ebcd531601d39b8b6b7e507a176216a264fb80f574d7a6a587d5ebba7c355007630fc49127368f6b672bd12fba956db75f189bd438f5034badc04d396b04a021608a7888434eefffd082efb0c3196698d1b015b42eb6f5a2123c085d37841c0cd6c7a1d77c55d61d76426758cdadcad3d7b1037b5d94c29962d4fe218c9f98c89ecdd9a50a48bc534bc167d536e11906bf594876aea01683269255701b705ac454b250abd45d10f4f8b881c6f4a360014b450132d750099100e15c70ef65ab0eeea340604f3c7b3eb3c51292036a6cc2843989ea418631fb595ca9d7e23a88a3a7d0a8dd5ec1212fc786dcf7107214d5c4d3f2cffffe2b1fbef09816033fdb616b9318b80b32f045ec1bec678dd1500480154ccdd87365d602f0126b57015784abee6fdb9b7f22ba135cb882dfa738baa17233fa53c68d35e4102f185c07a1310b710ff2d63db7238d36487ab504e64e239d0641137cd75cb282e831621f101663efb2f9de2520049c0d08a7c965477fef9d575ba31e101faaea20f96e40046132cd3aa981e8f360cb6a9bac68844d07c7d1741e56b104f634fa1a0c31d64ca4526de4f1754effc40aa04b9dfdb6f53ef77540d66fc9d0a4058fb46518433a1467a71873a03600b47c81e0d452cc44728e3a625235c84f7f62d753faf91fdfa1aea056925168616516e5c4357a7e82c94127fe8a4626c868bb7bcc13a0422e15e31c82935a4e1b9cac496bb456d1318e2e3e704627f1dbe646f5f1590283833193cb03f28e00f5020fbd43c5fdc39fd35498cf4fb7471417e814974331500d8e4ec8af34af39b457d20843d897d0e015456600325ef702c1bdfa7bbb3f2e7a74dcd8fd77beb3df4022320108d0f48a7a9b2a32e1587eb01094e20cb7c002d3de08f481a1d7cdc6b3f9c7c20185bc7ee65a12aa6003a58b83aa90d90baef64c7d324c662ea5138fbb4bd063720f8f9111e20def54a90755537e44fd506737cf1fcc4d0b320bef16eedbd750f8b2002e3af2f477e9d2912e50e4f03e43742c19e6e94c1ccf84ab04f0eff342bfc1020a544d7e745fa08a740418aa6da398bf10870a860427dcdbb857b6b41cec8e1342059eabb84637e61530039918cca60b860ef24c0df9e586b7ee9ce89336d5f1f7a210f0385ce2ad9f83a8534ab0325a42495a9cc4cd4774c9e8910bbf4e7192c2e98001fc3aae0957bc822f2aca9be874e1493a0dbad2ceaf959996060a2bc1781c23520cd178b0cfdcaa92929b9daef0c4dc752225792454327bc351b63765208972820203954fb6c5f5fa020f757c1bac415fec7e1bad3b7c19a93ba5ba53dc12f5ffc6720e7572f4709fdb74cdf2dbbf50a7bec9c10ee302cc2c5dd878bd109d1c87bfe3e20883ea4bb25deffe4bed0029e066230311cd71a4beafa608d43d615652cf9c146204c98a01cfce1f0ab321105b2f2659d375f691e2f6cd9eb821adc512718acdd6120d5f64f7950ad04508b36baeadb52228ed3a1139512125834c1f449b2a9613d67204550d2bb9a9333d567b6c2ab154f1fb4bde51d4b5c50307989ed07100095485720109b0b89090c8a2fb0c51f3f9ca1eab0e36b4d9200396e7958523e57b705d11b209195a60cb9f034fced26b3336b0c49872fa13d56cc410f59463e4f312c93423d20f52bbd6ff9d724752b5ecdb148a47e86c1378111d76e77a2f4434816e325c62920510cf87382f2a4c1417203c4e17511170ac616fca2caa49b521dd721a8183f1120ee7ab5b0de3332ec1fb81c2d5baaf0509eda6de26147b081866c2a9aaa3435882011ce790ec01637ff533a68dfd8fa22733bd7e4fefe18a5796e9bfaf996adf54b20509ea869f679c1ae8b074619487fedad28874cf9c93d01003e9dd0df2f45b66b20380577ba031eb78adb2c71bedc8154056b157ae0d01203c19d7df420a424f71521d04f8ed62e7175b7e70d7f92dafff586e5a60be02901f9f67e97374bcecfe7830020c6be51acd068843922b23415a1c7b39f4846da7584a496483a36612dc295968c204e43611bdc897913427fae390deb145024077c5808238d960f93c62a62f70e6520e2f6ace6ca1e8b6b73dc2fc3c43f3d1e740f1c663e3e0ff9415b2d282e1d377420bf6b145630d553e8c6e40d84626f01964e3d6befc33ac284263ee2c35d76003721a9d895ebe788be0a6988027ae6b33a26bee33016d8f4b64255f320d34deba4900020c8bbe181b5482c9352b9d607eed48dde4a2759d765bc2da53cbc1c03d68da30a20818a0a41e03fcaf75f38766bfa81738f0c8ec2fabed7142e998e7b55e014300120b61bcde758db04bc126bc67a1c87b9fb4f8eb2c3958b9e4106306508e2b22c5220cec0548ab3977bd4bbc802483f0c2ce3b1d6dae43bc0f8c79167e4a76ee6f7522007f74fab71a89f6b94bdc8b5128de93ef2be214d04fbb8ac8c2832e1f2ea6371213c26fe406e69b2060b60c6d0eb7759fe85f19cb2288344239f2a398f04b2b8900020689efa2cc2baa15ce24852b1f71d08200fb1f2ab8e10be0c4db8c8263a85032220b1f2737db4e6e55370ce004319e7c5e137f3567bc4d5603e4ed1f5c636db6a6b206dc3dc994eb2ed126f1a76892117b18b24028dce73ab6d35924ec69df9bc361121fb6e92175d959528d8326cafddb4ee94fb9ffc2d9c8a413898f14cec99d4299a0020349eb2d5c10d69df3bfc41f99d6cafff21ce69323637565e1cefa60173ad0d78201818225053a5e2d51745c5002281935d4dca8ad82ff76c5ca2dee6be84fb767021e8d31f169295ce6052584692ed9e4da87e637ca0b52455f216d5036c55d45c85002100c51c6bcf08c0a3926b2be93b47d0ed8955386d369ab8d6849957efb23904a20020b4b64e35cfcbde2461db3ce57292361aa30d136b0e7bbc766f4aa3ab4a72b66821e802f9eb8168e002b64b2b7ccdaba46e73e5c422dda18311788b19eec7af5782002062e41e97d6c8f4c6ac4c0f978f6f8488c92a51eb5793ad109abee4d6c009256920009f7f1755459578897b027bc2b282466aa1d3b9b0983fe4cdf51c9090548d5120760bc9a32ab7facf7f3c1d1aea1b6cab28bb65276e269472cb24ded949256c0121ea8ed0306eb39aeefdd4f57e98d25cef9ee6288a915cc96207ddff402bc2fe81002138caf88bb834204209e39e6c9430ceda0294f1253f8be81917370a34eb28149200203aa2e1582961cbf9b22333ed51aa4512dddeb3cef2e4fdc3644dfa5145366618203a0ac51cf2e1a98ea343e9ca5fbead275516bc20e9bc421e967be939026677682164c2810712d79c82e75116a7e173af1208478e8ab6763c7ee58551b4f323f78200209943e959b9228e61e5def9b2ea18de1688565a2c49dd98f1c0a7d43e81a6800820c4ab26007fa076e1ecd22f104a860f10b22b1ce2ad1229eaa2cf204815f7246e204090f4ac3d42b2793d83aa79103e0d0488ae1297109a6f47bcb29d8c8b6d383d2058ceed8e9f61bca997b4fef220feba451f5401ff7186d1348aab81d7951b5e1d2014994a638981db8c53c2a364c744009050975bfc23fe7bcfc5c8c36b3df0bc2120e26863332f579b931156f0559025f7f96a4d72a9fd108fbfd5c29a71b13df72520e84680238c100c359efc6bfbb7e97d3ddd4d1d24ef3fe8a5208a714580e9aa552108c2aed76de2cc32cfd4d0fa02eea4be069e74da620ba09461e63b5127cf9d8e0020626acd1bbc9c821f30e5ad5b682272cc4b87a9e05323357a367e170ebdae283c20ff8a87793a1dd9996598d50333ace7856916b2add797fb4cd7cb7fdf24245a642165e43a1aa272317475fddbb370cb547766ee44842ab25d83cef370304213eb8b002121565e2ca6c09a263cfcd17fed20fb6aa06e1e798276a5f0ca8cc16dfc5c309600203e8034276a6f2379f8374e8fbc709a692b2dfdf271c0726c4f890282a40eec2920fa335f7d0543267026ecca4424d15cd07755568dc93c6557f850031098fa55572036cea7284ab1abcbe3c7a83ea779392fec2fd8c1524f95c5bb481c67e300600521444c7d18c458aa1e62e5efc656b27d6396c2fc9299befe67e3eef807711f948c0020829cf5a6319d4467027f9057dfa46e1de952a01233e068f7fd18cdadf9a3534820b622a3b592686cde240a056840080c5116582bf166f4655ace84b34145a78b0520fddc9480245d4ac36034ada99a182ada449373ea0fe16557c3c6d9d55b21942720e59aec0bc8b52c1ea0179ef1bece2bf3275164c8b14ef9528a1335dbe13def6620af95305f745778a84f8405956a82816f366957d31e74af5509ff0b7f7dee737221424e7838cbff036afc7e59ba67c6add560e242a1772ff069a31e5e320c08428e0020f4855f5217b4a20c250b75c69d494ec450321911f9d7841e489d43b3739eca792010f35a5fc740eccaa7fa007f33b028089dbb8bf539f560df19627d5bdba6fe3f213c35de64e6b2d64bdb6ba246a5c9b6a91047e35240db65c383a1be9210b7838c0050fd000184213201216f3c4d446f38e3733348efdc4a4dfd79febf41f03567e0ec2b5a8acb93639951dc506d8649ca6d1926a25b4f19549d2b3700cff07c100c47c456ae23df2e60e27f8822c427e2de12038e00293de92621bc4a27d104803e48d076e3ffcf8c31f549fe9f95c6b9233be396cbd3c557efdfca11fdd91397e52f35d6b3a2130fd46ae505dc66bc987d8e93689d41be09a1df024af243b843e18f298de218ab87e557c782bd20648dc4fd4a5e654f1e9fa59626663adb179f51fec2f5534f2f4929ceaccd34d6928ca3d42fcc5efa32490428abc3296147147eabffda85ec2be06be4512448aabe1829d0219902fe1e9cd2bef29a8f89ed8eb1966b89bffd00015e46cc825850532b9bd9d584441772b3963c554af1424ffa9ccc7d7de55dbef9456a3fbf7b4be985d185eb898e166ba646daa12cca359ea77bb7a59e45e8a6cf2708c16b5ccacb708839eef2ee4b5b6597ba3c5899c9f5214bdace3a521fe36cd39b77952d1bcc81f9e5edfae34f1cce40369b7ee492701351d34e231af8a3768cc158a796e900d0cac462f5204da5cde3abcf561ad60f91fc8951e4fb37166ce37261649f840aa51e6ff9be749d72f1f5b5ae3349f14d572860dcf36aa4655788b8cf5108dff7a4e231d2e2a3bec0a159a1e16c9bb38c4052439f2b6b1a62addf739772a1d51057c89751f7518a3b1cd7b37c4ff7d621a54bca5b3936b137c3fd0001b4a09bfe02e11668f8f6204cb9ffa9817ec412c820b6b394ce2cc3a12546ebc5052ec1c74876f3de8fa22e19158198cd04c1336a792ea47e63c2bf4e85dbe7460501606c97409a906dae4e7ad84517a7955793365ca4f49b5f6829efe61f52069fa19cb30ce0a74d415898e7e134ed8c4106cc9d6be32577e9a024b9dfd8129cb5efb1ef282802fe0066aa41e587ac9ee20d6416010e1b0772a44b6d6d1eb4ddb32b80b288952b26323bbec16614c227e4599cf721484443f56571c7b048e58fe48b1786c44e4979e105196eb9b5803795b8aa3d3e3a8f85e10abeb0f7b43b9a62dbd0905af620309ef74f281f39b241c4b4c109ca7e0ec55b13eef8ee0544c820b833fcfd65b4a897458cb9aa376c6bdccf1032e099598452130bd38d28096322fd0001b6c9b1d3d60a49cdf61f9031694f9f790a4e0118ae39cce789df472c13bba207d101410af89ba4b2a88cff5eb09e53593bfdc69a4e2706633a45c77a6d8f4baff22c7e5e6b32278dbfe97100271b1fd1e64463fa6a757c4cf4b19954d1d363494664750fcde0f0b6e35f9e0f08f4fb1e438669f78b10e800943840b15541a31c3090437364159681974adba314bcac37e6cb3ec97c3d8e0cd52241a4c162787c199a256599b97e488b8a75a46d2c3d8af1951ddac43b0d338e739deed1ba26a8616f04d124244f365573b602697aab0b427a8f26f1a22de77c563cda13fc03a030817a837c497732c6108eac62102641176f62d5b8e73d0e27e17586d6e2c2c3fd0001ca7e381637425fa77d95f92b8b491ea1398f53d763a6ce32a2b2ff972ab74bd377723dde5302c487fcf7eac93b089457761ca341f143b7c5aeb51b33cf103f2f4ce4c282bc5bb8c7290c2a9dcc82fce3dcd9669f660872f602de46e869fccdd99c85c06d1d7bc9af4e93502e4ffc385b9641df8f21a25a14b8e4cf99cca023da529f8689d86418c37dbe3d4116e08069f3d08b9c952f4e2164dada9e62908d57cc68b264df8fcbfe449c21cae576adb8169a97294a6bbd369a58654cd182c8403080b3a5a916c107d7271311a291841f3e35e065d48f6023ec4118a3a88d417507ae86b6c74e6ced02c768e0f879844e22862c357a9fe22574e30c179e2243e62192455f24a5c214eba9746d2a8f6a47625b53e5e62bcecade179907fccb08e4b200218a8694fa7fa94c9c174c8e1525bb14dd946981e66b1c02178cde818615e3b6910021fca3cbe539842d405545a71cdacd7a795b6f6fd1c1095497ee8ee215d5cb8eca002039f76b8f7067cd9cf8bde783c8ea6edee7638113ec6729093749c2cc0c15d13c2091ecd1eb4b171326a6a26d764e72e09160ff2112e577149bc7c24e6c0907324421cdebc7f780b73aef4358c6f7e5c9a78ee95dce81c4c1bbbf570cde3c43ff5ca00021c6955241d2f09d2e90e33601fc139d0603919a1011ea7ee89f0e5d53727c45a3002037be468901ac92d7c3481ad63d364a93e2daa023bbcfaf4f6fa40cc54974de19209178cde568ebc660a7698cdd7f83d5932364eb8ac144e62cdc045a00ccda5201fd0001a3347a2cd4b1f5337653554be5fea273a5bf0292c0fd60908c6e699802079912372435166baf2f71b35ec0e00a714e9f7c10096d0f39a65f66f220057f369ce7c8221dddd00e90604f9de897ebf9f06afd41814b57350ca2d7ff7e08f60c94dc1aa087b7975936832a7050ae4a14b47b1986e70d61223cdc37dfeabc5e4212a869d953450202c9c12db23f8819faad59252173ac11c54020fd7324b4d39673befd2b585437affe9398e7967772d408c6db860ec512f94d02b0cbe9c9d414bb4be4b08c3a420a549efaedaa6f0aed7c74044785b611b2d65abfa33374748adb690a54c8719131e9dda9d46afc9dd09c8abc498d6de657920642d3d47a55f17fd3fd000189cc7f3247c6ede0becf1ec89f5d92142d25a713e037454409ccd146dc18c58319550275149342c690f00b1e060db34bf6ea34473c661ff9f32a40214a4715bbcea59a9ad6ff976e43b325135ca16377be53be39255f9e45ab4b3652d629a30500f9e37671d56952b215628ef42c1e49046476e9a760041003742751b158aa85c4e95f70e267d2631491c60cf09a174816890a2dcc2f8a38f296389c3ea66a6d577301297c242a6b9ca7465d8282f2e72e7673f9e1f1c8f470949c970854134de397c08c06d5d21fc487b2b9529c901c052e838a087f15b802d081e869cbb311efed66ba31c17c73cce1e0fc32d58d35e54bc5524776ee3f45d7b8f81fdad4d621b95f023b2202f5d7c814aa89c27b5e0bf5cd64b444c711c2e1c4b805af4fd58300208214f56ae87b268fd16d6df77efaa715fccd1fb1e6298c1c4f7a4f18d39c2f75fd00013fddc975fb72a66d5a7f3ba6df9c1ff55c7abb5a8f08317c6b346875e850223d87d72d71d9b17c11bdbe6eca34b521fe98dc8d31bef6fcfacec74e5c4a6c5f00ac769363df4720e17c3b687a42f29e8edbc20184dc9274d8546368e3fd2408bb68dd224b73a1487c10f4e29b0d8b9823f7c73db26ed16baa4c5d75b162cb5a97caff3cc8957072902538e0e698fad2e90b471777c5e8d90e5cd313933c2f3b30e49b6cf7ae7dcc09ab2e5e464601f093973d99c0815c3ea587d1803b4ca1d9bcc2ac20cb818f95d9239fbbe62c3356ba41f7dc9d2232f6221447fc4858bbf11ee382bfc639d9101943d958a59ff9b81007508c0879c4bf16885bcd6eb2383898fd00018ef6741f57def1a55a42b565ebe0451f0208762da626acaf0cbaac6d9bcec14e22c15660c2a0c5a23b27c445ad968a7e6b2daab11463040eb50799858b7a5063965f947234beb0d42fe1c2dc7bdce7fcda3de1bae384b62e798995eba4836dbee41a8a4de269c026018a22687ad77985d049bda1d39b79528b320ad3d22d893c547b4dc4acb57fac4e0603468beafdf54408ddb7d4c5db0cf14162d0d735b3fad10888f0048035481e5713b846761838504a36c1f956b072b46f11c7c6fc86c766c36fc7dfd5068855335b96162d8b299e3cd21060fed730310d7748c3d16f228b0eea1f37f5eee5453e3a19ebe1e60e4f2834d3338bf1887969de57ef02a1bbfd0001b50e09d8212707f0035a46513208bdba63a5d1fd7a7ea88e181d7a3de81b8afb4ea4b0428eff67c04b27372d73896fd9df443aa9b2a42cdb8665271bb1c54833454504dbc80645bc02366dc998d334b2723bbf5ee1e139265c107db84774b4884f26b57818d39d05b47d5bdab25778965a0627a96ec303c743ba57c0d32d0337a6d3dbe982284109eb0a7976221816cf3fa7f2e3cf739f30be83f253564af7f411358d9c5340fa5578120c48b617c088d9a3d0811a575bb1e96b746e25312ff6d87b9705c04a10123c7606b4c5d6658244121310ce3f1d062250b57ee51e35e0d7b787d38206fd9d26c70ef94f337cc7108ae8ffddf5fa314e6c4e5e1538fbb0fd000192fa78c12fea8a45902ac86e2878e8327b5f4c2ad9bec7d6167bc5590df49cbddda4d4d652f6f087f2ddc7a2813aa5b33048adc5f900eb4acd983bc86ea7b89f630b647a9ef42ab1bb6484ebc20989603144912ffd80158aff7e7d40a1a76489afb21f5f711ecc3ba613868a7aac4f28f2cd5440dfd064c4874d4e398a4718e10de8f7055e452271f01b27544af7035aed32259796962035d7bc37ef843cdccfacf969a1659e354ec27efda8756b09c0bf855f4fbf7598273154b3f517303b6169bdbe4ede890a90d3b34c917311027afeaca7dcd27158b04886dfd81803594292cdb3dbb77ad3a8df97df63095fd28c275b956cc61faddf770608b898d74ca9fd000138b1c34ee04d01368d9cd8ec4d70b97633951ada508c031470cb4cfb15a62426276c7442e7679f926e93325e287ab9ed87446c144be02e559dd076c9c5e1d6a6a7de45438076b1bd7b9ff5a821070877c1d9626925c1f47838e56bb6982b54fb9e131741bab5ea38aabbe4003538c454980dc3be9a436283edc32a3de0321f114ad32cf34e860ca36f18d476980910e564af57da498cd16d08af8b4d4696a9962adeb298d7af4c8c4ad9cb911d80a2115e833f0856833232f92f94cc9c3a6a14270f4c30ada671bacc9aa35bcd3c945dacbb01f4556ebac4e52adf8790dd49fa799584d227ab95afd2da9e67e5642a90e54fc8b693a07384577d04cc40861cc9fd00012c7977632367de82810aecc22a1a802a1aeb6ac54a4a79d8e61606a6eb72effe1abd74a0a18ef83709355e77cef5666f16d94c0d710f5ae3973bb26d07c0a436aebca4d156d42952cc8cedca94162225b5b4780cf47a6426757758957dcf2f638ead2312e67e140ee8c380949c0885c68b396517ba122d90a0ed184564bbe67bf1d0115c77857fbb29945ea00d8e809c295295494dc8cca091d900837b60b31a79bacfae59fe638ee8a2208942e27f605c452be3a4a43cf21e8e0f78e7cc20ffd2c546a0bbcc404b415e4eae2c7b9c2f9121bc741a6f8c5c28c9df3e0169cd86809f5e235ce17fb625f913f94b75b360f9097b625a5305040c55a4057dc4a68efd0001d594cafd37d880576656265ebb737602d6f3b9d0feb3147d8c1f6e1514ca64e3ee046bea5e6892d8db9c094198fb3f697ae6fc880075bbd461e9aab7425ac3a7fad84071b170739e7815d0c76d2ae7fefb718ec132b32a842c58cde33605488043aad9c5df6e58401994a38dc50779e348cd18582ca74aaadcce6df39b221d4235a88000eb2e3efed6744abd0b68836d56ff5d9ec973be549d52a195195725caf3b8cb683da9b96868d35727bf4b74dcbee838d9c39a33d15609fde1c2b345974a93c030952f52da7ac20b8c26b340fd9c08f56b97ccce21004af28ebe4946a91682f9738085cdd9a53e160346cfcf396cfb59465d114c600d9571634aa3c880fd00016c714ccc93e995491adb6718e9ef69fb2cc36044d9e6b73130606b9c845dfdc56e9518a120237f58fb6f2546bec6f47ea6a11f3d898baa47682fe3f537542fe9f6d3679398064a48a3ef8abc92e89eea84c6d8ca956b3c40e9b82b2a496bb1230f8789fc7b0befc061468049d416aca3fb41d4272304a728322684f9ca6125b91dea97bbbba8c83045b5ce8b3110d429d65998b3aed570ecfedd176e98a91eb3410cd501ba52d176bd2c8d05e94acc8f352b3cbe12adefb82d34e174765ef1197f8a93fd4e03c98967ddd0332733e5aa0ce87f63aff78a2b44d10ac63c1549d9a9c6808b743e6f785173924d736bc0890f2e68c1df72b0fb1632bd4679e87690fd0001e736a64f64d58af8d43f0980d29ba57dccea56ff13040270c926a2703401b83859a94d6cbf4cd62109053c9e6ddd1cc61ac649ebb1243037adec455891642de8f43b57afe96cb63736e3e7d735bbeec8d1dc2c698f37f0bcd85bd84cc6b7e25eab500c03f1c62ec730c24208e2df2830d32842277d9e5c9416a91217cbdad60d6a77a7127b09e7463354266a1130d1f04de9041f0f83d41246766027700fa9a02d10d2b0fcb0bd534d22ed38278ce177bbc1c429c09030f105a67db70011d3eb24754bbb31f8a6a98bde215f635409e4cb8c3d769efdd7f1561976bd29876c8a11a130cbb8e3fdf15fd9ad0329852dffd794f499345c3fee09eee21997a5c8a021b993d7cea74a8935b42df2d55606a8841b1a4a8fc0321701d5de9bce1dd1bee100fd00016e510cf2b787c67956990655ca7ba97aaea25163317e7ebfbcf2681b29b0b821aedb8f9b2e309d37f660ac7169dc8234a7e7e4d01e79164eb75f28d284c52ea3d7edff718aa96db6b0b6781366ab985d202823130ca53c2a8e5186fc18862348952e967b1a3e3636517c3e3c48d8fe5e4ef1d5e230ab584964c888c61393ee3d6e34c50446b86e68ebfd048ac86065f9a9c4bbfc2474027b612dcafbbb0416f12d3d856a96557529d1144a852ade77f50f600ecf3c0e00296576eb49a0b211baaa815cea78e1b7a56a1c698ff58488378722f58fdb1ab8bb0063654a8de8344041fddd04be1b4b2b1944ce9d1ebda667caf9ca00e5c2de892a9063a449edd8c1fd00017140014f19e2474e1cc4b40a5a8033f3ddfd960ef33c7e35432deabd85a5b2c18a27b85477c9e6fbb2d8a5e6c686078009b1f7868768fbb5f569ae3429fd64e49cb3a62f32fada09059982a03a20494bd2fd2caa75a01b3ecdbc32acbbd648905d56cd2aefc714af1c24bd6e8f06b1ddbb85e9882ddf8f0e67c654402bfe2e0d9ba404bb3da2d58305184949ce513b3784c3234b39d25e0c6df741683750cb46d7856e67a0d45f4839b5305f9965808d41cbcfb2ad3ad18cdba6744eab0148dd0c1d5e687b6e76bdb9408766b57297be6fdbcf9d7e3e6918c194ef32bb776b5ab85f3a6a164baf866d93ecc3acaefbac43a9fa267bc623cc136ad712b00d788efd00016d3db1d97d5d429249f5cd6dc35f61d0b1b44f1e433cd5e01d28afa6cd6718fc1432b77e8eb6418b0a4b6fcc6707cd3d5c6661cf57b3b73b78feae7c89e25eff2ec1d465be91a18b2f3bc4311919f410cfafe8ae8a06b4b947f9b6fd8ac17453c2f558dfd67f71829be66250d40c3aad6e3ff52d1c0c455e7a4f646445405cf1ad45ba65392bb622a770ab0f5a57e73d32d98ee49d73ac7dde7dad9b9c8e1da9d1e6aef0f9cd120bc1d2cbc5ad819190b758be385f2b0dd736184382af8aa419ac7d734881ab4728ea927b6655b7d3faa4a1e14caf6d38cc706a940bff9c9021e4b6c3ce516d0257deda35d2ac4e0423f01ac849b19b919b773a58f34a06c6b1fd0001464885fd950d292d5aaa6155164216521d2113d795d9aff389771e8f39ff3d96afb5e2359fc52aa6d6cbc3921a7a21e6f3fab62e748337e2cd212456e2b2f52dcb352a75902ac6fdcce93dcf027138be788aad5e09490bbce637751cfd5bda9ec540d7daa92eed7b27ff1bf66585fbe3b39db3de9dd386ecd7671f2395522c1c9006908afd04fe68d88194cbbc3216377cfc27c4fce55c8e558ebc4943cbb477a1172aa8b344c08d6fb853e64ff0f986b7f3e7cc3b2c3d8b2abbc43e08eff15787bfd6a9a8f98b207d8e2530c0c37a37ffcec2fecbd726b4b845ff48a44c1ec7031e4e663ec0042663ff81b9a7fe7d599695737511a685148fce0c3eb01513bb20ffe083f86d11f3c552efeba225a93eb8ea756f45c2e49a4dd08467b79a14000220fd4b0ae4d4c5a165ab75af08922366e89721ba7ee52751e0b3e64017787b9445fd0001d0a8ddbc130bbf7bcd20bdbc8728e812a45cdbe602dcea5826f753b94e1220cf698c1212464a17ed846b29db82b1cf5373241d4117b07a8b7d279d4e8511d22c47be22291e9ab56454becf533b771e5e602542d07828952d5ef900e2548739d57cbb6254c667bc50a0f97c11b7f3dc1624111f9d32cb0d85ac4106b41cd6d82db7aea1135334220163c190744b6daefa456f69c331facf9083af360db6f2a2c80c423357c8fd1bc28fcfd42db69d733efa9ecdff9df079bde1b73a63ee74e5af5c75b67a4824a72466e17b501f6057a68efc19627d115f19fbcb48c0e0889857f0d9191db5875ad6d336adfbf7f09989b2aecfe868c2efc2cc64af46d07c44b9fd000151620fcd4091a3d3e78e8a1f1b5ac9ae8bdd3760320ed9bea2e1b237110d7747e9894704336b958fc92eb200f06507cb56a12f202a8b098bcec5b7b6941dccc18d2bd968538185dbfdbb6ea61eaee22aa8ad24d73df0adfcfafaec181fda3626479710bc19835a5aa7a3b9afd8166b89f5aee8d52589059eda61f19f6319335dfac7765a9a9e22cce0fb3236eeba6ce250ea0b7cfc4a021ca3c88859f556dc1137349a7ad5a628bc47267ad91ff86174a2fe74e3ab298ae8917d6a57a916f00b16bc0f3584b12b0d63141a20b1ed54c6551c6dfa5647783dd9acc68ed75044faf6745161c1ee4abcd9969ff9e01f14791de7d0c8e44e77a5b249e2da833ad3bbfd000119884821e74482b639ec1f8484eb6199a01d6e0a3606e25527d7a9fdbdabaad9f378a9aab04a153b1003d520d03f25a9e41c82504ad6de9fa6cda30a3ee1128c35f49d469a79b3c190ab0fab92d9977478c7ddabb6a66f291b58756e040892f44ebf6c8d0ba3cdf5d9335c8b05b0d34a8f4e832c54979274f5d4554af2d05aec3d51a3cbe03282c9c104f664fc39863c3a23396e762a5a8b5ba18b3c84f0f49f8b7cb6f627905a4fec65e5ab41e868561dba5cc8bcaa8c201d613eb678342aaba5e5d44f7ad7a58810129aaea2e6bb9850ef022e54a50b18e5fbbb76b93f050c31d279e66cd51a29c42591b18db05e88283e52070e6eeffd8fa447bce2f22eb921b2e3f53f38bb201511b9e1d5bbf22bf9e23be137543e34d81a0b194f373dbaa600fd00010ef2d9c59966c760260612842d16526b4352a5f05de655fd0bf50546be1d893d90f4b8264488467880be2c4d7f3c577e6b68335f1e0764fdb16d6fc44fc5bc2c1660798e6b31bdcafbd33a9edb44e48dc37306abe9ea761cd2077e977a6d5b92aff3cdc33f644f47d90fa9a4b172faf29e264c9d55d27cc4e7b7e5e891adb566d172207406ac400348c386e74716a518cfde36169e4cbd8d3093c8d85b99e54474fb7c7ebec5a3beb18b664b953f4d9037041c45738e87c53d55eb92fe862a78627a8f4f3f8f3ef01102b8df05c9c1d81da85e36bff90943e0dd93efc00edbec664fe16f03c8e79d3e105803c50a606f9812d3717921477e224efc9c38604e932116d048938c5e50af925d722ada7f78de5fb1020ef09d45e001364d0ac87ea4b800fd00015a2d2be1965546e9ffe759719faf9960648c9183812a7db83a24b0ea52bc26e3774ac36fcef82756ad386332d49551a147e87ca68fedb289b965a4088b3de6506378af1f858c1c995313e189684bbd3e484499dd7a26097ee6e89ff1d0b6d4c6c03917c53a95d9fd1b6f9a8e59e1687506a5499adea9c1bbf2f63b14207daebf64c8bd5742bec97f6364560cb3d687f8a1ce12a197e87df8b23eff607c97849672fcd5803b43bc8b6dc2090d7b99299248007a3207621c0b40e6fcecc3f68b2888f5abc842a44e6f019391449a7356f9e2637c77612b342fe61e76b307ffb780f529c6c84a6e6ad8d7553021070499e85542cd288a543bb0d318b7abca62c48721047951dd48307113449db6a3f997999f421c62b4524e5baebffef1ed21a6a1a800fd0001c4594030b3c64f25c491e6a5ab25b959438c11265e0fb2e5b2c58e3d59c86d642aff89f4e5137f08ee4871df4098a5748795d596baeadd2db0b006e02822ae2c5f6a93c5281b82a7d60f8c390a264d293f1769d55a5b1f78f0f65f7694477ca79ec16677993d5b6606298171df85a7a8c9bc74a2438618d2aeb384de706b797758dd418b2b5d59dff089f7d4f7f21a8390a3a707c08ea4a238682670a0d80297ba68febea9c4c5afccc241d9f95aa77366880ccf891174b33d95f462839d2820451611e8453d981a703595e8fc73df0a4963c1baab591b7f3dbbbf62b50dc7bbfeb05ec2ecdb1a63d06ec20c4311451b4129a08b94049263e180411ce7b8b7b120a96a6184af4c9ac706b2fb01d6389d94cc4e3930925a5b2bdac9e96dbfa42a78fd0001cb6e8b1c0da834c4fc0d797fc3521eda3ddacade8a4ecfeb518a2e2e8a234d2f6901a7eb4d117404328c70a5c24f36236e88eb1dd19a7e9cf4ec7582f472da053e283dc193d70bc71867d2a348521a860fccebe0b45958f1b5919cb833d79802640b7665b7ec45135b24108eac8a882121b6e6734dcd327b506a434cea9298e6067c36457858e3c18d88a320ce33cc3861f5329bc1f9a8f4af57caf2c134051b12dbb58e309d7bfe9028c3b9b4179759095fa531cf20bdf18134c517ceddfb38b4d94849c3d7eae9b46c353bc43d2f8345e345f818e328875c1bdcd754b706258779c7c30592d0a4c4b5cb557eaad484d1cef2bd4d98d12e70b8fda33f3a5e8320486c47e0fc0d0e679b413041e800ed28ca862ceec23c1a937954131b960fdb3320a3e4a1a7ded3a425d38b144b090d9fb0aeb9ba631622041a702626583d41142520f6fded7e7b2408e70ac4ab0b4d23bc2571cfb9048b0bc738dfd0d6507549a451fd0001656bbe84db36e12bf3c78c07ba3f561d2c2687aaeeef2e2da17cb00f060e025a993c12551c12bd4d75c4c116d18951515f42c5628b0858d85b8afe5deb0aa14a2e33d147eb62b9a52bd4769b6a97382d6c39e5f70e727ee0c9a4684fcf9a03e7232394b4ecc6a2d32e27fe2b436b1081bcb4f3c8bc844e9cb1cf9b828bfc155a2467d506be2f89c9a36bfe2d19125fb21666ed4625cf881ffba75e67d209fde2742d5a46634019cf1f96c1e649a9dd58edbc374b9d6220bccf20055104e8ae8917537fd07f69e12e0b8200af3d924997ee33a7d1e9eb3ebba741f0edd1b59f05e1f279d0c4f7106276968fac8055088bd5f57840438a2776bb21693d9587d188fd00011ae402863053cdeb79711a4c4e7472a1559d86f89bc4daaa6865eced1126197f3c43d78ba16fb2d6508632ab4712b13c3f45d674064a51867bcba96a71b7683fa69ad337fd0c50e7927b913f025fcc47adddf1376a053280cd0fc45bbf29767b4555bbc4054a824c11dc93c3648b3e1c2ca42a5dc5f536eca084370ef65c5a6229bcac295c3fa578fc22eab793205cdc8d37fe3cbf7dc6e1a7c53ff3c7e4d226f0a0d4667bad282c625695d4a1ad88931ca894d4931b19b09c56d2f72e72f62600c97348ed8814f0841baea716d1e0d90f26f8c3932a1e66dd1e8c8e039d3e891ab7be0a887c16ea9fbf3dd7f2c7200587ef56cd0e75e8e828aedcef6198e6d6fd0001a265b258bad27b42d12a12e43a25bf566f2b77f6f0924e8e0c32f294768fd6d9f92e5a15dcc94a2067c71a1f9740700ee0e7f626d35fad2c441b176a077fe681515cb0c8613b0b43c708895c9ca5a41745ca87cc5e8d02e272a484be75cd9afc7478b98bd4c030c3dd4885c5214efbe70cf9a1f8a2616e2eadc150f979e1ca8c389b6fa288889464886bbdaee7539c6bba71ad0927e455d45db2c7371a5b15ccd8c7e7e91592e9bd057d85a18a9a9d1165a84329a6c7beab031f2819cf36a26aeaa4cbef4e7871c472b9b363a1a5d597005cb98e6828a7b6b7ae81cbd036f8d8afbc6289efacedbe51c10f27462c81525b18d119ab4527d9c6db52cde19cfdddfd000143a024d1136d92c3d0de09ce4a3837fff20ce4c4307fca87b1a09acdba6a1df122cc69dca4ec3ca89118ade730ec8959d0dd84db0eff5ab2ff71793af68a2d6bf3a301edce9cf1088046b3177c18d90f6318e2bea3a071469873d1a320d5036a6ea1375ce17113721f01852e1c436745536bba80365d2ee1060a73098f99983d18511059ecac21b84131d845bddfc589a1a4c195ee1d89ba9845c09d681a87c3fe2322cdf571b4a31756d2de38276b97ecef325f4ada73b747d78899c3f84aeec26fc9732ef5843f8b2d7af0fee0f04e01f85731eb9fde3f0a69c4c0aad09f51db5cd03db3627cce5d2a1dfe9910817efc2e53ebf9f79afaf09521a3f14980cd20d6c0a0d48152ef8efbf7a58b809f5c28186addc9690ba0857cbf2c96e395e92afd0001781c2002615bfb1b7d35b2e208a1df0bd8f95f2d639422c213683828227885660bb231058546848fd01763a1ae99e0a7a1040a0f398d8171b45ffc4a58a4439a5addbad802fe79c71a4a27fea8ed229c51d6cb26c3e21127aeccd2f0e31ff1c7d38987c95b917d4c86439a7a0d54b3985ccc3072727c654bea4d473676a45f13ac693de273581cd6f864fba7ff00f3e61cabfb689689c49849419ca1cf489cf9db2138f65c1445b74a0e0cc83e8368e6e79149e699b6e64c77c70ac5156bfa98794cd0c561732fc7e3623673e1f0ebc09de026d9745c4986d498762be6799cf99143fca5d69d04283b504c8d8e325c8811853d467b72e204417c7e35064ce7bcfd0001e42f7a528e3e1de18bdde6e2d9888886aea8ab8eb149299c32c68f6c93a19889efc05e641037687a091933cc83bfbdfac77d48a2828bac6b0625260d4b9da29b0d215d403c6d3aaaf33addeeac4d46875cbc4e3edc1a865d6fe0b6a588631188897cfd131672a8c5d098c0ff8ec5b17bcaecac1d78f83b5296408c994c1e81f93021da25ca403ab6694fb3e82ed260e88067e3edcd8cd0e70ce4c79b49962096eeb1eeb2b17be69033a338ebfadaaa1293f7fe17fb7ddce051d9eb82c84d639bab4a415353ae82dfed2996a26d184189ff1f25d3196c67f34ae50a39bdc2f711ed6468b364d1f0ea8eb29f486dc6c2f6dc59a24acf4772bd542557e9ef4b5b93fd00015ea90490404b84482136f214f4497c09a0ca87dcebecc03ac3152ae1c3e87b945f721137c7a309e1036d0e5d94c6cce38c36b1645a62c7160ca45abcfb5c165eb696154ae38684002a150ab45f1b8f1bb69b28757e2503967a1432090b6c90ce7e3671860e40e95200f8a1ac1ad927b49bdc0a66472eac7123c383ba28578b149f121ad8b1ead1e1908858a640ebebd2e1b8a4f787e5f41d573168493115448edb0de580a8c281b783afe2b62ac6ea243d021187367df9fd28f97e2ca7c8856d89c64a4c3c5e2147aea8120b4bc8b2d0b8ec5b7edeed35d24a800760e82ab19cb7363c7b2fcde6200b8e63e2b698489e9dc0bc4c8f1ba9ff68b59a7277038eb920fe94cb66b60142227c026662ddbc3dc29b373c5c805c365b245c2f69152f1e2b2007e3634d06ba18b973add33bf3e23a7175729a9442fc4213adcb0e5a52e2cc272096af7547b4220ef6335cf5fe47c75896ef2d27e58acb6d7b444b3645ad1a9370fd0001f3642f6dab0eedbdf04e554106719fde491a9bfe00e8228f250e0035f5a95782bef5680a15e6148467d4c7db9e22d9a4bc766ba884940645845b25ace95d405744929adc2c63e41e4c807e33a0d514919c09e855c9d77690be00720d83dcf2b2276e157d39b7acb3ae262e65a8a09ff49478fcd67765dd03b545d7e83bf194ee6b0c5f83c41f7c470e0a1c3f1014a7afc2b7149c01b3f1181eeee4ce8a9be47f0f7f897a05683629d6164fb882e1b67765e7560e7f5c6be76ad9902a755c6af0c455156b93f14e618f533feb9d351bb956da352b7a9d63cc5082d6f9768d80f1bb703c84be2bd75b848f06925f47e226c424c0ae8ad4293b0e9c330cdb16bbddfd000158cc2778c31d0436228349fd19e0428de5916401bbed1f3668014cc51cc6b42381c32e5a7d5ec4d194037b0544284c52e151b652e2733b17065b2a98fc1f5ff30a8acf5dc7522b64eb94a8a71526e2aff0acba058b541fa412bb5344eaae4945ace66b4f9251e842e4b52feae5ad89ae1ce3617debbb551c09c87c6e6e69b7942f1178db84dd758fcb3f63d658e3f48c1a47b725cd2e003a59b8e318950a45fd908c6c4d53d706dfcc2041c046324e3925c9ca032f62cc825f0c11c9fff3fed99f616837244c7d71b3a8d79a8d5bba3284280b59ca1d0ed044801b7d4d6148f014c8c04e8859d9c3d074a7ad77b86ea0ae39fde03fb33eda53fe5c1728cf2daafd0001991691f21883d17fbf12b24e86f0f639967240dcf120c01713ad612ce32a7b8a9c911248414d8a2b836e82c827270f60f154d5de0efb7aec5b3db3f8b9cedbcda84aedea5fd0988ed0069871fda9db5ec2a1ba0f04592048f0040599023ac01e758886daa3fdd9190bb1c8ea29898cdc711e8e8e6c4497ae95ee2e5a6730bd8388d8fa812b21c9b97af84a7cc9f790139b0005dc86efe16436e63982fe8d8ca6bc5bea86b2297a0726dab7704fdcc8a3f6c273eb0f8aa008ad1e6c4a985d7cefe05d3b24cfd195d2fae5392a48be5bd50386244fa9002f95ce18efe97be356a3c5b3990172f812987d0fa10d7fdf9afcd20e165697e3e8f1fa564d1f03010f8f20d53cbea6789dd88800af410af54c3c346483fa085c6e02c088092372ce828e2afd0001f903226d53becf91fe3ee0e556a7ddd652e179dc3c2de5d7af38f380c9916791a37e149ec620429b47e5e0c016b898ee1dc45db857c93a718daeab3167c3c336b22692da51bbf7ef1bc42cba25af0b1fa89df2adcf41535803ad6e80ff1e1f57c80514f1d091040aec55e87c4810ea31ba22e4f93a101d71e324cfb2d84e381f1a59a601ea97907013e119a24a468b27558b68de170690e71bc3c2d7361ca7f77d116b642516d9cb128a70d6bfba83b2c22420059e8c22c9f794f2e144a3a065c942d3276253013efaa88a80e3d7f3c32dc249347a6df656df62d4f9482cc8470bd1bebda925d47c5cd9a54ce81d7bda23c2d0434a98a1f73911c6172facb08a21cb1b1935a2667416f5cb810c787fac55fe8e14146721452870f27b1d5a14fcce0021600ca51450ca3b29e9ff6b388f4df5286db585d027f68fbbd1d4a95fd756318700fd0001b941fa0b95cbce10d49d29c80ac6c45bb624fbcef94b9bcc75d3593a5e11ed85488b6332f9d059e0123d5df6486dd2f57a9239d137d46f3b9c0120d391d1f06cd48c13a0b847020d0832b15162461811662eadcaa2757e2b6b2240d478e7411c7e807e09ef824ecfb3681b49aa72a3319dd310d8efd930ffa7751a222e73f198032f2dfa00e978c9542dc476ca44b161fe470a5f63759de5086a18fc92aa375c608841c6ea41ebc6fb86dea22a8987f7d9abac948d5e67a173eee3b9b90c323c4f2624f53fcaaadd79427a36f560ce6cbd99872d8119acd2173935b0331217e33ceb4a0ef314e45c1a90ad06a46948a38a00feac8f58d8780e6d15ff6e013dbe214cc25d39b2def7e68e2d5c7afd69c5d629265f750b683dd660040dd3220a2ad600209901a1f845f602ecd4f341e7b68c6787561b4f18d7819c8b319dba6836a42517216578b022ea0911e03a47f0814046cea045fa63bb506730b0b491614417df91f900fd0001ab0bc038363d54f8e9e8ebed6a498b0f989c8ee56c81720bd66f71d0d97477d0db56d4481cc0e57c2316f4bd3a6843f86e5b28152436ba23f377dac267c7bb6501666efefb705be623d0491dd42a1a5397f45b6be6ceb1e0499842914f56296a34f304320f5b623ae7e16379d89394b7057d1b92de4c913265ba231d81dbf9e4b586704803baf1cf0fd474a721bd65206df02888dd77df03c831f8433f3b2c7cf7e1211c7c85a975d129f33734fcf77c09aaf68b681da7c506e8c89ac5394589d185117b722b757e307ccf31e9ddc1b9131633bd684ad458fdef09d346566eb4df920801ec4ac019081feda518cafe1ff9f9197b1473ddb18d3349652db870e0fd00014168b6756041ac27a58e4160cdc78c2cca30e144cfac7c58d40b5521c76ae171399e4e22bd3eb5a59d0a44666cbd8d38c4983f8e2bac6fa5ee84c86adbf9679bf8631dec545667463df5fbfdd5ff2d0f6f9021a4a03510e291253bd133520d5366e3c272bf8ec41a14b15aea97c420ff263bb52dacb3c3962359312e5a4690483434ba5f592057dc449727f03768f3756c24c85814e1204d2d90cad6e656d39e7dd7b9c4bfcd103dac62415b0d64901b01278602553c149f6d64342f16757b5ca1033494395404fe1ac7ae33d796be12b0d4f986de23186ecdbdc8477a742fdc973b34b312f5984b74faa42f5f62dd4938f76f5d7ec141249902825d81ab2d88fd000193a2f7ea915b7e506459b8484c43d305deca93a84b3c313a6cea7286d485395158806c3d7f3155f3ddadbfc913cbd7673193a951b356ae208319ecf406172cfbbad940f9fb6accde0c73ea5a19037d5d8644b4de22db968851a303717f473c491465712da34aba4e5dbc31361163ae1492bb3435779569598639943640b6e14233d8888aefa72580e22091b6848592d5b2273cd2df009d614da03d0c0803b4320800094dcbaaa337fd210820e07a8deabbd595d2b59b3bee5c7a27e585a1cef44e532de83f08f3df43d68f5934ca69776b8ee8ddc42969970d64145c093d4e989cf32ba71f750cd03b5c818c33462d05c6477ec1f0ec81c48e4b119d05d31cd5fd00010b1cbcbad09df7edcebe79a913033895c66686f078402893059e4a986fb76d28e89324f5b04ead2c2aa0a05786faa1e8f66fbaf66b7ca94bf52123e6afe4dab54bc33e5860e8766031dd256edf40b294f55062e613cafac04f77e284f5097e88cd7aaf46fd28f4fc24ef1ec1aa0bebbb3da5b504e4dca8dfc13ed99e4371dea6f3bc0f78cdf6ff47548e5a426c92095da045e5ccf45f446116e2c9567ed01d62c1fd3a78c62aa359118f77705174db6a4faacaa0b6d49b4da38e42b20e9cd2ed7979a0f6da28964e0b3fb755046f0a5477dda7465e00d13c8751c36255b7f922dbfe108a14e359257ad607dd7e1c9ccf330779135714b01746a25ddd11e565c821713c8702e50956f6f6ed9c283cfdd08938c8ccf08ada04309e47fe9ddf5cf5dc00fd0001f5e2772a310c56c1343104f8f618a6408dbdd5e421e31856f271fb33329d5055c4c73e06870feb8f7b298cb9b403dd6c4e87dd2137ac84df583c58ffd627b0c52996bea39da5959ccce7968455d3b4ac512c3d552cf63f2da74d6de8e8f037eca47a715d0d9398502b287e5f1034e536a84839bac21ae9958da81fe54c67165fc650f19679c9a628b74fb40c65257dc5b50f690263f9c1eaf41c3365c0e23f91aa7ade32a352c8b88b40b2d7acabbd085185f7737be6f5a7c342e2c908e8a4f997ec949e4bfd6ca7b3da894fd4581c81141cbfc01afc3eb86bc91c304c18c1dccde49d8b61e41d922fcb02162e444dd4931a076bb072ee3a6d5cebbe8683e992fd0001197c654987867c3fa8c71ce3718a70756063be0179e55bf302daafbbdfdd5669ec38e140e3204b260b1d54373f84657101147e672120d735c179fe2dc5fdf7dbb8aadee63b867e31d9d1242e45bd23ed510dc7a6d44ea7806159895a2293c0898bfa6d0018d9864f584a59003a8d4e3a38eae3ec7c9e35d1e5985acd544547ccc1e31024f05bfd5dc07ccbf535b2e0a0a703548e355eb9d1acd8a59684ba0fb674075011c7be7cc899c30ea6c1df0dc34c9366f47743aa12991192cc836db7fd5a3b0306d6905a82e13b729038c70fd3bea01def02b93cd2e71439a9fdd5fb93da6a72436ce9f5b431b7e18c76d8173964d5a02e24195e761d888a6a2532d785fd000177fcd4e94194a3c34efea9f8f04faccbdeae9f0eb8662b0195db08e2b41a08fed52ab7060465ecebdb2eeed7a768c4f65ed7b5193151a56c752cdf97cd4c6be8daf95c16c6703ec95b6cb541506c715983be6384031685d48190e611ddbded6fa377affb759a1f289523e56761857afb3c33b68f743c59a6bf2098a2f5bf3c1c8a3d41224dd12ef247874ff5ab6257cc3e29a65a1c16f3ecc26108142d1899a340be3db55556258a348f51ef61e8e73fa9db743f08df3d31919f2d9484603f0281422c2cfbd66959dc138c2176d6e242a5a04d5b1cbd8c39ed051486869f8b0f3ecdaf42498be6016dc9d49ae52220612a32f50259fb9a8be0cab88deafd56b12024a02c37de6161be5eee8cf63c3b0d16d0bdca07ad59fa0532678b864c13bc20c87f061046d5218e768801c69818dadd24e62aa4625d5a2af09b08f9eba8efb5e29fc2977184990fcd81a0a3f896691051da425d7b99292d8ad7a3bbae5ed9503c5b761907097dd6457a5444b9c85ccc829d5901d46cd917ead52a3316f96588a7b508501be9f69c73d97e0db9d615f20e50712f377663f6c47f42d005db72b225048abeb8d6e3a60afbe21cd13d4539b7d39806952d745a7c49a552175135d840a3f9b9e9ef6ba3c46e9f4bd42f9bbde0d5d5abda8cf06538d3492aa856d8708285c0aa99567adaf6e0d4b28feec0a55e49879ac8ee966e4520e5eeae3e6b4473cb710b4213973850ea6ff0331b04ccffab2c2c3f55ff28b8bdf644e4c19982916d01e28d745302b079066c61fc0e8e1c90931f122893f7f5eb86e9111f98cf36e719129668dcc4718c52d0c4c6a1a941b939e5e744d61aa9fb1aed6eca5fe062cd109b15abfc5851ff77c026e9ba7023b298d40acb1ae9884671ff1776aedabd859f2a481eb18b963afae2e2ec41e3f3671e9dfaeab234d6aaf97249f4702e706437d8e36f517b3b227bae68b79064a3fbe999bc2d75912d970826908dc17b815ec0f030e20b24f17c527557e36695abd05f67c60475a0c74aff42b5e7fd13efc3b1c3bf5ae6e3251731ffaad110c4210c9d6d78d69d6f68cc31ca99fe783a12fca506af01e28234f01e1b30dcdccdfbe696bd5703ddbf0554c8c4863edb0f252e2a9daa54d4900d8fd6aef61b8a8f1e165eea79a3f20663b08722762f2c548e0cd10cf0d2d8e8b9239b638d02c49749d55b721a6c81d2554b51b49191518150664b7e4dbde3ae02b36ab3ec9250290c8cee6b7371e0d3c7ad64d267cf463c4c82cc5a325aa72461345c4f45c8753de23bc148527b0e982511bed9e4bcd4cc37e1e3b5089373fcbfbef9a111c96cc79e9f37d619c0d68598541f2a37ea0fd614ca310c915c0d412d2eeee8f7d71e4a250bcc60d68ab3f296a5f75d75d627e5913aedf3646f733701218878049c420ce0089ded11b087f16111e397d0f2e354f1df0a858aaf07eeb8e80002210305cf1786fa950ae9b553390d6d62e2b285ebaeb978822439e0922403f9cc7dbc473045022100b807fa7bc196a7b2d7a3000e5e1870e2ff488bfd6e2850aeaefb3c606f28379e022009c3cec446550e5cb04483404a677c4b8406d85c62cb4d714e5ca3a50aa02f2600e80300000100e87648170000001976a914dbe6d470fa9fe4d037043533eff4f80aeef0c8d288ac0000000018b6dcbedb05200028a2ac4f32bbab010a001220000000000000000000000000000000000000000000000000000000000000000018ffffffff0f228aab01c2028655e8030000dc8ce67bfe1851477371a9ac40b6ae0cb8571f6e2d5285855288f6079f1ce7239ee8c85f465b1820058b79554f41af297e9caf95ce0084b7c35dea0b95e15a2fb9f8e62c5427c1c36120cbc1fc11ff344909079335209c6b84b45a9211cac960f64e9432ba5eb6e4ecb2068223dfe3d85b345da17bf374f9140c9577c148bcc431c9ec3c7d13bd2363dba821381ed9fa0614416261e88330b3e74c40e6561310eab3f26f092e72f3cab761f373d02680dfb52937bd9515be242f6573754f8f665523cce3bd606c8ad190954f8181577fd0efe7cc64b711d03774958df4a5211e44870302056557777951d7ff8c002161a6a59e979f05469cb31770bd484be6525625359979220eb7e9912e835065fb00fd000216aefbae3525166510814d1636b76b0d48ea3cd54a3a17b136a84340989d75f74ff952966830e4c0d59daa006d5a7190978270ee9475a0778afaf002cdce7efdbfad630f72838b5c4a3b538ba61b94bbd9e353437a50725af5f16fbcbf36bb34e7da54e5c24dfc90b545f95c973877bfadc2703ee10585a1fc1c97d7377bf41c9cbcd5a313849a3c826e7c1301083694e6dc05f46899a901ab4a8d7f6b3600df280157fbef6eca4c28fc610957a42a9acf7c4d7f9846ab6b9b04fa6abb5fefc168d45f10078b97d4d6a39638588a1c19e1bfc472657861a902c2d52cd32fb0463746f649ae88bc0602dbf35816fbccf91dc249be809160cbc7f8b6702d6cc5b81fdebd283231f40758afc899f6fecedc51dc4e5d09cb8961092220541f75ddad45680ea92b4ee78c29f58c197a68420bbb25b450c72d02d7249f7facf9927378620eb36fbf9b4ccbcb55627eb9cf905b4a4c65fcb77a537f642f10901b6e94afa37e4afb0d6d91194454a9c2dd8ef8fe4316f8594c7822a7d58cab09657cf501da5be5a44f947bb957b71e4291a7fc60cd5cef9f0676f7c89123c7ff1ae2e6dc001b6f19785534e207fed2bade8597541b13714284f67d6986bc616ef1b0adbe415242fee85acbf482a6a48b3f142ef7ddb5dd1c97a4b0c53c6ac7aceb8c042d9c9ada1bc986b8c276d07fbf8512a3dae6a357fc02b167eb85000040e8693e45fd000200e23abd27b258f9827ed58545a507bd465e255e1156610da314bf7df68b6b55129df84c7b19e362751ebb9beba10790c9c26c5ddc7f087258d81b006c0d2e92be0178bf5edf6e78e89f73cd97746afbb2551dbc97eafe32ae62e7f9ebcd14ad69faf74d2011d16f2c50775f4f499c87c3c50d9d5d486394c2a7f462675d2a4885493332e0610a78fc0c8b08eda42e4bfe93b8c7f80a911a7992a1deb7cca2e40933e1559815688d4e5ae5e58d706bc513e5108449a8393928b5b77ae73cb03fe212c6375b6c5e61fce9db16360a147e7f7fcd49e05a99711d4d5799be77f7e39d9d1397388d6680d4931b48798ce013256a586781ba80168bae63bed4d150b64a73f7d0ab0c9ebb42f5d4db40eeee303783249af4bbf334c660f8c084ed9a2e5fff8be230940a4a08b59418676ef005192365e4e67757288791ce4992b903a31537596cf6dad0be2af2418a6b9cc2c33e99d874168f6a29df189a869b16eb5d24400ab30e4eca9274114d646aaaa8bad45832b6c0ded2bfa698939a8af9d0af380d2afc58966afe0f45483ecad0f114b904cc2fafcf470dd4fb8f193795e8afc3243b4c946d5eac82babac6feaf4ff10bf53acdf8347fb9fb7a5ac4efcf160f0a7ef3576f439404a3078ea092f46f408a955c965344023d847fad7374cf145cadbc8348eeb2c5aa999ebeeb8a5548bb14e0092b184caee354020c19d66cb213fd0002729bdb186c4c494e17fd6effe29cbe7fbc539caca738d54f9ffc6e52a35a27da134192e2f7f4ee2a86af281b78670e662677e97a1ef008f10f42349fa83bf7841d88b1a457d38164a383ae9c6b974137d58216f22d135d30b9d6e7a74952a7e905f385141f4df088415d704cc3b03b2cd600cb5507f8ea1b53fc0e73031a3946c0d6269f020c9c26a3be3bfdcd37f8d3b9fd42538ebd72029fa0bd8eb57a4fe6769e1b43b5d5d7be311e12e52ee9dad67aa988dedc80ad616d7540381993d9de91a7fac6d08e4414254b9d1d72940fec032833a6b1a5605f4b62c47a86d70dbec5ec913d0a613d438cad385fedf24566bf79edd17238e55421520b95772224623f145100e663b2ba20161784f688afcf07a900ade1d48060d21be9ba9297697891c2584cb99a44868efbdf65178592ecfbadc92f4883662d6b21b7f266eb21815c7401b8e7da061e3258dd685f8cf65f2c2e407c913f85d053b05f6f92ed1299186632ddcf175ccbbc933044bdac5e10916917dea1146f77a8ba4b4fc8ce260b5deed395ecae9b81baa6b385fecca5d2982041c131ce02a1dec517ad2d459434aa3a514e7a4c6c1362401b1ab62b4c89bd7705d5072e0be5250c60c2fdd946bc73050d3b8bcfaa73165eee3660063f279e824d1e15f87307a40bc9e1ccc0f7d7087ba84fe9275742455241b61d3687d23eb9d7a9cc18072ed8be1492db46a454464090750eb0a393499a73d23f4c552ec6ae425a77f97d4285a7f287066b2198bfaa99073da6f4009755e59838e48cd5fe692962b87da3ea7e14b34b352fb2a4673eaaaa594a094610bd0cc566acafc21891b7b0c2470bbb338f579231e01c064f275c5ac9c2748cc50e7f2e36f2768d2a59c22d14b6b9a431f7772e716731c55ccbf086187fb15bd07a5af3040d468d4088ba9e6383f1df6dead9384758f2da81ee96370d9055ce5a0db56bbdccb57ab490f42a01e083b61c5157b3c00e2011dda865c7294cdfd2be5c03a1a36a4deda9cf03b500fd0101b3c97046a717a2f38fe2265185f4411cc68cce6cf9885a7a8fe6292eb9e3eca69fb6249774a8c82b888d22d5fcdd549846c3ebcf054c6bf07aa4d6b1c0d4d9bd8e16501f65373ceda249e9c760848fc86ea92fae7142d211c8bb4a287c91eb08cef7678ef1f445f76f81d464eb1d29ce5c6d6286d73d49cd0ef03b65376eb146a3ff69a487ec90b53c11cce613a586f22cd56fd34df6f7ad64fa3c68a6ae5c9dad99d4a3d2b0974ea1f627be4f0153c7b5fe472d0c562556c4d8d1c7c592bea45bb7886b74f8639f9487b2f6aebf4848b5718f2ce65b8f5a4efcf140e2857bc9c503f0058f9b6af16e75f2d530fbba8979f81569e6cc0bc04a54e30de4e03a9300fd00019b881d73a78b86771d41c0c2a9ebdb3899727f33d2d4d81dd27b6a00b4c7db265999b8442800750abde7cd97be0c691ed06b5d40da115546d90d4803e82c61d43eb5e2bc684adcf180be47660870921fdedb2ce43f33564541fc0debe175c6c49bdfb51378902dc709594b9b7d34d0af70b67c3c608aeb5185e78c1c39cc3080b71a36115f623a07a4e1a3e1f3e17b6f9f695f1a1acd9dd1319d0a0d67b337e64f720e5168c09196244bc71b083f302e042be19b6aa1f8ad61755f4883c3a1ae615252b884ca3cca5e18a023ad6725f08f9e0ffd60e7a73ccd29afc910d60dc99c06f5953c3e398ef615fca45f6a83f8a0be653d31a7e1a1666cc7334d9cad51fd00016f7efa38c8f9f478a6ea1217d8be9b7f0b01c5d1fa483c26e403eb3a4875aeebbd0e7eb8aab8472a5bd80e8be38df13526042952f813b71f8aeb4a2281bfe5e5d9ba70f7e4c9f477706da922899f505dd172e260ce5f008b59c0590d498ac50810e9a38d35f1a2ea4e9ce8f77e46d0b5604dbf94629cfb8b65453a0295eeca9d992365309b7956481a8c5080510a09183bd3358fb26933e15c83fe2ca6d186e631d72889a09464f5dafdc8a93dd100329071e52beb522bef1af0fbae0516ad3b02011e19a2b2924791b3f22679b78039c8356c0e6676e2451487f056d0cff064f55a992afba08f59af7606a809394772ff85c4c40673dd54eb30020c6a5cde3cfd0001ecbc47845e4a1f3c05c4e9d0a47e5e8996326f48d7ee1bd0e432c5f33fecf8a94feaf03f2da65525bbcb119c7928456c28f31c183a21af1acfcd9615669cf47a077f861f694bfc1831f1a71ab66540906c62b85274f63abcc53a37e3fccdc8563ca2153818b6b796473848d765d1c81d4e6f78ef8fce804184e04664c5c6af7c3abe4d92aac3f6ea99b84a3e53a7bf7679c26dc96804ac9e1443b054e3c55fe89315106a14646d06b84122861ac0ac27fe64fbee3c9807b0264a90eb9602187f4df2cc0c62b025ba70bbb30a7e43d533f32546d2e6f97537bc2d623a7f545676a37cb511614037d77fe21a35ce4a2275e7ea1d85b7bc19e477c61033f5effe7fa5d8ff48e2873845157b7a29718e6692704a9d864301fa254798555172c6277cce4ed5aebdff9c27a1bb37071677c16da15d6c1547c5b708e1988eba68befff1f1743342ad7cb89ec8731b567198953d965ef6e395d38b410a326ef3e262937c763179f22a076d17f848dc4e36be38d838d5788f121d771f23209a0d50fb3d016c5cbc47cec31069f7d22bd78691662d8b609932b9533bf828e213e5ed4e61f2b80f05acbc00fda0017bdc59c8a6a23d261cdf1dd10e91523503c3375a803755c29b6a84bac626708b7db937ed39f4053b5c6376b3988fc9388cc64dc07466c67704a32b703d5dd86d8fb8597cc2ff7ca190044c66638028855f29fdf2a0943c64125e3ad2487de6928bc34088957eb32d843613e6b588a91cbfd8c37c24ba655739cefdb203bc3061ff93498160c7e949823b0947c68b6c51dcdf038c538f50413266680ee23817ed9cb840e0094f6fd2277012aa2c6f82b086242e6332a4e00bb9c12c153dfea9340e681e63d72551f8830b2bb2587e5937685252928894bf90bfa174f62bf7ccd43415a094bb4142fcfe639c62f6eda0e4d7ee033f49f51eaa6a35b0f7f400992d8367a275e9d018a547430083c41a35ef543bb92e159efad39c5b84d127bc68dd581c308afd5f81654ffcdb3317dd6e21d5251916214e873c83b6ac197dbac6c1b93d79b9dd5da090be204b9765fdcf662c9296295610e42a0570a1503dd67c44e17936946f3a6ce61f82b13cf00a0b47d06f2cd28651b6af32ffc58304593d5ce81159ccc952ed980f93182fb468ebccadae4dab4565d64a5bcb3aec7a09d681fd24016a4905a3a143e4d61b16898fddbb0f9d7602ce865716b62ae4f9a37e2f6ab89de930e066db6f4a8667ccc4e79e7ac760642c33e5f24266540c9b4fbadda4c0aa1b6a74d02bdf2324ebf9598d8ba918438de1e3343be0b057925bdd52304581ef621fd085dc55cf8b45d605cb0b60047bfa935c2968d554753a615e75f24086b4e40508ecdeb411ed26c007a1110f3e73f504d7fdefb275cbb59cf9cd68bf4784b8845467fac90275f3bbcc2c14a87fbbd7d111441d6ba0833b9045db43975317aee170242b291f8b07254d395472bd4b67db7576bcf2460bf0c182f745a6cbbec3f680b7c6e0a85308bc3af8af3302355757a77a2fe3f98350e4ea1b3074e37a638c630d529141843583ba4b802e089e0a7ecaeeeb42079e072e64fa5251782cbc67ba9d46e4c7f502d44a06e5212d09f4dc5bef1f1dfc376d4a042f608b860971c44caeb3735cd57e19401314af06a73180918af7693ec5204b3f858806e6919af05a1a8c6daea2ee3f08fd2401c082f09f7042a7a0b6b484287050a15f5d8c1011c200d42eb51aff5a484fe1de9feaa264ed3022b6f5b1b54a4a316d3d7e2635210ad83e2d3c497bed46f417ed22804682529b034925dc785a2d74361d1db395d1681d71bc1b0635908ec3e92f850577f35912fe5c173402e6e2ed8003c64a57bb65a2013014a3ce14ccc725f733a50457a696396cac0a551cdda03a2ec81597031feaddd801a9bceaeb5f862a4cdb3eda06dc317a96b29c27d78dd977cc6f25d62bb967814fd1d7e87c675042522c904fadf1cff80289374de8f98df511de975011d877058aa7ea9cdf186ccfa5cfa5258e581ee7ee73e16dcfaa82f079a16c95e6ca4f49c037b2423c12006b549a0b80c5dc9b93685fd2a4bda99e5aea74eafe9d28149e94adc538198d459c7a9a45a1fd24011c69aaa26aa94954fb7dfae2d63eac286b4979e7d513eac8144a7bccc34fb298b7c556a82e7450bf590f1ae658c474ac7c12b3fccaec877b6bdd11fd9655a27b7b69b922b1629a24a8d7f81a6827dd22cbab62d121f5cf96d59be904022360c5bf04d2435c52541ea4d7f932e19fc471a0afb38f2e84668d974c57c963346d790a670a699ff53b5557def1ce9650a8624bb2065f9ce99d6b5361a1b39629040fc897a5d0a07816618840f86601508e90198de64d7091a8bda406009948fa9ebbf2f56adc66e057ab23152504438062867c4237ce2b99c020add16bf66a0c06a072c4fabd9b9fd1146b73366535d934328188798ccc18ad37d30e06a39b8e14468d32820d912359323b7d474dbf507e894a448a4e921f70d1fb4d4ac03b178ae157814004fda0015c202cbc492bde212955236761fcad7de9ec8932946b5352f6c35a242d39b354a1f96a706bcb84174a5b11a7e51cf0adeec9c1e5c88a3f8f32ca81977db668650734398e6feb45cbe0ed5c5d8f77cfa67b78f8c7c856629e6731321126d12ebe70603fe28d4281f5c9165e60749bc8688d7ff3cd509d958dcd1ac7b068a3548af0a3e20b984bf4406a6c6d55c674bc83240757e3a0515bbb626b6b6b863fa7029c5043d67ebcd531601d39b8b6b7e507a176216a264fb80f574d7a6a587d5ebba7c355007630fc49127368f6b672bd12fba956db75f189bd438f5034badc04d396b04a021608a7888434eefffd082efb0c3196698d1b015b42eb6f5a2123c085d37841c0cd6c7a1d77c55d61d76426758cdadcad3d7b1037b5d94c29962d4fe218c9f98c89ecdd9a50a48bc534bc167d536e11906bf594876aea01683269255701b705ac454b250abd45d10f4f8b881c6f4a360014b450132d750099100e15c70ef65ab0eeea340604f3c7b3eb3c51292036a6cc2843989ea418631fb595ca9d7e23a88a3a7d0a8dd5ec1212fc786dcf7107214d5c4d3f2cffffe2b1fbef09816033fdb616b9318b80b32f045ec1bec678dd1500480154ccdd87365d602f0126b57015784abee6fdb9b7f22ba135cb882dfa738baa17233fa53c68d35e4102f185c07a1310b710ff2d63db7238d36487ab504e64e239d0641137cd75cb282e831621f101663efb2f9de2520049c0d08a7c965477fef9d575ba31e101faaea20f96e40046132cd3aa981e8f360cb6a9bac68844d07c7d1741e56b104f634fa1a0c31d64ca4526de4f1754effc40aa04b9dfdb6f53ef77540d66fc9d0a4058fb46518433a1467a71873a03600b47c81e0d452cc44728e3a625235c84f7f62d753faf91fdfa1aea056925168616516e5c4357a7e82c94127fe8a4626c868bb7bcc13a0422e15e31c82935a4e1b9cac496bb456d1318e2e3e704627f1dbe646f5f1590283833193cb03f28e00f5020fbd43c5fdc39fd35498cf4fb7471417e814974331500d8e4ec8af34af39b457d20843d897d0e015456600325ef702c1bdfa7bbb3f2e7a74dcd8fd77beb3df4022320108d0f48a7a9b2a32e1587eb01094e20cb7c002d3de08f481a1d7cdc6b3f9c7c20185bc7ee65a12aa6003a58b83aa90d90baef64c7d324c662ea5138fbb4bd063720f8f9111e20def54a90755537e44fd506737cf1fcc4d0b320bef16eedbd750f8b2002e3af2f477e9d2912e50e4f03e43742c19e6e94c1ccf84ab04f0eff342bfc1020a544d7e745fa08a740418aa6da398bf10870a860427dcdbb857b6b41cec8e1342059eabb84637e61530039918cca60b860ef24c0df9e586b7ee9ce89336d5f1f7a210f0385ce2ad9f83a8534ab0325a42495a9cc4cd4774c9e8910bbf4e7192c2e98001fc3aae0957bc822f2aca9be874e1493a0dbad2ceaf959996060a2bc1781c23520cd178b0cfdcaa92929b9daef0c4dc752225792454327bc351b63765208972820203954fb6c5f5fa020f757c1bac415fec7e1bad3b7c19a93ba5ba53dc12f5ffc6720e7572f4709fdb74cdf2dbbf50a7bec9c10ee302cc2c5dd878bd109d1c87bfe3e20883ea4bb25deffe4bed0029e066230311cd71a4beafa608d43d615652cf9c146204c98a01cfce1f0ab321105b2f2659d375f691e2f6cd9eb821adc512718acdd6120d5f64f7950ad04508b36baeadb52228ed3a1139512125834c1f449b2a9613d67204550d2bb9a9333d567b6c2ab154f1fb4bde51d4b5c50307989ed07100095485720109b0b89090c8a2fb0c51f3f9ca1eab0e36b4d9200396e7958523e57b705d11b209195a60cb9f034fced26b3336b0c49872fa13d56cc410f59463e4f312c93423d20f52bbd6ff9d724752b5ecdb148a47e86c1378111d76e77a2f4434816e325c62920510cf87382f2a4c1417203c4e17511170ac616fca2caa49b521dd721a8183f1120ee7ab5b0de3332ec1fb81c2d5baaf0509eda6de26147b081866c2a9aaa3435882011ce790ec01637ff533a68dfd8fa22733bd7e4fefe18a5796e9bfaf996adf54b20509ea869f679c1ae8b074619487fedad28874cf9c93d01003e9dd0df2f45b66b20380577ba031eb78adb2c71bedc8154056b157ae0d01203c19d7df420a424f71521d04f8ed62e7175b7e70d7f92dafff586e5a60be02901f9f67e97374bcecfe7830020c6be51acd068843922b23415a1c7b39f4846da7584a496483a36612dc295968c204e43611bdc897913427fae390deb145024077c5808238d960f93c62a62f70e6520e2f6ace6ca1e8b6b73dc2fc3c43f3d1e740f1c663e3e0ff9415b2d282e1d377420bf6b145630d553e8c6e40d84626f01964e3d6befc33ac284263ee2c35d76003721a9d895ebe788be0a6988027ae6b33a26bee33016d8f4b64255f320d34deba4900020c8bbe181b5482c9352b9d607eed48dde4a2759d765bc2da53cbc1c03d68da30a20818a0a41e03fcaf75f38766bfa81738f0c8ec2fabed7142e998e7b55e014300120b61bcde758db04bc126bc67a1c87b9fb4f8eb2c3958b9e4106306508e2b22c5220cec0548ab3977bd4bbc802483f0c2ce3b1d6dae43bc0f8c79167e4a76ee6f7522007f74fab71a89f6b94bdc8b5128de93ef2be214d04fbb8ac8c2832e1f2ea6371213c26fe406e69b2060b60c6d0eb7759fe85f19cb2288344239f2a398f04b2b8900020689efa2cc2baa15ce24852b1f71d08200fb1f2ab8e10be0c4db8c8263a85032220b1f2737db4e6e55370ce004319e7c5e137f3567bc4d5603e4ed1f5c636db6a6b206dc3dc994eb2ed126f1a76892117b18b24028dce73ab6d35924ec69df9bc361121fb6e92175d959528d8326cafddb4ee94fb9ffc2d9c8a413898f14cec99d4299a0020349eb2d5c10d69df3bfc41f99d6cafff21ce69323637565e1cefa60173ad0d78201818225053a5e2d51745c5002281935d4dca8ad82ff76c5ca2dee6be84fb767021e8d31f169295ce6052584692ed9e4da87e637ca0b52455f216d5036c55d45c85002100c51c6bcf08c0a3926b2be93b47d0ed8955386d369ab8d6849957efb23904a20020b4b64e35cfcbde2461db3ce57292361aa30d136b0e7bbc766f4aa3ab4a72b66821e802f9eb8168e002b64b2b7ccdaba46e73e5c422dda18311788b19eec7af5782002062e41e97d6c8f4c6ac4c0f978f6f8488c92a51eb5793ad109abee4d6c009256920009f7f1755459578897b027bc2b282466aa1d3b9b0983fe4cdf51c9090548d5120760bc9a32ab7facf7f3c1d1aea1b6cab28bb65276e269472cb24ded949256c0121ea8ed0306eb39aeefdd4f57e98d25cef9ee6288a915cc96207ddff402bc2fe81002138caf88bb834204209e39e6c9430ceda0294f1253f8be81917370a34eb28149200203aa2e1582961cbf9b22333ed51aa4512dddeb3cef2e4fdc3644dfa5145366618203a0ac51cf2e1a98ea343e9ca5fbead275516bc20e9bc421e967be939026677682164c2810712d79c82e75116a7e173af1208478e8ab6763c7ee58551b4f323f78200209943e959b9228e61e5def9b2ea18de1688565a2c49dd98f1c0a7d43e81a6800820c4ab26007fa076e1ecd22f104a860f10b22b1ce2ad1229eaa2cf204815f7246e204090f4ac3d42b2793d83aa79103e0d0488ae1297109a6f47bcb29d8c8b6d383d2058ceed8e9f61bca997b4fef220feba451f5401ff7186d1348aab81d7951b5e1d2014994a638981db8c53c2a364c744009050975bfc23fe7bcfc5c8c36b3df0bc2120e26863332f579b931156f0559025f7f96a4d72a9fd108fbfd5c29a71b13df72520e84680238c100c359efc6bfbb7e97d3ddd4d1d24ef3fe8a5208a714580e9aa552108c2aed76de2cc32cfd4d0fa02eea4be069e74da620ba09461e63b5127cf9d8e0020626acd1bbc9c821f30e5ad5b682272cc4b87a9e05323357a367e170ebdae283c20ff8a87793a1dd9996598d50333ace7856916b2add797fb4cd7cb7fdf24245a642165e43a1aa272317475fddbb370cb547766ee44842ab25d83cef370304213eb8b002121565e2ca6c09a263cfcd17fed20fb6aa06e1e798276a5f0ca8cc16dfc5c309600203e8034276a6f2379f8374e8fbc709a692b2dfdf271c0726c4f890282a40eec2920fa335f7d0543267026ecca4424d15cd07755568dc93c6557f850031098fa55572036cea7284ab1abcbe3c7a83ea779392fec2fd8c1524f95c5bb481c67e300600521444c7d18c458aa1e62e5efc656b27d6396c2fc9299befe67e3eef807711f948c0020829cf5a6319d4467027f9057dfa46e1de952a01233e068f7fd18cdadf9a3534820b622a3b592686cde240a056840080c5116582bf166f4655ace84b34145a78b0520fddc9480245d4ac36034ada99a182ada449373ea0fe16557c3c6d9d55b21942720e59aec0bc8b52c1ea0179ef1bece2bf3275164c8b14ef9528a1335dbe13def6620af95305f745778a84f8405956a82816f366957d31e74af5509ff0b7f7dee737221424e7838cbff036afc7e59ba67c6add560e242a1772ff069a31e5e320c08428e0020f4855f5217b4a20c250b75c69d494ec450321911f9d7841e489d43b3739eca792010f35a5fc740eccaa7fa007f33b028089dbb8bf539f560df19627d5bdba6fe3f213c35de64e6b2d64bdb6ba246a5c9b6a91047e35240db65c383a1be9210b7838c0050fd000184213201216f3c4d446f38e3733348efdc4a4dfd79febf41f03567e0ec2b5a8acb93639951dc506d8649ca6d1926a25b4f19549d2b3700cff07c100c47c456ae23df2e60e27f8822c427e2de12038e00293de92621bc4a27d104803e48d076e3ffcf8c31f549fe9f95c6b9233be396cbd3c557efdfca11fdd91397e52f35d6b3a2130fd46ae505dc66bc987d8e93689d41be09a1df024af243b843e18f298de218ab87e557c782bd20648dc4fd4a5e654f1e9fa59626663adb179f51fec2f5534f2f4929ceaccd34d6928ca3d42fcc5efa32490428abc3296147147eabffda85ec2be06be4512448aabe1829d0219902fe1e9cd2bef29a8f89ed8eb1966b89bffd00015e46cc825850532b9bd9d584441772b3963c554af1424ffa9ccc7d7de55dbef9456a3fbf7b4be985d185eb898e166ba646daa12cca359ea77bb7a59e45e8a6cf2708c16b5ccacb708839eef2ee4b5b6597ba3c5899c9f5214bdace3a521fe36cd39b77952d1bcc81f9e5edfae34f1cce40369b7ee492701351d34e231af8a3768cc158a796e900d0cac462f5204da5cde3abcf561ad60f91fc8951e4fb37166ce37261649f840aa51e6ff9be749d72f1f5b5ae3349f14d572860dcf36aa4655788b8cf5108dff7a4e231d2e2a3bec0a159a1e16c9bb38c4052439f2b6b1a62addf739772a1d51057c89751f7518a3b1cd7b37c4ff7d621a54bca5b3936b137c3fd0001b4a09bfe02e11668f8f6204cb9ffa9817ec412c820b6b394ce2cc3a12546ebc5052ec1c74876f3de8fa22e19158198cd04c1336a792ea47e63c2bf4e85dbe7460501606c97409a906dae4e7ad84517a7955793365ca4f49b5f6829efe61f52069fa19cb30ce0a74d415898e7e134ed8c4106cc9d6be32577e9a024b9dfd8129cb5efb1ef282802fe0066aa41e587ac9ee20d6416010e1b0772a44b6d6d1eb4ddb32b80b288952b26323bbec16614c227e4599cf721484443f56571c7b048e58fe48b1786c44e4979e105196eb9b5803795b8aa3d3e3a8f85e10abeb0f7b43b9a62dbd0905af620309ef74f281f39b241c4b4c109ca7e0ec55b13eef8ee0544c820b833fcfd65b4a897458cb9aa376c6bdccf1032e099598452130bd38d28096322fd0001b6c9b1d3d60a49cdf61f9031694f9f790a4e0118ae39cce789df472c13bba207d101410af89ba4b2a88cff5eb09e53593bfdc69a4e2706633a45c77a6d8f4baff22c7e5e6b32278dbfe97100271b1fd1e64463fa6a757c4cf4b19954d1d363494664750fcde0f0b6e35f9e0f08f4fb1e438669f78b10e800943840b15541a31c3090437364159681974adba314bcac37e6cb3ec97c3d8e0cd52241a4c162787c199a256599b97e488b8a75a46d2c3d8af1951ddac43b0d338e739deed1ba26a8616f04d124244f365573b602697aab0b427a8f26f1a22de77c563cda13fc03a030817a837c497732c6108eac62102641176f62d5b8e73d0e27e17586d6e2c2c3fd0001ca7e381637425fa77d95f92b8b491ea1398f53d763a6ce32a2b2ff972ab74bd377723dde5302c487fcf7eac93b089457761ca341f143b7c5aeb51b33cf103f2f4ce4c282bc5bb8c7290c2a9dcc82fce3dcd9669f660872f602de46e869fccdd99c85c06d1d7bc9af4e93502e4ffc385b9641df8f21a25a14b8e4cf99cca023da529f8689d86418c37dbe3d4116e08069f3d08b9c952f4e2164dada9e62908d57cc68b264df8fcbfe449c21cae576adb8169a97294a6bbd369a58654cd182c8403080b3a5a916c107d7271311a291841f3e35e065d48f6023ec4118a3a88d417507ae86b6c74e6ced02c768e0f879844e22862c357a9fe22574e30c179e2243e62192455f24a5c214eba9746d2a8f6a47625b53e5e62bcecade179907fccb08e4b200218a8694fa7fa94c9c174c8e1525bb14dd946981e66b1c02178cde818615e3b6910021fca3cbe539842d405545a71cdacd7a795b6f6fd1c1095497ee8ee215d5cb8eca002039f76b8f7067cd9cf8bde783c8ea6edee7638113ec6729093749c2cc0c15d13c2091ecd1eb4b171326a6a26d764e72e09160ff2112e577149bc7c24e6c0907324421cdebc7f780b73aef4358c6f7e5c9a78ee95dce81c4c1bbbf570cde3c43ff5ca00021c6955241d2f09d2e90e33601fc139d0603919a1011ea7ee89f0e5d53727c45a3002037be468901ac92d7c3481ad63d364a93e2daa023bbcfaf4f6fa40cc54974de19209178cde568ebc660a7698cdd7f83d5932364eb8ac144e62cdc045a00ccda5201fd0001a3347a2cd4b1f5337653554be5fea273a5bf0292c0fd60908c6e699802079912372435166baf2f71b35ec0e00a714e9f7c10096d0f39a65f66f220057f369ce7c8221dddd00e90604f9de897ebf9f06afd41814b57350ca2d7ff7e08f60c94dc1aa087b7975936832a7050ae4a14b47b1986e70d61223cdc37dfeabc5e4212a869d953450202c9c12db23f8819faad59252173ac11c54020fd7324b4d39673befd2b585437affe9398e7967772d408c6db860ec512f94d02b0cbe9c9d414bb4be4b08c3a420a549efaedaa6f0aed7c74044785b611b2d65abfa33374748adb690a54c8719131e9dda9d46afc9dd09c8abc498d6de657920642d3d47a55f17fd3fd000189cc7f3247c6ede0becf1ec89f5d92142d25a713e037454409ccd146dc18c58319550275149342c690f00b1e060db34bf6ea34473c661ff9f32a40214a4715bbcea59a9ad6ff976e43b325135ca16377be53be39255f9e45ab4b3652d629a30500f9e37671d56952b215628ef42c1e49046476e9a760041003742751b158aa85c4e95f70e267d2631491c60cf09a174816890a2dcc2f8a38f296389c3ea66a6d577301297c242a6b9ca7465d8282f2e72e7673f9e1f1c8f470949c970854134de397c08c06d5d21fc487b2b9529c901c052e838a087f15b802d081e869cbb311efed66ba31c17c73cce1e0fc32d58d35e54bc5524776ee3f45d7b8f81fdad4d621b95f023b2202f5d7c814aa89c27b5e0bf5cd64b444c711c2e1c4b805af4fd58300208214f56ae87b268fd16d6df77efaa715fccd1fb1e6298c1c4f7a4f18d39c2f75fd00013fddc975fb72a66d5a7f3ba6df9c1ff55c7abb5a8f08317c6b346875e850223d87d72d71d9b17c11bdbe6eca34b521fe98dc8d31bef6fcfacec74e5c4a6c5f00ac769363df4720e17c3b687a42f29e8edbc20184dc9274d8546368e3fd2408bb68dd224b73a1487c10f4e29b0d8b9823f7c73db26ed16baa4c5d75b162cb5a97caff3cc8957072902538e0e698fad2e90b471777c5e8d90e5cd313933c2f3b30e49b6cf7ae7dcc09ab2e5e464601f093973d99c0815c3ea587d1803b4ca1d9bcc2ac20cb818f95d9239fbbe62c3356ba41f7dc9d2232f6221447fc4858bbf11ee382bfc639d9101943d958a59ff9b81007508c0879c4bf16885bcd6eb2383898fd00018ef6741f57def1a55a42b565ebe0451f0208762da626acaf0cbaac6d9bcec14e22c15660c2a0c5a23b27c445ad968a7e6b2daab11463040eb50799858b7a5063965f947234beb0d42fe1c2dc7bdce7fcda3de1bae384b62e798995eba4836dbee41a8a4de269c026018a22687ad77985d049bda1d39b79528b320ad3d22d893c547b4dc4acb57fac4e0603468beafdf54408ddb7d4c5db0cf14162d0d735b3fad10888f0048035481e5713b846761838504a36c1f956b072b46f11c7c6fc86c766c36fc7dfd5068855335b96162d8b299e3cd21060fed730310d7748c3d16f228b0eea1f37f5eee5453e3a19ebe1e60e4f2834d3338bf1887969de57ef02a1bbfd0001b50e09d8212707f0035a46513208bdba63a5d1fd7a7ea88e181d7a3de81b8afb4ea4b0428eff67c04b27372d73896fd9df443aa9b2a42cdb8665271bb1c54833454504dbc80645bc02366dc998d334b2723bbf5ee1e139265c107db84774b4884f26b57818d39d05b47d5bdab25778965a0627a96ec303c743ba57c0d32d0337a6d3dbe982284109eb0a7976221816cf3fa7f2e3cf739f30be83f253564af7f411358d9c5340fa5578120c48b617c088d9a3d0811a575bb1e96b746e25312ff6d87b9705c04a10123c7606b4c5d6658244121310ce3f1d062250b57ee51e35e0d7b787d38206fd9d26c70ef94f337cc7108ae8ffddf5fa314e6c4e5e1538fbb0fd000192fa78c12fea8a45902ac86e2878e8327b5f4c2ad9bec7d6167bc5590df49cbddda4d4d652f6f087f2ddc7a2813aa5b33048adc5f900eb4acd983bc86ea7b89f630b647a9ef42ab1bb6484ebc20989603144912ffd80158aff7e7d40a1a76489afb21f5f711ecc3ba613868a7aac4f28f2cd5440dfd064c4874d4e398a4718e10de8f7055e452271f01b27544af7035aed32259796962035d7bc37ef843cdccfacf969a1659e354ec27efda8756b09c0bf855f4fbf7598273154b3f517303b6169bdbe4ede890a90d3b34c917311027afeaca7dcd27158b04886dfd81803594292cdb3dbb77ad3a8df97df63095fd28c275b956cc61faddf770608b898d74ca9fd000138b1c34ee04d01368d9cd8ec4d70b97633951ada508c031470cb4cfb15a62426276c7442e7679f926e93325e287ab9ed87446c144be02e559dd076c9c5e1d6a6a7de45438076b1bd7b9ff5a821070877c1d9626925c1f47838e56bb6982b54fb9e131741bab5ea38aabbe4003538c454980dc3be9a436283edc32a3de0321f114ad32cf34e860ca36f18d476980910e564af57da498cd16d08af8b4d4696a9962adeb298d7af4c8c4ad9cb911d80a2115e833f0856833232f92f94cc9c3a6a14270f4c30ada671bacc9aa35bcd3c945dacbb01f4556ebac4e52adf8790dd49fa799584d227ab95afd2da9e67e5642a90e54fc8b693a07384577d04cc40861cc9fd00012c7977632367de82810aecc22a1a802a1aeb6ac54a4a79d8e61606a6eb72effe1abd74a0a18ef83709355e77cef5666f16d94c0d710f5ae3973bb26d07c0a436aebca4d156d42952cc8cedca94162225b5b4780cf47a6426757758957dcf2f638ead2312e67e140ee8c380949c0885c68b396517ba122d90a0ed184564bbe67bf1d0115c77857fbb29945ea00d8e809c295295494dc8cca091d900837b60b31a79bacfae59fe638ee8a2208942e27f605c452be3a4a43cf21e8e0f78e7cc20ffd2c546a0bbcc404b415e4eae2c7b9c2f9121bc741a6f8c5c28c9df3e0169cd86809f5e235ce17fb625f913f94b75b360f9097b625a5305040c55a4057dc4a68efd0001d594cafd37d880576656265ebb737602d6f3b9d0feb3147d8c1f6e1514ca64e3ee046bea5e6892d8db9c094198fb3f697ae6fc880075bbd461e9aab7425ac3a7fad84071b170739e7815d0c76d2ae7fefb718ec132b32a842c58cde33605488043aad9c5df6e58401994a38dc50779e348cd18582ca74aaadcce6df39b221d4235a88000eb2e3efed6744abd0b68836d56ff5d9ec973be549d52a195195725caf3b8cb683da9b96868d35727bf4b74dcbee838d9c39a33d15609fde1c2b345974a93c030952f52da7ac20b8c26b340fd9c08f56b97ccce21004af28ebe4946a91682f9738085cdd9a53e160346cfcf396cfb59465d114c600d9571634aa3c880fd00016c714ccc93e995491adb6718e9ef69fb2cc36044d9e6b73130606b9c845dfdc56e9518a120237f58fb6f2546bec6f47ea6a11f3d898baa47682fe3f537542fe9f6d3679398064a48a3ef8abc92e89eea84c6d8ca956b3c40e9b82b2a496bb1230f8789fc7b0befc061468049d416aca3fb41d4272304a728322684f9ca6125b91dea97bbbba8c83045b5ce8b3110d429d65998b3aed570ecfedd176e98a91eb3410cd501ba52d176bd2c8d05e94acc8f352b3cbe12adefb82d34e174765ef1197f8a93fd4e03c98967ddd0332733e5aa0ce87f63aff78a2b44d10ac63c1549d9a9c6808b743e6f785173924d736bc0890f2e68c1df72b0fb1632bd4679e87690fd0001e736a64f64d58af8d43f0980d29ba57dccea56ff13040270c926a2703401b83859a94d6cbf4cd62109053c9e6ddd1cc61ac649ebb1243037adec455891642de8f43b57afe96cb63736e3e7d735bbeec8d1dc2c698f37f0bcd85bd84cc6b7e25eab500c03f1c62ec730c24208e2df2830d32842277d9e5c9416a91217cbdad60d6a77a7127b09e7463354266a1130d1f04de9041f0f83d41246766027700fa9a02d10d2b0fcb0bd534d22ed38278ce177bbc1c429c09030f105a67db70011d3eb24754bbb31f8a6a98bde215f635409e4cb8c3d769efdd7f1561976bd29876c8a11a130cbb8e3fdf15fd9ad0329852dffd794f499345c3fee09eee21997a5c8a021b993d7cea74a8935b42df2d55606a8841b1a4a8fc0321701d5de9bce1dd1bee100fd00016e510cf2b787c67956990655ca7ba97aaea25163317e7ebfbcf2681b29b0b821aedb8f9b2e309d37f660ac7169dc8234a7e7e4d01e79164eb75f28d284c52ea3d7edff718aa96db6b0b6781366ab985d202823130ca53c2a8e5186fc18862348952e967b1a3e3636517c3e3c48d8fe5e4ef1d5e230ab584964c888c61393ee3d6e34c50446b86e68ebfd048ac86065f9a9c4bbfc2474027b612dcafbbb0416f12d3d856a96557529d1144a852ade77f50f600ecf3c0e00296576eb49a0b211baaa815cea78e1b7a56a1c698ff58488378722f58fdb1ab8bb0063654a8de8344041fddd04be1b4b2b1944ce9d1ebda667caf9ca00e5c2de892a9063a449edd8c1fd00017140014f19e2474e1cc4b40a5a8033f3ddfd960ef33c7e35432deabd85a5b2c18a27b85477c9e6fbb2d8a5e6c686078009b1f7868768fbb5f569ae3429fd64e49cb3a62f32fada09059982a03a20494bd2fd2caa75a01b3ecdbc32acbbd648905d56cd2aefc714af1c24bd6e8f06b1ddbb85e9882ddf8f0e67c654402bfe2e0d9ba404bb3da2d58305184949ce513b3784c3234b39d25e0c6df741683750cb46d7856e67a0d45f4839b5305f9965808d41cbcfb2ad3ad18cdba6744eab0148dd0c1d5e687b6e76bdb9408766b57297be6fdbcf9d7e3e6918c194ef32bb776b5ab85f3a6a164baf866d93ecc3acaefbac43a9fa267bc623cc136ad712b00d788efd00016d3db1d97d5d429249f5cd6dc35f61d0b1b44f1e433cd5e01d28afa6cd6718fc1432b77e8eb6418b0a4b6fcc6707cd3d5c6661cf57b3b73b78feae7c89e25eff2ec1d465be91a18b2f3bc4311919f410cfafe8ae8a06b4b947f9b6fd8ac17453c2f558dfd67f71829be66250d40c3aad6e3ff52d1c0c455e7a4f646445405cf1ad45ba65392bb622a770ab0f5a57e73d32d98ee49d73ac7dde7dad9b9c8e1da9d1e6aef0f9cd120bc1d2cbc5ad819190b758be385f2b0dd736184382af8aa419ac7d734881ab4728ea927b6655b7d3faa4a1e14caf6d38cc706a940bff9c9021e4b6c3ce516d0257deda35d2ac4e0423f01ac849b19b919b773a58f34a06c6b1fd0001464885fd950d292d5aaa6155164216521d2113d795d9aff389771e8f39ff3d96afb5e2359fc52aa6d6cbc3921a7a21e6f3fab62e748337e2cd212456e2b2f52dcb352a75902ac6fdcce93dcf027138be788aad5e09490bbce637751cfd5bda9ec540d7daa92eed7b27ff1bf66585fbe3b39db3de9dd386ecd7671f2395522c1c9006908afd04fe68d88194cbbc3216377cfc27c4fce55c8e558ebc4943cbb477a1172aa8b344c08d6fb853e64ff0f986b7f3e7cc3b2c3d8b2abbc43e08eff15787bfd6a9a8f98b207d8e2530c0c37a37ffcec2fecbd726b4b845ff48a44c1ec7031e4e663ec0042663ff81b9a7fe7d599695737511a685148fce0c3eb01513bb20ffe083f86d11f3c552efeba225a93eb8ea756f45c2e49a4dd08467b79a14000220fd4b0ae4d4c5a165ab75af08922366e89721ba7ee52751e0b3e64017787b9445fd0001d0a8ddbc130bbf7bcd20bdbc8728e812a45cdbe602dcea5826f753b94e1220cf698c1212464a17ed846b29db82b1cf5373241d4117b07a8b7d279d4e8511d22c47be22291e9ab56454becf533b771e5e602542d07828952d5ef900e2548739d57cbb6254c667bc50a0f97c11b7f3dc1624111f9d32cb0d85ac4106b41cd6d82db7aea1135334220163c190744b6daefa456f69c331facf9083af360db6f2a2c80c423357c8fd1bc28fcfd42db69d733efa9ecdff9df079bde1b73a63ee74e5af5c75b67a4824a72466e17b501f6057a68efc19627d115f19fbcb48c0e0889857f0d9191db5875ad6d336adfbf7f09989b2aecfe868c2efc2cc64af46d07c44b9fd000151620fcd4091a3d3e78e8a1f1b5ac9ae8bdd3760320ed9bea2e1b237110d7747e9894704336b958fc92eb200f06507cb56a12f202a8b098bcec5b7b6941dccc18d2bd968538185dbfdbb6ea61eaee22aa8ad24d73df0adfcfafaec181fda3626479710bc19835a5aa7a3b9afd8166b89f5aee8d52589059eda61f19f6319335dfac7765a9a9e22cce0fb3236eeba6ce250ea0b7cfc4a021ca3c88859f556dc1137349a7ad5a628bc47267ad91ff86174a2fe74e3ab298ae8917d6a57a916f00b16bc0f3584b12b0d63141a20b1ed54c6551c6dfa5647783dd9acc68ed75044faf6745161c1ee4abcd9969ff9e01f14791de7d0c8e44e77a5b249e2da833ad3bbfd000119884821e74482b639ec1f8484eb6199a01d6e0a3606e25527d7a9fdbdabaad9f378a9aab04a153b1003d520d03f25a9e41c82504ad6de9fa6cda30a3ee1128c35f49d469a79b3c190ab0fab92d9977478c7ddabb6a66f291b58756e040892f44ebf6c8d0ba3cdf5d9335c8b05b0d34a8f4e832c54979274f5d4554af2d05aec3d51a3cbe03282c9c104f664fc39863c3a23396e762a5a8b5ba18b3c84f0f49f8b7cb6f627905a4fec65e5ab41e868561dba5cc8bcaa8c201d613eb678342aaba5e5d44f7ad7a58810129aaea2e6bb9850ef022e54a50b18e5fbbb76b93f050c31d279e66cd51a29c42591b18db05e88283e52070e6eeffd8fa447bce2f22eb921b2e3f53f38bb201511b9e1d5bbf22bf9e23be137543e34d81a0b194f373dbaa600fd00010ef2d9c59966c760260612842d16526b4352a5f05de655fd0bf50546be1d893d90f4b8264488467880be2c4d7f3c577e6b68335f1e0764fdb16d6fc44fc5bc2c1660798e6b31bdcafbd33a9edb44e48dc37306abe9ea761cd2077e977a6d5b92aff3cdc33f644f47d90fa9a4b172faf29e264c9d55d27cc4e7b7e5e891adb566d172207406ac400348c386e74716a518cfde36169e4cbd8d3093c8d85b99e54474fb7c7ebec5a3beb18b664b953f4d9037041c45738e87c53d55eb92fe862a78627a8f4f3f8f3ef01102b8df05c9c1d81da85e36bff90943e0dd93efc00edbec664fe16f03c8e79d3e105803c50a606f9812d3717921477e224efc9c38604e932116d048938c5e50af925d722ada7f78de5fb1020ef09d45e001364d0ac87ea4b800fd00015a2d2be1965546e9ffe759719faf9960648c9183812a7db83a24b0ea52bc26e3774ac36fcef82756ad386332d49551a147e87ca68fedb289b965a4088b3de6506378af1f858c1c995313e189684bbd3e484499dd7a26097ee6e89ff1d0b6d4c6c03917c53a95d9fd1b6f9a8e59e1687506a5499adea9c1bbf2f63b14207daebf64c8bd5742bec97f6364560cb3d687f8a1ce12a197e87df8b23eff607c97849672fcd5803b43bc8b6dc2090d7b99299248007a3207621c0b40e6fcecc3f68b2888f5abc842a44e6f019391449a7356f9e2637c77612b342fe61e76b307ffb780f529c6c84a6e6ad8d7553021070499e85542cd288a543bb0d318b7abca62c48721047951dd48307113449db6a3f997999f421c62b4524e5baebffef1ed21a6a1a800fd0001c4594030b3c64f25c491e6a5ab25b959438c11265e0fb2e5b2c58e3d59c86d642aff89f4e5137f08ee4871df4098a5748795d596baeadd2db0b006e02822ae2c5f6a93c5281b82a7d60f8c390a264d293f1769d55a5b1f78f0f65f7694477ca79ec16677993d5b6606298171df85a7a8c9bc74a2438618d2aeb384de706b797758dd418b2b5d59dff089f7d4f7f21a8390a3a707c08ea4a238682670a0d80297ba68febea9c4c5afccc241d9f95aa77366880ccf891174b33d95f462839d2820451611e8453d981a703595e8fc73df0a4963c1baab591b7f3dbbbf62b50dc7bbfeb05ec2ecdb1a63d06ec20c4311451b4129a08b94049263e180411ce7b8b7b120a96a6184af4c9ac706b2fb01d6389d94cc4e3930925a5b2bdac9e96dbfa42a78fd0001cb6e8b1c0da834c4fc0d797fc3521eda3ddacade8a4ecfeb518a2e2e8a234d2f6901a7eb4d117404328c70a5c24f36236e88eb1dd19a7e9cf4ec7582f472da053e283dc193d70bc71867d2a348521a860fccebe0b45958f1b5919cb833d79802640b7665b7ec45135b24108eac8a882121b6e6734dcd327b506a434cea9298e6067c36457858e3c18d88a320ce33cc3861f5329bc1f9a8f4af57caf2c134051b12dbb58e309d7bfe9028c3b9b4179759095fa531cf20bdf18134c517ceddfb38b4d94849c3d7eae9b46c353bc43d2f8345e345f818e328875c1bdcd754b706258779c7c30592d0a4c4b5cb557eaad484d1cef2bd4d98d12e70b8fda33f3a5e8320486c47e0fc0d0e679b413041e800ed28ca862ceec23c1a937954131b960fdb3320a3e4a1a7ded3a425d38b144b090d9fb0aeb9ba631622041a702626583d41142520f6fded7e7b2408e70ac4ab0b4d23bc2571cfb9048b0bc738dfd0d6507549a451fd0001656bbe84db36e12bf3c78c07ba3f561d2c2687aaeeef2e2da17cb00f060e025a993c12551c12bd4d75c4c116d18951515f42c5628b0858d85b8afe5deb0aa14a2e33d147eb62b9a52bd4769b6a97382d6c39e5f70e727ee0c9a4684fcf9a03e7232394b4ecc6a2d32e27fe2b436b1081bcb4f3c8bc844e9cb1cf9b828bfc155a2467d506be2f89c9a36bfe2d19125fb21666ed4625cf881ffba75e67d209fde2742d5a46634019cf1f96c1e649a9dd58edbc374b9d6220bccf20055104e8ae8917537fd07f69e12e0b8200af3d924997ee33a7d1e9eb3ebba741f0edd1b59f05e1f279d0c4f7106276968fac8055088bd5f57840438a2776bb21693d9587d188fd00011ae402863053cdeb79711a4c4e7472a1559d86f89bc4daaa6865eced1126197f3c43d78ba16fb2d6508632ab4712b13c3f45d674064a51867bcba96a71b7683fa69ad337fd0c50e7927b913f025fcc47adddf1376a053280cd0fc45bbf29767b4555bbc4054a824c11dc93c3648b3e1c2ca42a5dc5f536eca084370ef65c5a6229bcac295c3fa578fc22eab793205cdc8d37fe3cbf7dc6e1a7c53ff3c7e4d226f0a0d4667bad282c625695d4a1ad88931ca894d4931b19b09c56d2f72e72f62600c97348ed8814f0841baea716d1e0d90f26f8c3932a1e66dd1e8c8e039d3e891ab7be0a887c16ea9fbf3dd7f2c7200587ef56cd0e75e8e828aedcef6198e6d6fd0001a265b258bad27b42d12a12e43a25bf566f2b77f6f0924e8e0c32f294768fd6d9f92e5a15dcc94a2067c71a1f9740700ee0e7f626d35fad2c441b176a077fe681515cb0c8613b0b43c708895c9ca5a41745ca87cc5e8d02e272a484be75cd9afc7478b98bd4c030c3dd4885c5214efbe70cf9a1f8a2616e2eadc150f979e1ca8c389b6fa288889464886bbdaee7539c6bba71ad0927e455d45db2c7371a5b15ccd8c7e7e91592e9bd057d85a18a9a9d1165a84329a6c7beab031f2819cf36a26aeaa4cbef4e7871c472b9b363a1a5d597005cb98e6828a7b6b7ae81cbd036f8d8afbc6289efacedbe51c10f27462c81525b18d119ab4527d9c6db52cde19cfdddfd000143a024d1136d92c3d0de09ce4a3837fff20ce4c4307fca87b1a09acdba6a1df122cc69dca4ec3ca89118ade730ec8959d0dd84db0eff5ab2ff71793af68a2d6bf3a301edce9cf1088046b3177c18d90f6318e2bea3a071469873d1a320d5036a6ea1375ce17113721f01852e1c436745536bba80365d2ee1060a73098f99983d18511059ecac21b84131d845bddfc589a1a4c195ee1d89ba9845c09d681a87c3fe2322cdf571b4a31756d2de38276b97ecef325f4ada73b747d78899c3f84aeec26fc9732ef5843f8b2d7af0fee0f04e01f85731eb9fde3f0a69c4c0aad09f51db5cd03db3627cce5d2a1dfe9910817efc2e53ebf9f79afaf09521a3f14980cd20d6c0a0d48152ef8efbf7a58b809f5c28186addc9690ba0857cbf2c96e395e92afd0001781c2002615bfb1b7d35b2e208a1df0bd8f95f2d639422c213683828227885660bb231058546848fd01763a1ae99e0a7a1040a0f398d8171b45ffc4a58a4439a5addbad802fe79c71a4a27fea8ed229c51d6cb26c3e21127aeccd2f0e31ff1c7d38987c95b917d4c86439a7a0d54b3985ccc3072727c654bea4d473676a45f13ac693de273581cd6f864fba7ff00f3e61cabfb689689c49849419ca1cf489cf9db2138f65c1445b74a0e0cc83e8368e6e79149e699b6e64c77c70ac5156bfa98794cd0c561732fc7e3623673e1f0ebc09de026d9745c4986d498762be6799cf99143fca5d69d04283b504c8d8e325c8811853d467b72e204417c7e35064ce7bcfd0001e42f7a528e3e1de18bdde6e2d9888886aea8ab8eb149299c32c68f6c93a19889efc05e641037687a091933cc83bfbdfac77d48a2828bac6b0625260d4b9da29b0d215d403c6d3aaaf33addeeac4d46875cbc4e3edc1a865d6fe0b6a588631188897cfd131672a8c5d098c0ff8ec5b17bcaecac1d78f83b5296408c994c1e81f93021da25ca403ab6694fb3e82ed260e88067e3edcd8cd0e70ce4c79b49962096eeb1eeb2b17be69033a338ebfadaaa1293f7fe17fb7ddce051d9eb82c84d639bab4a415353ae82dfed2996a26d184189ff1f25d3196c67f34ae50a39bdc2f711ed6468b364d1f0ea8eb29f486dc6c2f6dc59a24acf4772bd542557e9ef4b5b93fd00015ea90490404b84482136f214f4497c09a0ca87dcebecc03ac3152ae1c3e87b945f721137c7a309e1036d0e5d94c6cce38c36b1645a62c7160ca45abcfb5c165eb696154ae38684002a150ab45f1b8f1bb69b28757e2503967a1432090b6c90ce7e3671860e40e95200f8a1ac1ad927b49bdc0a66472eac7123c383ba28578b149f121ad8b1ead1e1908858a640ebebd2e1b8a4f787e5f41d573168493115448edb0de580a8c281b783afe2b62ac6ea243d021187367df9fd28f97e2ca7c8856d89c64a4c3c5e2147aea8120b4bc8b2d0b8ec5b7edeed35d24a800760e82ab19cb7363c7b2fcde6200b8e63e2b698489e9dc0bc4c8f1ba9ff68b59a7277038eb920fe94cb66b60142227c026662ddbc3dc29b373c5c805c365b245c2f69152f1e2b2007e3634d06ba18b973add33bf3e23a7175729a9442fc4213adcb0e5a52e2cc272096af7547b4220ef6335cf5fe47c75896ef2d27e58acb6d7b444b3645ad1a9370fd0001f3642f6dab0eedbdf04e554106719fde491a9bfe00e8228f250e0035f5a95782bef5680a15e6148467d4c7db9e22d9a4bc766ba884940645845b25ace95d405744929adc2c63e41e4c807e33a0d514919c09e855c9d77690be00720d83dcf2b2276e157d39b7acb3ae262e65a8a09ff49478fcd67765dd03b545d7e83bf194ee6b0c5f83c41f7c470e0a1c3f1014a7afc2b7149c01b3f1181eeee4ce8a9be47f0f7f897a05683629d6164fb882e1b67765e7560e7f5c6be76ad9902a755c6af0c455156b93f14e618f533feb9d351bb956da352b7a9d63cc5082d6f9768d80f1bb703c84be2bd75b848f06925f47e226c424c0ae8ad4293b0e9c330cdb16bbddfd000158cc2778c31d0436228349fd19e0428de5916401bbed1f3668014cc51cc6b42381c32e5a7d5ec4d194037b0544284c52e151b652e2733b17065b2a98fc1f5ff30a8acf5dc7522b64eb94a8a71526e2aff0acba058b541fa412bb5344eaae4945ace66b4f9251e842e4b52feae5ad89ae1ce3617debbb551c09c87c6e6e69b7942f1178db84dd758fcb3f63d658e3f48c1a47b725cd2e003a59b8e318950a45fd908c6c4d53d706dfcc2041c046324e3925c9ca032f62cc825f0c11c9fff3fed99f616837244c7d71b3a8d79a8d5bba3284280b59ca1d0ed044801b7d4d6148f014c8c04e8859d9c3d074a7ad77b86ea0ae39fde03fb33eda53fe5c1728cf2daafd0001991691f21883d17fbf12b24e86f0f639967240dcf120c01713ad612ce32a7b8a9c911248414d8a2b836e82c827270f60f154d5de0efb7aec5b3db3f8b9cedbcda84aedea5fd0988ed0069871fda9db5ec2a1ba0f04592048f0040599023ac01e758886daa3fdd9190bb1c8ea29898cdc711e8e8e6c4497ae95ee2e5a6730bd8388d8fa812b21c9b97af84a7cc9f790139b0005dc86efe16436e63982fe8d8ca6bc5bea86b2297a0726dab7704fdcc8a3f6c273eb0f8aa008ad1e6c4a985d7cefe05d3b24cfd195d2fae5392a48be5bd50386244fa9002f95ce18efe97be356a3c5b3990172f812987d0fa10d7fdf9afcd20e165697e3e8f1fa564d1f03010f8f20d53cbea6789dd88800af410af54c3c346483fa085c6e02c088092372ce828e2afd0001f903226d53becf91fe3ee0e556a7ddd652e179dc3c2de5d7af38f380c9916791a37e149ec620429b47e5e0c016b898ee1dc45db857c93a718daeab3167c3c336b22692da51bbf7ef1bc42cba25af0b1fa89df2adcf41535803ad6e80ff1e1f57c80514f1d091040aec55e87c4810ea31ba22e4f93a101d71e324cfb2d84e381f1a59a601ea97907013e119a24a468b27558b68de170690e71bc3c2d7361ca7f77d116b642516d9cb128a70d6bfba83b2c22420059e8c22c9f794f2e144a3a065c942d3276253013efaa88a80e3d7f3c32dc249347a6df656df62d4f9482cc8470bd1bebda925d47c5cd9a54ce81d7bda23c2d0434a98a1f73911c6172facb08a21cb1b1935a2667416f5cb810c787fac55fe8e14146721452870f27b1d5a14fcce0021600ca51450ca3b29e9ff6b388f4df5286db585d027f68fbbd1d4a95fd756318700fd0001b941fa0b95cbce10d49d29c80ac6c45bb624fbcef94b9bcc75d3593a5e11ed85488b6332f9d059e0123d5df6486dd2f57a9239d137d46f3b9c0120d391d1f06cd48c13a0b847020d0832b15162461811662eadcaa2757e2b6b2240d478e7411c7e807e09ef824ecfb3681b49aa72a3319dd310d8efd930ffa7751a222e73f198032f2dfa00e978c9542dc476ca44b161fe470a5f63759de5086a18fc92aa375c608841c6ea41ebc6fb86dea22a8987f7d9abac948d5e67a173eee3b9b90c323c4f2624f53fcaaadd79427a36f560ce6cbd99872d8119acd2173935b0331217e33ceb4a0ef314e45c1a90ad06a46948a38a00feac8f58d8780e6d15ff6e013dbe214cc25d39b2def7e68e2d5c7afd69c5d629265f750b683dd660040dd3220a2ad600209901a1f845f602ecd4f341e7b68c6787561b4f18d7819c8b319dba6836a42517216578b022ea0911e03a47f0814046cea045fa63bb506730b0b491614417df91f900fd0001ab0bc038363d54f8e9e8ebed6a498b0f989c8ee56c81720bd66f71d0d97477d0db56d4481cc0e57c2316f4bd3a6843f86e5b28152436ba23f377dac267c7bb6501666efefb705be623d0491dd42a1a5397f45b6be6ceb1e0499842914f56296a34f304320f5b623ae7e16379d89394b7057d1b92de4c913265ba231d81dbf9e4b586704803baf1cf0fd474a721bd65206df02888dd77df03c831f8433f3b2c7cf7e1211c7c85a975d129f33734fcf77c09aaf68b681da7c506e8c89ac5394589d185117b722b757e307ccf31e9ddc1b9131633bd684ad458fdef09d346566eb4df920801ec4ac019081feda518cafe1ff9f9197b1473ddb18d3349652db870e0fd00014168b6756041ac27a58e4160cdc78c2cca30e144cfac7c58d40b5521c76ae171399e4e22bd3eb5a59d0a44666cbd8d38c4983f8e2bac6fa5ee84c86adbf9679bf8631dec545667463df5fbfdd5ff2d0f6f9021a4a03510e291253bd133520d5366e3c272bf8ec41a14b15aea97c420ff263bb52dacb3c3962359312e5a4690483434ba5f592057dc449727f03768f3756c24c85814e1204d2d90cad6e656d39e7dd7b9c4bfcd103dac62415b0d64901b01278602553c149f6d64342f16757b5ca1033494395404fe1ac7ae33d796be12b0d4f986de23186ecdbdc8477a742fdc973b34b312f5984b74faa42f5f62dd4938f76f5d7ec141249902825d81ab2d88fd000193a2f7ea915b7e506459b8484c43d305deca93a84b3c313a6cea7286d485395158806c3d7f3155f3ddadbfc913cbd7673193a951b356ae208319ecf406172cfbbad940f9fb6accde0c73ea5a19037d5d8644b4de22db968851a303717f473c491465712da34aba4e5dbc31361163ae1492bb3435779569598639943640b6e14233d8888aefa72580e22091b6848592d5b2273cd2df009d614da03d0c0803b4320800094dcbaaa337fd210820e07a8deabbd595d2b59b3bee5c7a27e585a1cef44e532de83f08f3df43d68f5934ca69776b8ee8ddc42969970d64145c093d4e989cf32ba71f750cd03b5c818c33462d05c6477ec1f0ec81c48e4b119d05d31cd5fd00010b1cbcbad09df7edcebe79a913033895c66686f078402893059e4a986fb76d28e89324f5b04ead2c2aa0a05786faa1e8f66fbaf66b7ca94bf52123e6afe4dab54bc33e5860e8766031dd256edf40b294f55062e613cafac04f77e284f5097e88cd7aaf46fd28f4fc24ef1ec1aa0bebbb3da5b504e4dca8dfc13ed99e4371dea6f3bc0f78cdf6ff47548e5a426c92095da045e5ccf45f446116e2c9567ed01d62c1fd3a78c62aa359118f77705174db6a4faacaa0b6d49b4da38e42b20e9cd2ed7979a0f6da28964e0b3fb755046f0a5477dda7465e00d13c8751c36255b7f922dbfe108a14e359257ad607dd7e1c9ccf330779135714b01746a25ddd11e565c821713c8702e50956f6f6ed9c283cfdd08938c8ccf08ada04309e47fe9ddf5cf5dc00fd0001f5e2772a310c56c1343104f8f618a6408dbdd5e421e31856f271fb33329d5055c4c73e06870feb8f7b298cb9b403dd6c4e87dd2137ac84df583c58ffd627b0c52996bea39da5959ccce7968455d3b4ac512c3d552cf63f2da74d6de8e8f037eca47a715d0d9398502b287e5f1034e536a84839bac21ae9958da81fe54c67165fc650f19679c9a628b74fb40c65257dc5b50f690263f9c1eaf41c3365c0e23f91aa7ade32a352c8b88b40b2d7acabbd085185f7737be6f5a7c342e2c908e8a4f997ec949e4bfd6ca7b3da894fd4581c81141cbfc01afc3eb86bc91c304c18c1dccde49d8b61e41d922fcb02162e444dd4931a076bb072ee3a6d5cebbe8683e992fd0001197c654987867c3fa8c71ce3718a70756063be0179e55bf302daafbbdfdd5669ec38e140e3204b260b1d54373f84657101147e672120d735c179fe2dc5fdf7dbb8aadee63b867e31d9d1242e45bd23ed510dc7a6d44ea7806159895a2293c0898bfa6d0018d9864f584a59003a8d4e3a38eae3ec7c9e35d1e5985acd544547ccc1e31024f05bfd5dc07ccbf535b2e0a0a703548e355eb9d1acd8a59684ba0fb674075011c7be7cc899c30ea6c1df0dc34c9366f47743aa12991192cc836db7fd5a3b0306d6905a82e13b729038c70fd3bea01def02b93cd2e71439a9fdd5fb93da6a72436ce9f5b431b7e18c76d8173964d5a02e24195e761d888a6a2532d785fd000177fcd4e94194a3c34efea9f8f04faccbdeae9f0eb8662b0195db08e2b41a08fed52ab7060465ecebdb2eeed7a768c4f65ed7b5193151a56c752cdf97cd4c6be8daf95c16c6703ec95b6cb541506c715983be6384031685d48190e611ddbded6fa377affb759a1f289523e56761857afb3c33b68f743c59a6bf2098a2f5bf3c1c8a3d41224dd12ef247874ff5ab6257cc3e29a65a1c16f3ecc26108142d1899a340be3db55556258a348f51ef61e8e73fa9db743f08df3d31919f2d9484603f0281422c2cfbd66959dc138c2176d6e242a5a04d5b1cbd8c39ed051486869f8b0f3ecdaf42498be6016dc9d49ae52220612a32f50259fb9a8be0cab88deafd56b12024a02c37de6161be5eee8cf63c3b0d16d0bdca07ad59fa0532678b864c13bc20c87f061046d5218e768801c69818dadd24e62aa4625d5a2af09b08f9eba8efb5e29fc2977184990fcd81a0a3f896691051da425d7b99292d8ad7a3bbae5ed9503c5b761907097dd6457a5444b9c85ccc829d5901d46cd917ead52a3316f96588a7b508501be9f69c73d97e0db9d615f20e50712f377663f6c47f42d005db72b225048abeb8d6e3a60afbe21cd13d4539b7d39806952d745a7c49a552175135d840a3f9b9e9ef6ba3c46e9f4bd42f9bbde0d5d5abda8cf06538d3492aa856d8708285c0aa99567adaf6e0d4b28feec0a55e49879ac8ee966e4520e5eeae3e6b4473cb710b4213973850ea6ff0331b04ccffab2c2c3f55ff28b8bdf644e4c19982916d01e28d745302b079066c61fc0e8e1c90931f122893f7f5eb86e9111f98cf36e719129668dcc4718c52d0c4c6a1a941b939e5e744d61aa9fb1aed6eca5fe062cd109b15abfc5851ff77c026e9ba7023b298d40acb1ae9884671ff1776aedabd859f2a481eb18b963afae2e2ec41e3f3671e9dfaeab234d6aaf97249f4702e706437d8e36f517b3b227bae68b79064a3fbe999bc2d75912d970826908dc17b815ec0f030e20b24f17c527557e36695abd05f67c60475a0c74aff42b5e7fd13efc3b1c3bf5ae6e3251731ffaad110c4210c9d6d78d69d6f68cc31ca99fe783a12fca506af01e28234f01e1b30dcdccdfbe696bd5703ddbf0554c8c4863edb0f252e2a9daa54d4900d8fd6aef61b8a8f1e165eea79a3f20663b08722762f2c548e0cd10cf0d2d8e8b9239b638d02c49749d55b721a6c81d2554b51b49191518150664b7e4dbde3ae02b36ab3ec9250290c8cee6b7371e0d3c7ad64d267cf463c4c82cc5a325aa72461345c4f45c8753de23bc148527b0e982511bed9e4bcd4cc37e1e3b5089373fcbfbef9a111c96cc79e9f37d619c0d68598541f2a37ea0fd614ca310c915c0d412d2eeee8f7d71e4a250bcc60d68ab3f296a5f75d75d627e5913aedf3646f733701218878049c420ce0089ded11b087f16111e397d0f2e354f1df0a858aaf07eeb8e80002210305cf1786fa950ae9b553390d6d62e2b285ebaeb978822439e0922403f9cc7dbc473045022100b807fa7bc196a7b2d7a3000e5e1870e2ff488bfd6e2850aeaefb3c606f28379e022009c3cec446550e5cb04483404a677c4b8406d85c62cb4d714e5ca3a50aa02f260028e8073a480a05174876e80010001a1976a914dbe6d470fa9fe4d037043533eff4f80aeef0c8d288ac222244524271336345713233515955416d48736f634336664452764a453753723548455a4000" + testTxPacked3 = "0a20b65181decb00e684fef238776a0a129db4e1ffdfc454f6ef323e5f7a8deae6a812e1ab0101000000010000000000000000000000000000000000000000000000000000000000000000fffffffffd8a55c2028655e8030000dc8ce67bfe1851477371a9ac40b6ae0cb8571f6e2d5285855288f6079f1ce7239ee8c85f465b1820058b79554f41af297e9caf95ce0084b7c35dea0b95e15a2fb9f8e62c5427c1c36120cbc1fc11ff344909079335209c6b84b45a9211cac960f64e9432ba5eb6e4ecb2068223dfe3d85b345da17bf374f9140c9577c148bcc431c9ec3c7d13bd2363dba821381ed9fa0614416261e88330b3e74c40e6561310eab3f26f092e72f3cab761f373d02680dfb52937bd9515be242f6573754f8f665523cce3bd606c8ad190954f8181577fd0efe7cc64b711d03774958df4a5211e44870302056557777951d7ff8c002161a6a59e979f05469cb31770bd484be6525625359979220eb7e9912e835065fb00fd000216aefbae3525166510814d1636b76b0d48ea3cd54a3a17b136a84340989d75f74ff952966830e4c0d59daa006d5a7190978270ee9475a0778afaf002cdce7efdbfad630f72838b5c4a3b538ba61b94bbd9e353437a50725af5f16fbcbf36bb34e7da54e5c24dfc90b545f95c973877bfadc2703ee10585a1fc1c97d7377bf41c9cbcd5a313849a3c826e7c1301083694e6dc05f46899a901ab4a8d7f6b3600df280157fbef6eca4c28fc610957a42a9acf7c4d7f9846ab6b9b04fa6abb5fefc168d45f10078b97d4d6a39638588a1c19e1bfc472657861a902c2d52cd32fb0463746f649ae88bc0602dbf35816fbccf91dc249be809160cbc7f8b6702d6cc5b81fdebd283231f40758afc899f6fecedc51dc4e5d09cb8961092220541f75ddad45680ea92b4ee78c29f58c197a68420bbb25b450c72d02d7249f7facf9927378620eb36fbf9b4ccbcb55627eb9cf905b4a4c65fcb77a537f642f10901b6e94afa37e4afb0d6d91194454a9c2dd8ef8fe4316f8594c7822a7d58cab09657cf501da5be5a44f947bb957b71e4291a7fc60cd5cef9f0676f7c89123c7ff1ae2e6dc001b6f19785534e207fed2bade8597541b13714284f67d6986bc616ef1b0adbe415242fee85acbf482a6a48b3f142ef7ddb5dd1c97a4b0c53c6ac7aceb8c042d9c9ada1bc986b8c276d07fbf8512a3dae6a357fc02b167eb85000040e8693e45fd000200e23abd27b258f9827ed58545a507bd465e255e1156610da314bf7df68b6b55129df84c7b19e362751ebb9beba10790c9c26c5ddc7f087258d81b006c0d2e92be0178bf5edf6e78e89f73cd97746afbb2551dbc97eafe32ae62e7f9ebcd14ad69faf74d2011d16f2c50775f4f499c87c3c50d9d5d486394c2a7f462675d2a4885493332e0610a78fc0c8b08eda42e4bfe93b8c7f80a911a7992a1deb7cca2e40933e1559815688d4e5ae5e58d706bc513e5108449a8393928b5b77ae73cb03fe212c6375b6c5e61fce9db16360a147e7f7fcd49e05a99711d4d5799be77f7e39d9d1397388d6680d4931b48798ce013256a586781ba80168bae63bed4d150b64a73f7d0ab0c9ebb42f5d4db40eeee303783249af4bbf334c660f8c084ed9a2e5fff8be230940a4a08b59418676ef005192365e4e67757288791ce4992b903a31537596cf6dad0be2af2418a6b9cc2c33e99d874168f6a29df189a869b16eb5d24400ab30e4eca9274114d646aaaa8bad45832b6c0ded2bfa698939a8af9d0af380d2afc58966afe0f45483ecad0f114b904cc2fafcf470dd4fb8f193795e8afc3243b4c946d5eac82babac6feaf4ff10bf53acdf8347fb9fb7a5ac4efcf160f0a7ef3576f439404a3078ea092f46f408a955c965344023d847fad7374cf145cadbc8348eeb2c5aa999ebeeb8a5548bb14e0092b184caee354020c19d66cb213fd0002729bdb186c4c494e17fd6effe29cbe7fbc539caca738d54f9ffc6e52a35a27da134192e2f7f4ee2a86af281b78670e662677e97a1ef008f10f42349fa83bf7841d88b1a457d38164a383ae9c6b974137d58216f22d135d30b9d6e7a74952a7e905f385141f4df088415d704cc3b03b2cd600cb5507f8ea1b53fc0e73031a3946c0d6269f020c9c26a3be3bfdcd37f8d3b9fd42538ebd72029fa0bd8eb57a4fe6769e1b43b5d5d7be311e12e52ee9dad67aa988dedc80ad616d7540381993d9de91a7fac6d08e4414254b9d1d72940fec032833a6b1a5605f4b62c47a86d70dbec5ec913d0a613d438cad385fedf24566bf79edd17238e55421520b95772224623f145100e663b2ba20161784f688afcf07a900ade1d48060d21be9ba9297697891c2584cb99a44868efbdf65178592ecfbadc92f4883662d6b21b7f266eb21815c7401b8e7da061e3258dd685f8cf65f2c2e407c913f85d053b05f6f92ed1299186632ddcf175ccbbc933044bdac5e10916917dea1146f77a8ba4b4fc8ce260b5deed395ecae9b81baa6b385fecca5d2982041c131ce02a1dec517ad2d459434aa3a514e7a4c6c1362401b1ab62b4c89bd7705d5072e0be5250c60c2fdd946bc73050d3b8bcfaa73165eee3660063f279e824d1e15f87307a40bc9e1ccc0f7d7087ba84fe9275742455241b61d3687d23eb9d7a9cc18072ed8be1492db46a454464090750eb0a393499a73d23f4c552ec6ae425a77f97d4285a7f287066b2198bfaa99073da6f4009755e59838e48cd5fe692962b87da3ea7e14b34b352fb2a4673eaaaa594a094610bd0cc566acafc21891b7b0c2470bbb338f579231e01c064f275c5ac9c2748cc50e7f2e36f2768d2a59c22d14b6b9a431f7772e716731c55ccbf086187fb15bd07a5af3040d468d4088ba9e6383f1df6dead9384758f2da81ee96370d9055ce5a0db56bbdccb57ab490f42a01e083b61c5157b3c00e2011dda865c7294cdfd2be5c03a1a36a4deda9cf03b500fd0101b3c97046a717a2f38fe2265185f4411cc68cce6cf9885a7a8fe6292eb9e3eca69fb6249774a8c82b888d22d5fcdd549846c3ebcf054c6bf07aa4d6b1c0d4d9bd8e16501f65373ceda249e9c760848fc86ea92fae7142d211c8bb4a287c91eb08cef7678ef1f445f76f81d464eb1d29ce5c6d6286d73d49cd0ef03b65376eb146a3ff69a487ec90b53c11cce613a586f22cd56fd34df6f7ad64fa3c68a6ae5c9dad99d4a3d2b0974ea1f627be4f0153c7b5fe472d0c562556c4d8d1c7c592bea45bb7886b74f8639f9487b2f6aebf4848b5718f2ce65b8f5a4efcf140e2857bc9c503f0058f9b6af16e75f2d530fbba8979f81569e6cc0bc04a54e30de4e03a9300fd00019b881d73a78b86771d41c0c2a9ebdb3899727f33d2d4d81dd27b6a00b4c7db265999b8442800750abde7cd97be0c691ed06b5d40da115546d90d4803e82c61d43eb5e2bc684adcf180be47660870921fdedb2ce43f33564541fc0debe175c6c49bdfb51378902dc709594b9b7d34d0af70b67c3c608aeb5185e78c1c39cc3080b71a36115f623a07a4e1a3e1f3e17b6f9f695f1a1acd9dd1319d0a0d67b337e64f720e5168c09196244bc71b083f302e042be19b6aa1f8ad61755f4883c3a1ae615252b884ca3cca5e18a023ad6725f08f9e0ffd60e7a73ccd29afc910d60dc99c06f5953c3e398ef615fca45f6a83f8a0be653d31a7e1a1666cc7334d9cad51fd00016f7efa38c8f9f478a6ea1217d8be9b7f0b01c5d1fa483c26e403eb3a4875aeebbd0e7eb8aab8472a5bd80e8be38df13526042952f813b71f8aeb4a2281bfe5e5d9ba70f7e4c9f477706da922899f505dd172e260ce5f008b59c0590d498ac50810e9a38d35f1a2ea4e9ce8f77e46d0b5604dbf94629cfb8b65453a0295eeca9d992365309b7956481a8c5080510a09183bd3358fb26933e15c83fe2ca6d186e631d72889a09464f5dafdc8a93dd100329071e52beb522bef1af0fbae0516ad3b02011e19a2b2924791b3f22679b78039c8356c0e6676e2451487f056d0cff064f55a992afba08f59af7606a809394772ff85c4c40673dd54eb30020c6a5cde3cfd0001ecbc47845e4a1f3c05c4e9d0a47e5e8996326f48d7ee1bd0e432c5f33fecf8a94feaf03f2da65525bbcb119c7928456c28f31c183a21af1acfcd9615669cf47a077f861f694bfc1831f1a71ab66540906c62b85274f63abcc53a37e3fccdc8563ca2153818b6b796473848d765d1c81d4e6f78ef8fce804184e04664c5c6af7c3abe4d92aac3f6ea99b84a3e53a7bf7679c26dc96804ac9e1443b054e3c55fe89315106a14646d06b84122861ac0ac27fe64fbee3c9807b0264a90eb9602187f4df2cc0c62b025ba70bbb30a7e43d533f32546d2e6f97537bc2d623a7f545676a37cb511614037d77fe21a35ce4a2275e7ea1d85b7bc19e477c61033f5effe7fa5d8ff48e2873845157b7a29718e6692704a9d864301fa254798555172c6277cce4ed5aebdff9c27a1bb37071677c16da15d6c1547c5b708e1988eba68befff1f1743342ad7cb89ec8731b567198953d965ef6e395d38b410a326ef3e262937c763179f22a076d17f848dc4e36be38d838d5788f121d771f23209a0d50fb3d016c5cbc47cec31069f7d22bd78691662d8b609932b9533bf828e213e5ed4e61f2b80f05acbc00fda0017bdc59c8a6a23d261cdf1dd10e91523503c3375a803755c29b6a84bac626708b7db937ed39f4053b5c6376b3988fc9388cc64dc07466c67704a32b703d5dd86d8fb8597cc2ff7ca190044c66638028855f29fdf2a0943c64125e3ad2487de6928bc34088957eb32d843613e6b588a91cbfd8c37c24ba655739cefdb203bc3061ff93498160c7e949823b0947c68b6c51dcdf038c538f50413266680ee23817ed9cb840e0094f6fd2277012aa2c6f82b086242e6332a4e00bb9c12c153dfea9340e681e63d72551f8830b2bb2587e5937685252928894bf90bfa174f62bf7ccd43415a094bb4142fcfe639c62f6eda0e4d7ee033f49f51eaa6a35b0f7f400992d8367a275e9d018a547430083c41a35ef543bb92e159efad39c5b84d127bc68dd581c308afd5f81654ffcdb3317dd6e21d5251916214e873c83b6ac197dbac6c1b93d79b9dd5da090be204b9765fdcf662c9296295610e42a0570a1503dd67c44e17936946f3a6ce61f82b13cf00a0b47d06f2cd28651b6af32ffc58304593d5ce81159ccc952ed980f93182fb468ebccadae4dab4565d64a5bcb3aec7a09d681fd24016a4905a3a143e4d61b16898fddbb0f9d7602ce865716b62ae4f9a37e2f6ab89de930e066db6f4a8667ccc4e79e7ac760642c33e5f24266540c9b4fbadda4c0aa1b6a74d02bdf2324ebf9598d8ba918438de1e3343be0b057925bdd52304581ef621fd085dc55cf8b45d605cb0b60047bfa935c2968d554753a615e75f24086b4e40508ecdeb411ed26c007a1110f3e73f504d7fdefb275cbb59cf9cd68bf4784b8845467fac90275f3bbcc2c14a87fbbd7d111441d6ba0833b9045db43975317aee170242b291f8b07254d395472bd4b67db7576bcf2460bf0c182f745a6cbbec3f680b7c6e0a85308bc3af8af3302355757a77a2fe3f98350e4ea1b3074e37a638c630d529141843583ba4b802e089e0a7ecaeeeb42079e072e64fa5251782cbc67ba9d46e4c7f502d44a06e5212d09f4dc5bef1f1dfc376d4a042f608b860971c44caeb3735cd57e19401314af06a73180918af7693ec5204b3f858806e6919af05a1a8c6daea2ee3f08fd2401c082f09f7042a7a0b6b484287050a15f5d8c1011c200d42eb51aff5a484fe1de9feaa264ed3022b6f5b1b54a4a316d3d7e2635210ad83e2d3c497bed46f417ed22804682529b034925dc785a2d74361d1db395d1681d71bc1b0635908ec3e92f850577f35912fe5c173402e6e2ed8003c64a57bb65a2013014a3ce14ccc725f733a50457a696396cac0a551cdda03a2ec81597031feaddd801a9bceaeb5f862a4cdb3eda06dc317a96b29c27d78dd977cc6f25d62bb967814fd1d7e87c675042522c904fadf1cff80289374de8f98df511de975011d877058aa7ea9cdf186ccfa5cfa5258e581ee7ee73e16dcfaa82f079a16c95e6ca4f49c037b2423c12006b549a0b80c5dc9b93685fd2a4bda99e5aea74eafe9d28149e94adc538198d459c7a9a45a1fd24011c69aaa26aa94954fb7dfae2d63eac286b4979e7d513eac8144a7bccc34fb298b7c556a82e7450bf590f1ae658c474ac7c12b3fccaec877b6bdd11fd9655a27b7b69b922b1629a24a8d7f81a6827dd22cbab62d121f5cf96d59be904022360c5bf04d2435c52541ea4d7f932e19fc471a0afb38f2e84668d974c57c963346d790a670a699ff53b5557def1ce9650a8624bb2065f9ce99d6b5361a1b39629040fc897a5d0a07816618840f86601508e90198de64d7091a8bda406009948fa9ebbf2f56adc66e057ab23152504438062867c4237ce2b99c020add16bf66a0c06a072c4fabd9b9fd1146b73366535d934328188798ccc18ad37d30e06a39b8e14468d32820d912359323b7d474dbf507e894a448a4e921f70d1fb4d4ac03b178ae157814004fda0015c202cbc492bde212955236761fcad7de9ec8932946b5352f6c35a242d39b354a1f96a706bcb84174a5b11a7e51cf0adeec9c1e5c88a3f8f32ca81977db668650734398e6feb45cbe0ed5c5d8f77cfa67b78f8c7c856629e6731321126d12ebe70603fe28d4281f5c9165e60749bc8688d7ff3cd509d958dcd1ac7b068a3548af0a3e20b984bf4406a6c6d55c674bc83240757e3a0515bbb626b6b6b863fa7029c5043d67ebcd531601d39b8b6b7e507a176216a264fb80f574d7a6a587d5ebba7c355007630fc49127368f6b672bd12fba956db75f189bd438f5034badc04d396b04a021608a7888434eefffd082efb0c3196698d1b015b42eb6f5a2123c085d37841c0cd6c7a1d77c55d61d76426758cdadcad3d7b1037b5d94c29962d4fe218c9f98c89ecdd9a50a48bc534bc167d536e11906bf594876aea01683269255701b705ac454b250abd45d10f4f8b881c6f4a360014b450132d750099100e15c70ef65ab0eeea340604f3c7b3eb3c51292036a6cc2843989ea418631fb595ca9d7e23a88a3a7d0a8dd5ec1212fc786dcf7107214d5c4d3f2cffffe2b1fbef09816033fdb616b9318b80b32f045ec1bec678dd1500480154ccdd87365d602f0126b57015784abee6fdb9b7f22ba135cb882dfa738baa17233fa53c68d35e4102f185c07a1310b710ff2d63db7238d36487ab504e64e239d0641137cd75cb282e831621f101663efb2f9de2520049c0d08a7c965477fef9d575ba31e101faaea20f96e40046132cd3aa981e8f360cb6a9bac68844d07c7d1741e56b104f634fa1a0c31d64ca4526de4f1754effc40aa04b9dfdb6f53ef77540d66fc9d0a4058fb46518433a1467a71873a03600b47c81e0d452cc44728e3a625235c84f7f62d753faf91fdfa1aea056925168616516e5c4357a7e82c94127fe8a4626c868bb7bcc13a0422e15e31c82935a4e1b9cac496bb456d1318e2e3e704627f1dbe646f5f1590283833193cb03f28e00f5020fbd43c5fdc39fd35498cf4fb7471417e814974331500d8e4ec8af34af39b457d20843d897d0e015456600325ef702c1bdfa7bbb3f2e7a74dcd8fd77beb3df4022320108d0f48a7a9b2a32e1587eb01094e20cb7c002d3de08f481a1d7cdc6b3f9c7c20185bc7ee65a12aa6003a58b83aa90d90baef64c7d324c662ea5138fbb4bd063720f8f9111e20def54a90755537e44fd506737cf1fcc4d0b320bef16eedbd750f8b2002e3af2f477e9d2912e50e4f03e43742c19e6e94c1ccf84ab04f0eff342bfc1020a544d7e745fa08a740418aa6da398bf10870a860427dcdbb857b6b41cec8e1342059eabb84637e61530039918cca60b860ef24c0df9e586b7ee9ce89336d5f1f7a210f0385ce2ad9f83a8534ab0325a42495a9cc4cd4774c9e8910bbf4e7192c2e98001fc3aae0957bc822f2aca9be874e1493a0dbad2ceaf959996060a2bc1781c23520cd178b0cfdcaa92929b9daef0c4dc752225792454327bc351b63765208972820203954fb6c5f5fa020f757c1bac415fec7e1bad3b7c19a93ba5ba53dc12f5ffc6720e7572f4709fdb74cdf2dbbf50a7bec9c10ee302cc2c5dd878bd109d1c87bfe3e20883ea4bb25deffe4bed0029e066230311cd71a4beafa608d43d615652cf9c146204c98a01cfce1f0ab321105b2f2659d375f691e2f6cd9eb821adc512718acdd6120d5f64f7950ad04508b36baeadb52228ed3a1139512125834c1f449b2a9613d67204550d2bb9a9333d567b6c2ab154f1fb4bde51d4b5c50307989ed07100095485720109b0b89090c8a2fb0c51f3f9ca1eab0e36b4d9200396e7958523e57b705d11b209195a60cb9f034fced26b3336b0c49872fa13d56cc410f59463e4f312c93423d20f52bbd6ff9d724752b5ecdb148a47e86c1378111d76e77a2f4434816e325c62920510cf87382f2a4c1417203c4e17511170ac616fca2caa49b521dd721a8183f1120ee7ab5b0de3332ec1fb81c2d5baaf0509eda6de26147b081866c2a9aaa3435882011ce790ec01637ff533a68dfd8fa22733bd7e4fefe18a5796e9bfaf996adf54b20509ea869f679c1ae8b074619487fedad28874cf9c93d01003e9dd0df2f45b66b20380577ba031eb78adb2c71bedc8154056b157ae0d01203c19d7df420a424f71521d04f8ed62e7175b7e70d7f92dafff586e5a60be02901f9f67e97374bcecfe7830020c6be51acd068843922b23415a1c7b39f4846da7584a496483a36612dc295968c204e43611bdc897913427fae390deb145024077c5808238d960f93c62a62f70e6520e2f6ace6ca1e8b6b73dc2fc3c43f3d1e740f1c663e3e0ff9415b2d282e1d377420bf6b145630d553e8c6e40d84626f01964e3d6befc33ac284263ee2c35d76003721a9d895ebe788be0a6988027ae6b33a26bee33016d8f4b64255f320d34deba4900020c8bbe181b5482c9352b9d607eed48dde4a2759d765bc2da53cbc1c03d68da30a20818a0a41e03fcaf75f38766bfa81738f0c8ec2fabed7142e998e7b55e014300120b61bcde758db04bc126bc67a1c87b9fb4f8eb2c3958b9e4106306508e2b22c5220cec0548ab3977bd4bbc802483f0c2ce3b1d6dae43bc0f8c79167e4a76ee6f7522007f74fab71a89f6b94bdc8b5128de93ef2be214d04fbb8ac8c2832e1f2ea6371213c26fe406e69b2060b60c6d0eb7759fe85f19cb2288344239f2a398f04b2b8900020689efa2cc2baa15ce24852b1f71d08200fb1f2ab8e10be0c4db8c8263a85032220b1f2737db4e6e55370ce004319e7c5e137f3567bc4d5603e4ed1f5c636db6a6b206dc3dc994eb2ed126f1a76892117b18b24028dce73ab6d35924ec69df9bc361121fb6e92175d959528d8326cafddb4ee94fb9ffc2d9c8a413898f14cec99d4299a0020349eb2d5c10d69df3bfc41f99d6cafff21ce69323637565e1cefa60173ad0d78201818225053a5e2d51745c5002281935d4dca8ad82ff76c5ca2dee6be84fb767021e8d31f169295ce6052584692ed9e4da87e637ca0b52455f216d5036c55d45c85002100c51c6bcf08c0a3926b2be93b47d0ed8955386d369ab8d6849957efb23904a20020b4b64e35cfcbde2461db3ce57292361aa30d136b0e7bbc766f4aa3ab4a72b66821e802f9eb8168e002b64b2b7ccdaba46e73e5c422dda18311788b19eec7af5782002062e41e97d6c8f4c6ac4c0f978f6f8488c92a51eb5793ad109abee4d6c009256920009f7f1755459578897b027bc2b282466aa1d3b9b0983fe4cdf51c9090548d5120760bc9a32ab7facf7f3c1d1aea1b6cab28bb65276e269472cb24ded949256c0121ea8ed0306eb39aeefdd4f57e98d25cef9ee6288a915cc96207ddff402bc2fe81002138caf88bb834204209e39e6c9430ceda0294f1253f8be81917370a34eb28149200203aa2e1582961cbf9b22333ed51aa4512dddeb3cef2e4fdc3644dfa5145366618203a0ac51cf2e1a98ea343e9ca5fbead275516bc20e9bc421e967be939026677682164c2810712d79c82e75116a7e173af1208478e8ab6763c7ee58551b4f323f78200209943e959b9228e61e5def9b2ea18de1688565a2c49dd98f1c0a7d43e81a6800820c4ab26007fa076e1ecd22f104a860f10b22b1ce2ad1229eaa2cf204815f7246e204090f4ac3d42b2793d83aa79103e0d0488ae1297109a6f47bcb29d8c8b6d383d2058ceed8e9f61bca997b4fef220feba451f5401ff7186d1348aab81d7951b5e1d2014994a638981db8c53c2a364c744009050975bfc23fe7bcfc5c8c36b3df0bc2120e26863332f579b931156f0559025f7f96a4d72a9fd108fbfd5c29a71b13df72520e84680238c100c359efc6bfbb7e97d3ddd4d1d24ef3fe8a5208a714580e9aa552108c2aed76de2cc32cfd4d0fa02eea4be069e74da620ba09461e63b5127cf9d8e0020626acd1bbc9c821f30e5ad5b682272cc4b87a9e05323357a367e170ebdae283c20ff8a87793a1dd9996598d50333ace7856916b2add797fb4cd7cb7fdf24245a642165e43a1aa272317475fddbb370cb547766ee44842ab25d83cef370304213eb8b002121565e2ca6c09a263cfcd17fed20fb6aa06e1e798276a5f0ca8cc16dfc5c309600203e8034276a6f2379f8374e8fbc709a692b2dfdf271c0726c4f890282a40eec2920fa335f7d0543267026ecca4424d15cd07755568dc93c6557f850031098fa55572036cea7284ab1abcbe3c7a83ea779392fec2fd8c1524f95c5bb481c67e300600521444c7d18c458aa1e62e5efc656b27d6396c2fc9299befe67e3eef807711f948c0020829cf5a6319d4467027f9057dfa46e1de952a01233e068f7fd18cdadf9a3534820b622a3b592686cde240a056840080c5116582bf166f4655ace84b34145a78b0520fddc9480245d4ac36034ada99a182ada449373ea0fe16557c3c6d9d55b21942720e59aec0bc8b52c1ea0179ef1bece2bf3275164c8b14ef9528a1335dbe13def6620af95305f745778a84f8405956a82816f366957d31e74af5509ff0b7f7dee737221424e7838cbff036afc7e59ba67c6add560e242a1772ff069a31e5e320c08428e0020f4855f5217b4a20c250b75c69d494ec450321911f9d7841e489d43b3739eca792010f35a5fc740eccaa7fa007f33b028089dbb8bf539f560df19627d5bdba6fe3f213c35de64e6b2d64bdb6ba246a5c9b6a91047e35240db65c383a1be9210b7838c0050fd000184213201216f3c4d446f38e3733348efdc4a4dfd79febf41f03567e0ec2b5a8acb93639951dc506d8649ca6d1926a25b4f19549d2b3700cff07c100c47c456ae23df2e60e27f8822c427e2de12038e00293de92621bc4a27d104803e48d076e3ffcf8c31f549fe9f95c6b9233be396cbd3c557efdfca11fdd91397e52f35d6b3a2130fd46ae505dc66bc987d8e93689d41be09a1df024af243b843e18f298de218ab87e557c782bd20648dc4fd4a5e654f1e9fa59626663adb179f51fec2f5534f2f4929ceaccd34d6928ca3d42fcc5efa32490428abc3296147147eabffda85ec2be06be4512448aabe1829d0219902fe1e9cd2bef29a8f89ed8eb1966b89bffd00015e46cc825850532b9bd9d584441772b3963c554af1424ffa9ccc7d7de55dbef9456a3fbf7b4be985d185eb898e166ba646daa12cca359ea77bb7a59e45e8a6cf2708c16b5ccacb708839eef2ee4b5b6597ba3c5899c9f5214bdace3a521fe36cd39b77952d1bcc81f9e5edfae34f1cce40369b7ee492701351d34e231af8a3768cc158a796e900d0cac462f5204da5cde3abcf561ad60f91fc8951e4fb37166ce37261649f840aa51e6ff9be749d72f1f5b5ae3349f14d572860dcf36aa4655788b8cf5108dff7a4e231d2e2a3bec0a159a1e16c9bb38c4052439f2b6b1a62addf739772a1d51057c89751f7518a3b1cd7b37c4ff7d621a54bca5b3936b137c3fd0001b4a09bfe02e11668f8f6204cb9ffa9817ec412c820b6b394ce2cc3a12546ebc5052ec1c74876f3de8fa22e19158198cd04c1336a792ea47e63c2bf4e85dbe7460501606c97409a906dae4e7ad84517a7955793365ca4f49b5f6829efe61f52069fa19cb30ce0a74d415898e7e134ed8c4106cc9d6be32577e9a024b9dfd8129cb5efb1ef282802fe0066aa41e587ac9ee20d6416010e1b0772a44b6d6d1eb4ddb32b80b288952b26323bbec16614c227e4599cf721484443f56571c7b048e58fe48b1786c44e4979e105196eb9b5803795b8aa3d3e3a8f85e10abeb0f7b43b9a62dbd0905af620309ef74f281f39b241c4b4c109ca7e0ec55b13eef8ee0544c820b833fcfd65b4a897458cb9aa376c6bdccf1032e099598452130bd38d28096322fd0001b6c9b1d3d60a49cdf61f9031694f9f790a4e0118ae39cce789df472c13bba207d101410af89ba4b2a88cff5eb09e53593bfdc69a4e2706633a45c77a6d8f4baff22c7e5e6b32278dbfe97100271b1fd1e64463fa6a757c4cf4b19954d1d363494664750fcde0f0b6e35f9e0f08f4fb1e438669f78b10e800943840b15541a31c3090437364159681974adba314bcac37e6cb3ec97c3d8e0cd52241a4c162787c199a256599b97e488b8a75a46d2c3d8af1951ddac43b0d338e739deed1ba26a8616f04d124244f365573b602697aab0b427a8f26f1a22de77c563cda13fc03a030817a837c497732c6108eac62102641176f62d5b8e73d0e27e17586d6e2c2c3fd0001ca7e381637425fa77d95f92b8b491ea1398f53d763a6ce32a2b2ff972ab74bd377723dde5302c487fcf7eac93b089457761ca341f143b7c5aeb51b33cf103f2f4ce4c282bc5bb8c7290c2a9dcc82fce3dcd9669f660872f602de46e869fccdd99c85c06d1d7bc9af4e93502e4ffc385b9641df8f21a25a14b8e4cf99cca023da529f8689d86418c37dbe3d4116e08069f3d08b9c952f4e2164dada9e62908d57cc68b264df8fcbfe449c21cae576adb8169a97294a6bbd369a58654cd182c8403080b3a5a916c107d7271311a291841f3e35e065d48f6023ec4118a3a88d417507ae86b6c74e6ced02c768e0f879844e22862c357a9fe22574e30c179e2243e62192455f24a5c214eba9746d2a8f6a47625b53e5e62bcecade179907fccb08e4b200218a8694fa7fa94c9c174c8e1525bb14dd946981e66b1c02178cde818615e3b6910021fca3cbe539842d405545a71cdacd7a795b6f6fd1c1095497ee8ee215d5cb8eca002039f76b8f7067cd9cf8bde783c8ea6edee7638113ec6729093749c2cc0c15d13c2091ecd1eb4b171326a6a26d764e72e09160ff2112e577149bc7c24e6c0907324421cdebc7f780b73aef4358c6f7e5c9a78ee95dce81c4c1bbbf570cde3c43ff5ca00021c6955241d2f09d2e90e33601fc139d0603919a1011ea7ee89f0e5d53727c45a3002037be468901ac92d7c3481ad63d364a93e2daa023bbcfaf4f6fa40cc54974de19209178cde568ebc660a7698cdd7f83d5932364eb8ac144e62cdc045a00ccda5201fd0001a3347a2cd4b1f5337653554be5fea273a5bf0292c0fd60908c6e699802079912372435166baf2f71b35ec0e00a714e9f7c10096d0f39a65f66f220057f369ce7c8221dddd00e90604f9de897ebf9f06afd41814b57350ca2d7ff7e08f60c94dc1aa087b7975936832a7050ae4a14b47b1986e70d61223cdc37dfeabc5e4212a869d953450202c9c12db23f8819faad59252173ac11c54020fd7324b4d39673befd2b585437affe9398e7967772d408c6db860ec512f94d02b0cbe9c9d414bb4be4b08c3a420a549efaedaa6f0aed7c74044785b611b2d65abfa33374748adb690a54c8719131e9dda9d46afc9dd09c8abc498d6de657920642d3d47a55f17fd3fd000189cc7f3247c6ede0becf1ec89f5d92142d25a713e037454409ccd146dc18c58319550275149342c690f00b1e060db34bf6ea34473c661ff9f32a40214a4715bbcea59a9ad6ff976e43b325135ca16377be53be39255f9e45ab4b3652d629a30500f9e37671d56952b215628ef42c1e49046476e9a760041003742751b158aa85c4e95f70e267d2631491c60cf09a174816890a2dcc2f8a38f296389c3ea66a6d577301297c242a6b9ca7465d8282f2e72e7673f9e1f1c8f470949c970854134de397c08c06d5d21fc487b2b9529c901c052e838a087f15b802d081e869cbb311efed66ba31c17c73cce1e0fc32d58d35e54bc5524776ee3f45d7b8f81fdad4d621b95f023b2202f5d7c814aa89c27b5e0bf5cd64b444c711c2e1c4b805af4fd58300208214f56ae87b268fd16d6df77efaa715fccd1fb1e6298c1c4f7a4f18d39c2f75fd00013fddc975fb72a66d5a7f3ba6df9c1ff55c7abb5a8f08317c6b346875e850223d87d72d71d9b17c11bdbe6eca34b521fe98dc8d31bef6fcfacec74e5c4a6c5f00ac769363df4720e17c3b687a42f29e8edbc20184dc9274d8546368e3fd2408bb68dd224b73a1487c10f4e29b0d8b9823f7c73db26ed16baa4c5d75b162cb5a97caff3cc8957072902538e0e698fad2e90b471777c5e8d90e5cd313933c2f3b30e49b6cf7ae7dcc09ab2e5e464601f093973d99c0815c3ea587d1803b4ca1d9bcc2ac20cb818f95d9239fbbe62c3356ba41f7dc9d2232f6221447fc4858bbf11ee382bfc639d9101943d958a59ff9b81007508c0879c4bf16885bcd6eb2383898fd00018ef6741f57def1a55a42b565ebe0451f0208762da626acaf0cbaac6d9bcec14e22c15660c2a0c5a23b27c445ad968a7e6b2daab11463040eb50799858b7a5063965f947234beb0d42fe1c2dc7bdce7fcda3de1bae384b62e798995eba4836dbee41a8a4de269c026018a22687ad77985d049bda1d39b79528b320ad3d22d893c547b4dc4acb57fac4e0603468beafdf54408ddb7d4c5db0cf14162d0d735b3fad10888f0048035481e5713b846761838504a36c1f956b072b46f11c7c6fc86c766c36fc7dfd5068855335b96162d8b299e3cd21060fed730310d7748c3d16f228b0eea1f37f5eee5453e3a19ebe1e60e4f2834d3338bf1887969de57ef02a1bbfd0001b50e09d8212707f0035a46513208bdba63a5d1fd7a7ea88e181d7a3de81b8afb4ea4b0428eff67c04b27372d73896fd9df443aa9b2a42cdb8665271bb1c54833454504dbc80645bc02366dc998d334b2723bbf5ee1e139265c107db84774b4884f26b57818d39d05b47d5bdab25778965a0627a96ec303c743ba57c0d32d0337a6d3dbe982284109eb0a7976221816cf3fa7f2e3cf739f30be83f253564af7f411358d9c5340fa5578120c48b617c088d9a3d0811a575bb1e96b746e25312ff6d87b9705c04a10123c7606b4c5d6658244121310ce3f1d062250b57ee51e35e0d7b787d38206fd9d26c70ef94f337cc7108ae8ffddf5fa314e6c4e5e1538fbb0fd000192fa78c12fea8a45902ac86e2878e8327b5f4c2ad9bec7d6167bc5590df49cbddda4d4d652f6f087f2ddc7a2813aa5b33048adc5f900eb4acd983bc86ea7b89f630b647a9ef42ab1bb6484ebc20989603144912ffd80158aff7e7d40a1a76489afb21f5f711ecc3ba613868a7aac4f28f2cd5440dfd064c4874d4e398a4718e10de8f7055e452271f01b27544af7035aed32259796962035d7bc37ef843cdccfacf969a1659e354ec27efda8756b09c0bf855f4fbf7598273154b3f517303b6169bdbe4ede890a90d3b34c917311027afeaca7dcd27158b04886dfd81803594292cdb3dbb77ad3a8df97df63095fd28c275b956cc61faddf770608b898d74ca9fd000138b1c34ee04d01368d9cd8ec4d70b97633951ada508c031470cb4cfb15a62426276c7442e7679f926e93325e287ab9ed87446c144be02e559dd076c9c5e1d6a6a7de45438076b1bd7b9ff5a821070877c1d9626925c1f47838e56bb6982b54fb9e131741bab5ea38aabbe4003538c454980dc3be9a436283edc32a3de0321f114ad32cf34e860ca36f18d476980910e564af57da498cd16d08af8b4d4696a9962adeb298d7af4c8c4ad9cb911d80a2115e833f0856833232f92f94cc9c3a6a14270f4c30ada671bacc9aa35bcd3c945dacbb01f4556ebac4e52adf8790dd49fa799584d227ab95afd2da9e67e5642a90e54fc8b693a07384577d04cc40861cc9fd00012c7977632367de82810aecc22a1a802a1aeb6ac54a4a79d8e61606a6eb72effe1abd74a0a18ef83709355e77cef5666f16d94c0d710f5ae3973bb26d07c0a436aebca4d156d42952cc8cedca94162225b5b4780cf47a6426757758957dcf2f638ead2312e67e140ee8c380949c0885c68b396517ba122d90a0ed184564bbe67bf1d0115c77857fbb29945ea00d8e809c295295494dc8cca091d900837b60b31a79bacfae59fe638ee8a2208942e27f605c452be3a4a43cf21e8e0f78e7cc20ffd2c546a0bbcc404b415e4eae2c7b9c2f9121bc741a6f8c5c28c9df3e0169cd86809f5e235ce17fb625f913f94b75b360f9097b625a5305040c55a4057dc4a68efd0001d594cafd37d880576656265ebb737602d6f3b9d0feb3147d8c1f6e1514ca64e3ee046bea5e6892d8db9c094198fb3f697ae6fc880075bbd461e9aab7425ac3a7fad84071b170739e7815d0c76d2ae7fefb718ec132b32a842c58cde33605488043aad9c5df6e58401994a38dc50779e348cd18582ca74aaadcce6df39b221d4235a88000eb2e3efed6744abd0b68836d56ff5d9ec973be549d52a195195725caf3b8cb683da9b96868d35727bf4b74dcbee838d9c39a33d15609fde1c2b345974a93c030952f52da7ac20b8c26b340fd9c08f56b97ccce21004af28ebe4946a91682f9738085cdd9a53e160346cfcf396cfb59465d114c600d9571634aa3c880fd00016c714ccc93e995491adb6718e9ef69fb2cc36044d9e6b73130606b9c845dfdc56e9518a120237f58fb6f2546bec6f47ea6a11f3d898baa47682fe3f537542fe9f6d3679398064a48a3ef8abc92e89eea84c6d8ca956b3c40e9b82b2a496bb1230f8789fc7b0befc061468049d416aca3fb41d4272304a728322684f9ca6125b91dea97bbbba8c83045b5ce8b3110d429d65998b3aed570ecfedd176e98a91eb3410cd501ba52d176bd2c8d05e94acc8f352b3cbe12adefb82d34e174765ef1197f8a93fd4e03c98967ddd0332733e5aa0ce87f63aff78a2b44d10ac63c1549d9a9c6808b743e6f785173924d736bc0890f2e68c1df72b0fb1632bd4679e87690fd0001e736a64f64d58af8d43f0980d29ba57dccea56ff13040270c926a2703401b83859a94d6cbf4cd62109053c9e6ddd1cc61ac649ebb1243037adec455891642de8f43b57afe96cb63736e3e7d735bbeec8d1dc2c698f37f0bcd85bd84cc6b7e25eab500c03f1c62ec730c24208e2df2830d32842277d9e5c9416a91217cbdad60d6a77a7127b09e7463354266a1130d1f04de9041f0f83d41246766027700fa9a02d10d2b0fcb0bd534d22ed38278ce177bbc1c429c09030f105a67db70011d3eb24754bbb31f8a6a98bde215f635409e4cb8c3d769efdd7f1561976bd29876c8a11a130cbb8e3fdf15fd9ad0329852dffd794f499345c3fee09eee21997a5c8a021b993d7cea74a8935b42df2d55606a8841b1a4a8fc0321701d5de9bce1dd1bee100fd00016e510cf2b787c67956990655ca7ba97aaea25163317e7ebfbcf2681b29b0b821aedb8f9b2e309d37f660ac7169dc8234a7e7e4d01e79164eb75f28d284c52ea3d7edff718aa96db6b0b6781366ab985d202823130ca53c2a8e5186fc18862348952e967b1a3e3636517c3e3c48d8fe5e4ef1d5e230ab584964c888c61393ee3d6e34c50446b86e68ebfd048ac86065f9a9c4bbfc2474027b612dcafbbb0416f12d3d856a96557529d1144a852ade77f50f600ecf3c0e00296576eb49a0b211baaa815cea78e1b7a56a1c698ff58488378722f58fdb1ab8bb0063654a8de8344041fddd04be1b4b2b1944ce9d1ebda667caf9ca00e5c2de892a9063a449edd8c1fd00017140014f19e2474e1cc4b40a5a8033f3ddfd960ef33c7e35432deabd85a5b2c18a27b85477c9e6fbb2d8a5e6c686078009b1f7868768fbb5f569ae3429fd64e49cb3a62f32fada09059982a03a20494bd2fd2caa75a01b3ecdbc32acbbd648905d56cd2aefc714af1c24bd6e8f06b1ddbb85e9882ddf8f0e67c654402bfe2e0d9ba404bb3da2d58305184949ce513b3784c3234b39d25e0c6df741683750cb46d7856e67a0d45f4839b5305f9965808d41cbcfb2ad3ad18cdba6744eab0148dd0c1d5e687b6e76bdb9408766b57297be6fdbcf9d7e3e6918c194ef32bb776b5ab85f3a6a164baf866d93ecc3acaefbac43a9fa267bc623cc136ad712b00d788efd00016d3db1d97d5d429249f5cd6dc35f61d0b1b44f1e433cd5e01d28afa6cd6718fc1432b77e8eb6418b0a4b6fcc6707cd3d5c6661cf57b3b73b78feae7c89e25eff2ec1d465be91a18b2f3bc4311919f410cfafe8ae8a06b4b947f9b6fd8ac17453c2f558dfd67f71829be66250d40c3aad6e3ff52d1c0c455e7a4f646445405cf1ad45ba65392bb622a770ab0f5a57e73d32d98ee49d73ac7dde7dad9b9c8e1da9d1e6aef0f9cd120bc1d2cbc5ad819190b758be385f2b0dd736184382af8aa419ac7d734881ab4728ea927b6655b7d3faa4a1e14caf6d38cc706a940bff9c9021e4b6c3ce516d0257deda35d2ac4e0423f01ac849b19b919b773a58f34a06c6b1fd0001464885fd950d292d5aaa6155164216521d2113d795d9aff389771e8f39ff3d96afb5e2359fc52aa6d6cbc3921a7a21e6f3fab62e748337e2cd212456e2b2f52dcb352a75902ac6fdcce93dcf027138be788aad5e09490bbce637751cfd5bda9ec540d7daa92eed7b27ff1bf66585fbe3b39db3de9dd386ecd7671f2395522c1c9006908afd04fe68d88194cbbc3216377cfc27c4fce55c8e558ebc4943cbb477a1172aa8b344c08d6fb853e64ff0f986b7f3e7cc3b2c3d8b2abbc43e08eff15787bfd6a9a8f98b207d8e2530c0c37a37ffcec2fecbd726b4b845ff48a44c1ec7031e4e663ec0042663ff81b9a7fe7d599695737511a685148fce0c3eb01513bb20ffe083f86d11f3c552efeba225a93eb8ea756f45c2e49a4dd08467b79a14000220fd4b0ae4d4c5a165ab75af08922366e89721ba7ee52751e0b3e64017787b9445fd0001d0a8ddbc130bbf7bcd20bdbc8728e812a45cdbe602dcea5826f753b94e1220cf698c1212464a17ed846b29db82b1cf5373241d4117b07a8b7d279d4e8511d22c47be22291e9ab56454becf533b771e5e602542d07828952d5ef900e2548739d57cbb6254c667bc50a0f97c11b7f3dc1624111f9d32cb0d85ac4106b41cd6d82db7aea1135334220163c190744b6daefa456f69c331facf9083af360db6f2a2c80c423357c8fd1bc28fcfd42db69d733efa9ecdff9df079bde1b73a63ee74e5af5c75b67a4824a72466e17b501f6057a68efc19627d115f19fbcb48c0e0889857f0d9191db5875ad6d336adfbf7f09989b2aecfe868c2efc2cc64af46d07c44b9fd000151620fcd4091a3d3e78e8a1f1b5ac9ae8bdd3760320ed9bea2e1b237110d7747e9894704336b958fc92eb200f06507cb56a12f202a8b098bcec5b7b6941dccc18d2bd968538185dbfdbb6ea61eaee22aa8ad24d73df0adfcfafaec181fda3626479710bc19835a5aa7a3b9afd8166b89f5aee8d52589059eda61f19f6319335dfac7765a9a9e22cce0fb3236eeba6ce250ea0b7cfc4a021ca3c88859f556dc1137349a7ad5a628bc47267ad91ff86174a2fe74e3ab298ae8917d6a57a916f00b16bc0f3584b12b0d63141a20b1ed54c6551c6dfa5647783dd9acc68ed75044faf6745161c1ee4abcd9969ff9e01f14791de7d0c8e44e77a5b249e2da833ad3bbfd000119884821e74482b639ec1f8484eb6199a01d6e0a3606e25527d7a9fdbdabaad9f378a9aab04a153b1003d520d03f25a9e41c82504ad6de9fa6cda30a3ee1128c35f49d469a79b3c190ab0fab92d9977478c7ddabb6a66f291b58756e040892f44ebf6c8d0ba3cdf5d9335c8b05b0d34a8f4e832c54979274f5d4554af2d05aec3d51a3cbe03282c9c104f664fc39863c3a23396e762a5a8b5ba18b3c84f0f49f8b7cb6f627905a4fec65e5ab41e868561dba5cc8bcaa8c201d613eb678342aaba5e5d44f7ad7a58810129aaea2e6bb9850ef022e54a50b18e5fbbb76b93f050c31d279e66cd51a29c42591b18db05e88283e52070e6eeffd8fa447bce2f22eb921b2e3f53f38bb201511b9e1d5bbf22bf9e23be137543e34d81a0b194f373dbaa600fd00010ef2d9c59966c760260612842d16526b4352a5f05de655fd0bf50546be1d893d90f4b8264488467880be2c4d7f3c577e6b68335f1e0764fdb16d6fc44fc5bc2c1660798e6b31bdcafbd33a9edb44e48dc37306abe9ea761cd2077e977a6d5b92aff3cdc33f644f47d90fa9a4b172faf29e264c9d55d27cc4e7b7e5e891adb566d172207406ac400348c386e74716a518cfde36169e4cbd8d3093c8d85b99e54474fb7c7ebec5a3beb18b664b953f4d9037041c45738e87c53d55eb92fe862a78627a8f4f3f8f3ef01102b8df05c9c1d81da85e36bff90943e0dd93efc00edbec664fe16f03c8e79d3e105803c50a606f9812d3717921477e224efc9c38604e932116d048938c5e50af925d722ada7f78de5fb1020ef09d45e001364d0ac87ea4b800fd00015a2d2be1965546e9ffe759719faf9960648c9183812a7db83a24b0ea52bc26e3774ac36fcef82756ad386332d49551a147e87ca68fedb289b965a4088b3de6506378af1f858c1c995313e189684bbd3e484499dd7a26097ee6e89ff1d0b6d4c6c03917c53a95d9fd1b6f9a8e59e1687506a5499adea9c1bbf2f63b14207daebf64c8bd5742bec97f6364560cb3d687f8a1ce12a197e87df8b23eff607c97849672fcd5803b43bc8b6dc2090d7b99299248007a3207621c0b40e6fcecc3f68b2888f5abc842a44e6f019391449a7356f9e2637c77612b342fe61e76b307ffb780f529c6c84a6e6ad8d7553021070499e85542cd288a543bb0d318b7abca62c48721047951dd48307113449db6a3f997999f421c62b4524e5baebffef1ed21a6a1a800fd0001c4594030b3c64f25c491e6a5ab25b959438c11265e0fb2e5b2c58e3d59c86d642aff89f4e5137f08ee4871df4098a5748795d596baeadd2db0b006e02822ae2c5f6a93c5281b82a7d60f8c390a264d293f1769d55a5b1f78f0f65f7694477ca79ec16677993d5b6606298171df85a7a8c9bc74a2438618d2aeb384de706b797758dd418b2b5d59dff089f7d4f7f21a8390a3a707c08ea4a238682670a0d80297ba68febea9c4c5afccc241d9f95aa77366880ccf891174b33d95f462839d2820451611e8453d981a703595e8fc73df0a4963c1baab591b7f3dbbbf62b50dc7bbfeb05ec2ecdb1a63d06ec20c4311451b4129a08b94049263e180411ce7b8b7b120a96a6184af4c9ac706b2fb01d6389d94cc4e3930925a5b2bdac9e96dbfa42a78fd0001cb6e8b1c0da834c4fc0d797fc3521eda3ddacade8a4ecfeb518a2e2e8a234d2f6901a7eb4d117404328c70a5c24f36236e88eb1dd19a7e9cf4ec7582f472da053e283dc193d70bc71867d2a348521a860fccebe0b45958f1b5919cb833d79802640b7665b7ec45135b24108eac8a882121b6e6734dcd327b506a434cea9298e6067c36457858e3c18d88a320ce33cc3861f5329bc1f9a8f4af57caf2c134051b12dbb58e309d7bfe9028c3b9b4179759095fa531cf20bdf18134c517ceddfb38b4d94849c3d7eae9b46c353bc43d2f8345e345f818e328875c1bdcd754b706258779c7c30592d0a4c4b5cb557eaad484d1cef2bd4d98d12e70b8fda33f3a5e8320486c47e0fc0d0e679b413041e800ed28ca862ceec23c1a937954131b960fdb3320a3e4a1a7ded3a425d38b144b090d9fb0aeb9ba631622041a702626583d41142520f6fded7e7b2408e70ac4ab0b4d23bc2571cfb9048b0bc738dfd0d6507549a451fd0001656bbe84db36e12bf3c78c07ba3f561d2c2687aaeeef2e2da17cb00f060e025a993c12551c12bd4d75c4c116d18951515f42c5628b0858d85b8afe5deb0aa14a2e33d147eb62b9a52bd4769b6a97382d6c39e5f70e727ee0c9a4684fcf9a03e7232394b4ecc6a2d32e27fe2b436b1081bcb4f3c8bc844e9cb1cf9b828bfc155a2467d506be2f89c9a36bfe2d19125fb21666ed4625cf881ffba75e67d209fde2742d5a46634019cf1f96c1e649a9dd58edbc374b9d6220bccf20055104e8ae8917537fd07f69e12e0b8200af3d924997ee33a7d1e9eb3ebba741f0edd1b59f05e1f279d0c4f7106276968fac8055088bd5f57840438a2776bb21693d9587d188fd00011ae402863053cdeb79711a4c4e7472a1559d86f89bc4daaa6865eced1126197f3c43d78ba16fb2d6508632ab4712b13c3f45d674064a51867bcba96a71b7683fa69ad337fd0c50e7927b913f025fcc47adddf1376a053280cd0fc45bbf29767b4555bbc4054a824c11dc93c3648b3e1c2ca42a5dc5f536eca084370ef65c5a6229bcac295c3fa578fc22eab793205cdc8d37fe3cbf7dc6e1a7c53ff3c7e4d226f0a0d4667bad282c625695d4a1ad88931ca894d4931b19b09c56d2f72e72f62600c97348ed8814f0841baea716d1e0d90f26f8c3932a1e66dd1e8c8e039d3e891ab7be0a887c16ea9fbf3dd7f2c7200587ef56cd0e75e8e828aedcef6198e6d6fd0001a265b258bad27b42d12a12e43a25bf566f2b77f6f0924e8e0c32f294768fd6d9f92e5a15dcc94a2067c71a1f9740700ee0e7f626d35fad2c441b176a077fe681515cb0c8613b0b43c708895c9ca5a41745ca87cc5e8d02e272a484be75cd9afc7478b98bd4c030c3dd4885c5214efbe70cf9a1f8a2616e2eadc150f979e1ca8c389b6fa288889464886bbdaee7539c6bba71ad0927e455d45db2c7371a5b15ccd8c7e7e91592e9bd057d85a18a9a9d1165a84329a6c7beab031f2819cf36a26aeaa4cbef4e7871c472b9b363a1a5d597005cb98e6828a7b6b7ae81cbd036f8d8afbc6289efacedbe51c10f27462c81525b18d119ab4527d9c6db52cde19cfdddfd000143a024d1136d92c3d0de09ce4a3837fff20ce4c4307fca87b1a09acdba6a1df122cc69dca4ec3ca89118ade730ec8959d0dd84db0eff5ab2ff71793af68a2d6bf3a301edce9cf1088046b3177c18d90f6318e2bea3a071469873d1a320d5036a6ea1375ce17113721f01852e1c436745536bba80365d2ee1060a73098f99983d18511059ecac21b84131d845bddfc589a1a4c195ee1d89ba9845c09d681a87c3fe2322cdf571b4a31756d2de38276b97ecef325f4ada73b747d78899c3f84aeec26fc9732ef5843f8b2d7af0fee0f04e01f85731eb9fde3f0a69c4c0aad09f51db5cd03db3627cce5d2a1dfe9910817efc2e53ebf9f79afaf09521a3f14980cd20d6c0a0d48152ef8efbf7a58b809f5c28186addc9690ba0857cbf2c96e395e92afd0001781c2002615bfb1b7d35b2e208a1df0bd8f95f2d639422c213683828227885660bb231058546848fd01763a1ae99e0a7a1040a0f398d8171b45ffc4a58a4439a5addbad802fe79c71a4a27fea8ed229c51d6cb26c3e21127aeccd2f0e31ff1c7d38987c95b917d4c86439a7a0d54b3985ccc3072727c654bea4d473676a45f13ac693de273581cd6f864fba7ff00f3e61cabfb689689c49849419ca1cf489cf9db2138f65c1445b74a0e0cc83e8368e6e79149e699b6e64c77c70ac5156bfa98794cd0c561732fc7e3623673e1f0ebc09de026d9745c4986d498762be6799cf99143fca5d69d04283b504c8d8e325c8811853d467b72e204417c7e35064ce7bcfd0001e42f7a528e3e1de18bdde6e2d9888886aea8ab8eb149299c32c68f6c93a19889efc05e641037687a091933cc83bfbdfac77d48a2828bac6b0625260d4b9da29b0d215d403c6d3aaaf33addeeac4d46875cbc4e3edc1a865d6fe0b6a588631188897cfd131672a8c5d098c0ff8ec5b17bcaecac1d78f83b5296408c994c1e81f93021da25ca403ab6694fb3e82ed260e88067e3edcd8cd0e70ce4c79b49962096eeb1eeb2b17be69033a338ebfadaaa1293f7fe17fb7ddce051d9eb82c84d639bab4a415353ae82dfed2996a26d184189ff1f25d3196c67f34ae50a39bdc2f711ed6468b364d1f0ea8eb29f486dc6c2f6dc59a24acf4772bd542557e9ef4b5b93fd00015ea90490404b84482136f214f4497c09a0ca87dcebecc03ac3152ae1c3e87b945f721137c7a309e1036d0e5d94c6cce38c36b1645a62c7160ca45abcfb5c165eb696154ae38684002a150ab45f1b8f1bb69b28757e2503967a1432090b6c90ce7e3671860e40e95200f8a1ac1ad927b49bdc0a66472eac7123c383ba28578b149f121ad8b1ead1e1908858a640ebebd2e1b8a4f787e5f41d573168493115448edb0de580a8c281b783afe2b62ac6ea243d021187367df9fd28f97e2ca7c8856d89c64a4c3c5e2147aea8120b4bc8b2d0b8ec5b7edeed35d24a800760e82ab19cb7363c7b2fcde6200b8e63e2b698489e9dc0bc4c8f1ba9ff68b59a7277038eb920fe94cb66b60142227c026662ddbc3dc29b373c5c805c365b245c2f69152f1e2b2007e3634d06ba18b973add33bf3e23a7175729a9442fc4213adcb0e5a52e2cc272096af7547b4220ef6335cf5fe47c75896ef2d27e58acb6d7b444b3645ad1a9370fd0001f3642f6dab0eedbdf04e554106719fde491a9bfe00e8228f250e0035f5a95782bef5680a15e6148467d4c7db9e22d9a4bc766ba884940645845b25ace95d405744929adc2c63e41e4c807e33a0d514919c09e855c9d77690be00720d83dcf2b2276e157d39b7acb3ae262e65a8a09ff49478fcd67765dd03b545d7e83bf194ee6b0c5f83c41f7c470e0a1c3f1014a7afc2b7149c01b3f1181eeee4ce8a9be47f0f7f897a05683629d6164fb882e1b67765e7560e7f5c6be76ad9902a755c6af0c455156b93f14e618f533feb9d351bb956da352b7a9d63cc5082d6f9768d80f1bb703c84be2bd75b848f06925f47e226c424c0ae8ad4293b0e9c330cdb16bbddfd000158cc2778c31d0436228349fd19e0428de5916401bbed1f3668014cc51cc6b42381c32e5a7d5ec4d194037b0544284c52e151b652e2733b17065b2a98fc1f5ff30a8acf5dc7522b64eb94a8a71526e2aff0acba058b541fa412bb5344eaae4945ace66b4f9251e842e4b52feae5ad89ae1ce3617debbb551c09c87c6e6e69b7942f1178db84dd758fcb3f63d658e3f48c1a47b725cd2e003a59b8e318950a45fd908c6c4d53d706dfcc2041c046324e3925c9ca032f62cc825f0c11c9fff3fed99f616837244c7d71b3a8d79a8d5bba3284280b59ca1d0ed044801b7d4d6148f014c8c04e8859d9c3d074a7ad77b86ea0ae39fde03fb33eda53fe5c1728cf2daafd0001991691f21883d17fbf12b24e86f0f639967240dcf120c01713ad612ce32a7b8a9c911248414d8a2b836e82c827270f60f154d5de0efb7aec5b3db3f8b9cedbcda84aedea5fd0988ed0069871fda9db5ec2a1ba0f04592048f0040599023ac01e758886daa3fdd9190bb1c8ea29898cdc711e8e8e6c4497ae95ee2e5a6730bd8388d8fa812b21c9b97af84a7cc9f790139b0005dc86efe16436e63982fe8d8ca6bc5bea86b2297a0726dab7704fdcc8a3f6c273eb0f8aa008ad1e6c4a985d7cefe05d3b24cfd195d2fae5392a48be5bd50386244fa9002f95ce18efe97be356a3c5b3990172f812987d0fa10d7fdf9afcd20e165697e3e8f1fa564d1f03010f8f20d53cbea6789dd88800af410af54c3c346483fa085c6e02c088092372ce828e2afd0001f903226d53becf91fe3ee0e556a7ddd652e179dc3c2de5d7af38f380c9916791a37e149ec620429b47e5e0c016b898ee1dc45db857c93a718daeab3167c3c336b22692da51bbf7ef1bc42cba25af0b1fa89df2adcf41535803ad6e80ff1e1f57c80514f1d091040aec55e87c4810ea31ba22e4f93a101d71e324cfb2d84e381f1a59a601ea97907013e119a24a468b27558b68de170690e71bc3c2d7361ca7f77d116b642516d9cb128a70d6bfba83b2c22420059e8c22c9f794f2e144a3a065c942d3276253013efaa88a80e3d7f3c32dc249347a6df656df62d4f9482cc8470bd1bebda925d47c5cd9a54ce81d7bda23c2d0434a98a1f73911c6172facb08a21cb1b1935a2667416f5cb810c787fac55fe8e14146721452870f27b1d5a14fcce0021600ca51450ca3b29e9ff6b388f4df5286db585d027f68fbbd1d4a95fd756318700fd0001b941fa0b95cbce10d49d29c80ac6c45bb624fbcef94b9bcc75d3593a5e11ed85488b6332f9d059e0123d5df6486dd2f57a9239d137d46f3b9c0120d391d1f06cd48c13a0b847020d0832b15162461811662eadcaa2757e2b6b2240d478e7411c7e807e09ef824ecfb3681b49aa72a3319dd310d8efd930ffa7751a222e73f198032f2dfa00e978c9542dc476ca44b161fe470a5f63759de5086a18fc92aa375c608841c6ea41ebc6fb86dea22a8987f7d9abac948d5e67a173eee3b9b90c323c4f2624f53fcaaadd79427a36f560ce6cbd99872d8119acd2173935b0331217e33ceb4a0ef314e45c1a90ad06a46948a38a00feac8f58d8780e6d15ff6e013dbe214cc25d39b2def7e68e2d5c7afd69c5d629265f750b683dd660040dd3220a2ad600209901a1f845f602ecd4f341e7b68c6787561b4f18d7819c8b319dba6836a42517216578b022ea0911e03a47f0814046cea045fa63bb506730b0b491614417df91f900fd0001ab0bc038363d54f8e9e8ebed6a498b0f989c8ee56c81720bd66f71d0d97477d0db56d4481cc0e57c2316f4bd3a6843f86e5b28152436ba23f377dac267c7bb6501666efefb705be623d0491dd42a1a5397f45b6be6ceb1e0499842914f56296a34f304320f5b623ae7e16379d89394b7057d1b92de4c913265ba231d81dbf9e4b586704803baf1cf0fd474a721bd65206df02888dd77df03c831f8433f3b2c7cf7e1211c7c85a975d129f33734fcf77c09aaf68b681da7c506e8c89ac5394589d185117b722b757e307ccf31e9ddc1b9131633bd684ad458fdef09d346566eb4df920801ec4ac019081feda518cafe1ff9f9197b1473ddb18d3349652db870e0fd00014168b6756041ac27a58e4160cdc78c2cca30e144cfac7c58d40b5521c76ae171399e4e22bd3eb5a59d0a44666cbd8d38c4983f8e2bac6fa5ee84c86adbf9679bf8631dec545667463df5fbfdd5ff2d0f6f9021a4a03510e291253bd133520d5366e3c272bf8ec41a14b15aea97c420ff263bb52dacb3c3962359312e5a4690483434ba5f592057dc449727f03768f3756c24c85814e1204d2d90cad6e656d39e7dd7b9c4bfcd103dac62415b0d64901b01278602553c149f6d64342f16757b5ca1033494395404fe1ac7ae33d796be12b0d4f986de23186ecdbdc8477a742fdc973b34b312f5984b74faa42f5f62dd4938f76f5d7ec141249902825d81ab2d88fd000193a2f7ea915b7e506459b8484c43d305deca93a84b3c313a6cea7286d485395158806c3d7f3155f3ddadbfc913cbd7673193a951b356ae208319ecf406172cfbbad940f9fb6accde0c73ea5a19037d5d8644b4de22db968851a303717f473c491465712da34aba4e5dbc31361163ae1492bb3435779569598639943640b6e14233d8888aefa72580e22091b6848592d5b2273cd2df009d614da03d0c0803b4320800094dcbaaa337fd210820e07a8deabbd595d2b59b3bee5c7a27e585a1cef44e532de83f08f3df43d68f5934ca69776b8ee8ddc42969970d64145c093d4e989cf32ba71f750cd03b5c818c33462d05c6477ec1f0ec81c48e4b119d05d31cd5fd00010b1cbcbad09df7edcebe79a913033895c66686f078402893059e4a986fb76d28e89324f5b04ead2c2aa0a05786faa1e8f66fbaf66b7ca94bf52123e6afe4dab54bc33e5860e8766031dd256edf40b294f55062e613cafac04f77e284f5097e88cd7aaf46fd28f4fc24ef1ec1aa0bebbb3da5b504e4dca8dfc13ed99e4371dea6f3bc0f78cdf6ff47548e5a426c92095da045e5ccf45f446116e2c9567ed01d62c1fd3a78c62aa359118f77705174db6a4faacaa0b6d49b4da38e42b20e9cd2ed7979a0f6da28964e0b3fb755046f0a5477dda7465e00d13c8751c36255b7f922dbfe108a14e359257ad607dd7e1c9ccf330779135714b01746a25ddd11e565c821713c8702e50956f6f6ed9c283cfdd08938c8ccf08ada04309e47fe9ddf5cf5dc00fd0001f5e2772a310c56c1343104f8f618a6408dbdd5e421e31856f271fb33329d5055c4c73e06870feb8f7b298cb9b403dd6c4e87dd2137ac84df583c58ffd627b0c52996bea39da5959ccce7968455d3b4ac512c3d552cf63f2da74d6de8e8f037eca47a715d0d9398502b287e5f1034e536a84839bac21ae9958da81fe54c67165fc650f19679c9a628b74fb40c65257dc5b50f690263f9c1eaf41c3365c0e23f91aa7ade32a352c8b88b40b2d7acabbd085185f7737be6f5a7c342e2c908e8a4f997ec949e4bfd6ca7b3da894fd4581c81141cbfc01afc3eb86bc91c304c18c1dccde49d8b61e41d922fcb02162e444dd4931a076bb072ee3a6d5cebbe8683e992fd0001197c654987867c3fa8c71ce3718a70756063be0179e55bf302daafbbdfdd5669ec38e140e3204b260b1d54373f84657101147e672120d735c179fe2dc5fdf7dbb8aadee63b867e31d9d1242e45bd23ed510dc7a6d44ea7806159895a2293c0898bfa6d0018d9864f584a59003a8d4e3a38eae3ec7c9e35d1e5985acd544547ccc1e31024f05bfd5dc07ccbf535b2e0a0a703548e355eb9d1acd8a59684ba0fb674075011c7be7cc899c30ea6c1df0dc34c9366f47743aa12991192cc836db7fd5a3b0306d6905a82e13b729038c70fd3bea01def02b93cd2e71439a9fdd5fb93da6a72436ce9f5b431b7e18c76d8173964d5a02e24195e761d888a6a2532d785fd000177fcd4e94194a3c34efea9f8f04faccbdeae9f0eb8662b0195db08e2b41a08fed52ab7060465ecebdb2eeed7a768c4f65ed7b5193151a56c752cdf97cd4c6be8daf95c16c6703ec95b6cb541506c715983be6384031685d48190e611ddbded6fa377affb759a1f289523e56761857afb3c33b68f743c59a6bf2098a2f5bf3c1c8a3d41224dd12ef247874ff5ab6257cc3e29a65a1c16f3ecc26108142d1899a340be3db55556258a348f51ef61e8e73fa9db743f08df3d31919f2d9484603f0281422c2cfbd66959dc138c2176d6e242a5a04d5b1cbd8c39ed051486869f8b0f3ecdaf42498be6016dc9d49ae52220612a32f50259fb9a8be0cab88deafd56b12024a02c37de6161be5eee8cf63c3b0d16d0bdca07ad59fa0532678b864c13bc20c87f061046d5218e768801c69818dadd24e62aa4625d5a2af09b08f9eba8efb5e29fc2977184990fcd81a0a3f896691051da425d7b99292d8ad7a3bbae5ed9503c5b761907097dd6457a5444b9c85ccc829d5901d46cd917ead52a3316f96588a7b508501be9f69c73d97e0db9d615f20e50712f377663f6c47f42d005db72b225048abeb8d6e3a60afbe21cd13d4539b7d39806952d745a7c49a552175135d840a3f9b9e9ef6ba3c46e9f4bd42f9bbde0d5d5abda8cf06538d3492aa856d8708285c0aa99567adaf6e0d4b28feec0a55e49879ac8ee966e4520e5eeae3e6b4473cb710b4213973850ea6ff0331b04ccffab2c2c3f55ff28b8bdf644e4c19982916d01e28d745302b079066c61fc0e8e1c90931f122893f7f5eb86e9111f98cf36e719129668dcc4718c52d0c4c6a1a941b939e5e744d61aa9fb1aed6eca5fe062cd109b15abfc5851ff77c026e9ba7023b298d40acb1ae9884671ff1776aedabd859f2a481eb18b963afae2e2ec41e3f3671e9dfaeab234d6aaf97249f4702e706437d8e36f517b3b227bae68b79064a3fbe999bc2d75912d970826908dc17b815ec0f030e20b24f17c527557e36695abd05f67c60475a0c74aff42b5e7fd13efc3b1c3bf5ae6e3251731ffaad110c4210c9d6d78d69d6f68cc31ca99fe783a12fca506af01e28234f01e1b30dcdccdfbe696bd5703ddbf0554c8c4863edb0f252e2a9daa54d4900d8fd6aef61b8a8f1e165eea79a3f20663b08722762f2c548e0cd10cf0d2d8e8b9239b638d02c49749d55b721a6c81d2554b51b49191518150664b7e4dbde3ae02b36ab3ec9250290c8cee6b7371e0d3c7ad64d267cf463c4c82cc5a325aa72461345c4f45c8753de23bc148527b0e982511bed9e4bcd4cc37e1e3b5089373fcbfbef9a111c96cc79e9f37d619c0d68598541f2a37ea0fd614ca310c915c0d412d2eeee8f7d71e4a250bcc60d68ab3f296a5f75d75d627e5913aedf3646f733701218878049c420ce0089ded11b087f16111e397d0f2e354f1df0a858aaf07eeb8e80002210305cf1786fa950ae9b553390d6d62e2b285ebaeb978822439e0922403f9cc7dbc473045022100b807fa7bc196a7b2d7a3000e5e1870e2ff488bfd6e2850aeaefb3c606f28379e022009c3cec446550e5cb04483404a677c4b8406d85c62cb4d714e5ca3a50aa02f2600e80300000100e87648170000001976a914dbe6d470fa9fe4d037043533eff4f80aeef0c8d288ac0000000018b6dcbedb0528a2ac4f32b9ab011220000000000000000000000000000000000000000000000000000000000000000018ffffffff0f228aab01c2028655e8030000dc8ce67bfe1851477371a9ac40b6ae0cb8571f6e2d5285855288f6079f1ce7239ee8c85f465b1820058b79554f41af297e9caf95ce0084b7c35dea0b95e15a2fb9f8e62c5427c1c36120cbc1fc11ff344909079335209c6b84b45a9211cac960f64e9432ba5eb6e4ecb2068223dfe3d85b345da17bf374f9140c9577c148bcc431c9ec3c7d13bd2363dba821381ed9fa0614416261e88330b3e74c40e6561310eab3f26f092e72f3cab761f373d02680dfb52937bd9515be242f6573754f8f665523cce3bd606c8ad190954f8181577fd0efe7cc64b711d03774958df4a5211e44870302056557777951d7ff8c002161a6a59e979f05469cb31770bd484be6525625359979220eb7e9912e835065fb00fd000216aefbae3525166510814d1636b76b0d48ea3cd54a3a17b136a84340989d75f74ff952966830e4c0d59daa006d5a7190978270ee9475a0778afaf002cdce7efdbfad630f72838b5c4a3b538ba61b94bbd9e353437a50725af5f16fbcbf36bb34e7da54e5c24dfc90b545f95c973877bfadc2703ee10585a1fc1c97d7377bf41c9cbcd5a313849a3c826e7c1301083694e6dc05f46899a901ab4a8d7f6b3600df280157fbef6eca4c28fc610957a42a9acf7c4d7f9846ab6b9b04fa6abb5fefc168d45f10078b97d4d6a39638588a1c19e1bfc472657861a902c2d52cd32fb0463746f649ae88bc0602dbf35816fbccf91dc249be809160cbc7f8b6702d6cc5b81fdebd283231f40758afc899f6fecedc51dc4e5d09cb8961092220541f75ddad45680ea92b4ee78c29f58c197a68420bbb25b450c72d02d7249f7facf9927378620eb36fbf9b4ccbcb55627eb9cf905b4a4c65fcb77a537f642f10901b6e94afa37e4afb0d6d91194454a9c2dd8ef8fe4316f8594c7822a7d58cab09657cf501da5be5a44f947bb957b71e4291a7fc60cd5cef9f0676f7c89123c7ff1ae2e6dc001b6f19785534e207fed2bade8597541b13714284f67d6986bc616ef1b0adbe415242fee85acbf482a6a48b3f142ef7ddb5dd1c97a4b0c53c6ac7aceb8c042d9c9ada1bc986b8c276d07fbf8512a3dae6a357fc02b167eb85000040e8693e45fd000200e23abd27b258f9827ed58545a507bd465e255e1156610da314bf7df68b6b55129df84c7b19e362751ebb9beba10790c9c26c5ddc7f087258d81b006c0d2e92be0178bf5edf6e78e89f73cd97746afbb2551dbc97eafe32ae62e7f9ebcd14ad69faf74d2011d16f2c50775f4f499c87c3c50d9d5d486394c2a7f462675d2a4885493332e0610a78fc0c8b08eda42e4bfe93b8c7f80a911a7992a1deb7cca2e40933e1559815688d4e5ae5e58d706bc513e5108449a8393928b5b77ae73cb03fe212c6375b6c5e61fce9db16360a147e7f7fcd49e05a99711d4d5799be77f7e39d9d1397388d6680d4931b48798ce013256a586781ba80168bae63bed4d150b64a73f7d0ab0c9ebb42f5d4db40eeee303783249af4bbf334c660f8c084ed9a2e5fff8be230940a4a08b59418676ef005192365e4e67757288791ce4992b903a31537596cf6dad0be2af2418a6b9cc2c33e99d874168f6a29df189a869b16eb5d24400ab30e4eca9274114d646aaaa8bad45832b6c0ded2bfa698939a8af9d0af380d2afc58966afe0f45483ecad0f114b904cc2fafcf470dd4fb8f193795e8afc3243b4c946d5eac82babac6feaf4ff10bf53acdf8347fb9fb7a5ac4efcf160f0a7ef3576f439404a3078ea092f46f408a955c965344023d847fad7374cf145cadbc8348eeb2c5aa999ebeeb8a5548bb14e0092b184caee354020c19d66cb213fd0002729bdb186c4c494e17fd6effe29cbe7fbc539caca738d54f9ffc6e52a35a27da134192e2f7f4ee2a86af281b78670e662677e97a1ef008f10f42349fa83bf7841d88b1a457d38164a383ae9c6b974137d58216f22d135d30b9d6e7a74952a7e905f385141f4df088415d704cc3b03b2cd600cb5507f8ea1b53fc0e73031a3946c0d6269f020c9c26a3be3bfdcd37f8d3b9fd42538ebd72029fa0bd8eb57a4fe6769e1b43b5d5d7be311e12e52ee9dad67aa988dedc80ad616d7540381993d9de91a7fac6d08e4414254b9d1d72940fec032833a6b1a5605f4b62c47a86d70dbec5ec913d0a613d438cad385fedf24566bf79edd17238e55421520b95772224623f145100e663b2ba20161784f688afcf07a900ade1d48060d21be9ba9297697891c2584cb99a44868efbdf65178592ecfbadc92f4883662d6b21b7f266eb21815c7401b8e7da061e3258dd685f8cf65f2c2e407c913f85d053b05f6f92ed1299186632ddcf175ccbbc933044bdac5e10916917dea1146f77a8ba4b4fc8ce260b5deed395ecae9b81baa6b385fecca5d2982041c131ce02a1dec517ad2d459434aa3a514e7a4c6c1362401b1ab62b4c89bd7705d5072e0be5250c60c2fdd946bc73050d3b8bcfaa73165eee3660063f279e824d1e15f87307a40bc9e1ccc0f7d7087ba84fe9275742455241b61d3687d23eb9d7a9cc18072ed8be1492db46a454464090750eb0a393499a73d23f4c552ec6ae425a77f97d4285a7f287066b2198bfaa99073da6f4009755e59838e48cd5fe692962b87da3ea7e14b34b352fb2a4673eaaaa594a094610bd0cc566acafc21891b7b0c2470bbb338f579231e01c064f275c5ac9c2748cc50e7f2e36f2768d2a59c22d14b6b9a431f7772e716731c55ccbf086187fb15bd07a5af3040d468d4088ba9e6383f1df6dead9384758f2da81ee96370d9055ce5a0db56bbdccb57ab490f42a01e083b61c5157b3c00e2011dda865c7294cdfd2be5c03a1a36a4deda9cf03b500fd0101b3c97046a717a2f38fe2265185f4411cc68cce6cf9885a7a8fe6292eb9e3eca69fb6249774a8c82b888d22d5fcdd549846c3ebcf054c6bf07aa4d6b1c0d4d9bd8e16501f65373ceda249e9c760848fc86ea92fae7142d211c8bb4a287c91eb08cef7678ef1f445f76f81d464eb1d29ce5c6d6286d73d49cd0ef03b65376eb146a3ff69a487ec90b53c11cce613a586f22cd56fd34df6f7ad64fa3c68a6ae5c9dad99d4a3d2b0974ea1f627be4f0153c7b5fe472d0c562556c4d8d1c7c592bea45bb7886b74f8639f9487b2f6aebf4848b5718f2ce65b8f5a4efcf140e2857bc9c503f0058f9b6af16e75f2d530fbba8979f81569e6cc0bc04a54e30de4e03a9300fd00019b881d73a78b86771d41c0c2a9ebdb3899727f33d2d4d81dd27b6a00b4c7db265999b8442800750abde7cd97be0c691ed06b5d40da115546d90d4803e82c61d43eb5e2bc684adcf180be47660870921fdedb2ce43f33564541fc0debe175c6c49bdfb51378902dc709594b9b7d34d0af70b67c3c608aeb5185e78c1c39cc3080b71a36115f623a07a4e1a3e1f3e17b6f9f695f1a1acd9dd1319d0a0d67b337e64f720e5168c09196244bc71b083f302e042be19b6aa1f8ad61755f4883c3a1ae615252b884ca3cca5e18a023ad6725f08f9e0ffd60e7a73ccd29afc910d60dc99c06f5953c3e398ef615fca45f6a83f8a0be653d31a7e1a1666cc7334d9cad51fd00016f7efa38c8f9f478a6ea1217d8be9b7f0b01c5d1fa483c26e403eb3a4875aeebbd0e7eb8aab8472a5bd80e8be38df13526042952f813b71f8aeb4a2281bfe5e5d9ba70f7e4c9f477706da922899f505dd172e260ce5f008b59c0590d498ac50810e9a38d35f1a2ea4e9ce8f77e46d0b5604dbf94629cfb8b65453a0295eeca9d992365309b7956481a8c5080510a09183bd3358fb26933e15c83fe2ca6d186e631d72889a09464f5dafdc8a93dd100329071e52beb522bef1af0fbae0516ad3b02011e19a2b2924791b3f22679b78039c8356c0e6676e2451487f056d0cff064f55a992afba08f59af7606a809394772ff85c4c40673dd54eb30020c6a5cde3cfd0001ecbc47845e4a1f3c05c4e9d0a47e5e8996326f48d7ee1bd0e432c5f33fecf8a94feaf03f2da65525bbcb119c7928456c28f31c183a21af1acfcd9615669cf47a077f861f694bfc1831f1a71ab66540906c62b85274f63abcc53a37e3fccdc8563ca2153818b6b796473848d765d1c81d4e6f78ef8fce804184e04664c5c6af7c3abe4d92aac3f6ea99b84a3e53a7bf7679c26dc96804ac9e1443b054e3c55fe89315106a14646d06b84122861ac0ac27fe64fbee3c9807b0264a90eb9602187f4df2cc0c62b025ba70bbb30a7e43d533f32546d2e6f97537bc2d623a7f545676a37cb511614037d77fe21a35ce4a2275e7ea1d85b7bc19e477c61033f5effe7fa5d8ff48e2873845157b7a29718e6692704a9d864301fa254798555172c6277cce4ed5aebdff9c27a1bb37071677c16da15d6c1547c5b708e1988eba68befff1f1743342ad7cb89ec8731b567198953d965ef6e395d38b410a326ef3e262937c763179f22a076d17f848dc4e36be38d838d5788f121d771f23209a0d50fb3d016c5cbc47cec31069f7d22bd78691662d8b609932b9533bf828e213e5ed4e61f2b80f05acbc00fda0017bdc59c8a6a23d261cdf1dd10e91523503c3375a803755c29b6a84bac626708b7db937ed39f4053b5c6376b3988fc9388cc64dc07466c67704a32b703d5dd86d8fb8597cc2ff7ca190044c66638028855f29fdf2a0943c64125e3ad2487de6928bc34088957eb32d843613e6b588a91cbfd8c37c24ba655739cefdb203bc3061ff93498160c7e949823b0947c68b6c51dcdf038c538f50413266680ee23817ed9cb840e0094f6fd2277012aa2c6f82b086242e6332a4e00bb9c12c153dfea9340e681e63d72551f8830b2bb2587e5937685252928894bf90bfa174f62bf7ccd43415a094bb4142fcfe639c62f6eda0e4d7ee033f49f51eaa6a35b0f7f400992d8367a275e9d018a547430083c41a35ef543bb92e159efad39c5b84d127bc68dd581c308afd5f81654ffcdb3317dd6e21d5251916214e873c83b6ac197dbac6c1b93d79b9dd5da090be204b9765fdcf662c9296295610e42a0570a1503dd67c44e17936946f3a6ce61f82b13cf00a0b47d06f2cd28651b6af32ffc58304593d5ce81159ccc952ed980f93182fb468ebccadae4dab4565d64a5bcb3aec7a09d681fd24016a4905a3a143e4d61b16898fddbb0f9d7602ce865716b62ae4f9a37e2f6ab89de930e066db6f4a8667ccc4e79e7ac760642c33e5f24266540c9b4fbadda4c0aa1b6a74d02bdf2324ebf9598d8ba918438de1e3343be0b057925bdd52304581ef621fd085dc55cf8b45d605cb0b60047bfa935c2968d554753a615e75f24086b4e40508ecdeb411ed26c007a1110f3e73f504d7fdefb275cbb59cf9cd68bf4784b8845467fac90275f3bbcc2c14a87fbbd7d111441d6ba0833b9045db43975317aee170242b291f8b07254d395472bd4b67db7576bcf2460bf0c182f745a6cbbec3f680b7c6e0a85308bc3af8af3302355757a77a2fe3f98350e4ea1b3074e37a638c630d529141843583ba4b802e089e0a7ecaeeeb42079e072e64fa5251782cbc67ba9d46e4c7f502d44a06e5212d09f4dc5bef1f1dfc376d4a042f608b860971c44caeb3735cd57e19401314af06a73180918af7693ec5204b3f858806e6919af05a1a8c6daea2ee3f08fd2401c082f09f7042a7a0b6b484287050a15f5d8c1011c200d42eb51aff5a484fe1de9feaa264ed3022b6f5b1b54a4a316d3d7e2635210ad83e2d3c497bed46f417ed22804682529b034925dc785a2d74361d1db395d1681d71bc1b0635908ec3e92f850577f35912fe5c173402e6e2ed8003c64a57bb65a2013014a3ce14ccc725f733a50457a696396cac0a551cdda03a2ec81597031feaddd801a9bceaeb5f862a4cdb3eda06dc317a96b29c27d78dd977cc6f25d62bb967814fd1d7e87c675042522c904fadf1cff80289374de8f98df511de975011d877058aa7ea9cdf186ccfa5cfa5258e581ee7ee73e16dcfaa82f079a16c95e6ca4f49c037b2423c12006b549a0b80c5dc9b93685fd2a4bda99e5aea74eafe9d28149e94adc538198d459c7a9a45a1fd24011c69aaa26aa94954fb7dfae2d63eac286b4979e7d513eac8144a7bccc34fb298b7c556a82e7450bf590f1ae658c474ac7c12b3fccaec877b6bdd11fd9655a27b7b69b922b1629a24a8d7f81a6827dd22cbab62d121f5cf96d59be904022360c5bf04d2435c52541ea4d7f932e19fc471a0afb38f2e84668d974c57c963346d790a670a699ff53b5557def1ce9650a8624bb2065f9ce99d6b5361a1b39629040fc897a5d0a07816618840f86601508e90198de64d7091a8bda406009948fa9ebbf2f56adc66e057ab23152504438062867c4237ce2b99c020add16bf66a0c06a072c4fabd9b9fd1146b73366535d934328188798ccc18ad37d30e06a39b8e14468d32820d912359323b7d474dbf507e894a448a4e921f70d1fb4d4ac03b178ae157814004fda0015c202cbc492bde212955236761fcad7de9ec8932946b5352f6c35a242d39b354a1f96a706bcb84174a5b11a7e51cf0adeec9c1e5c88a3f8f32ca81977db668650734398e6feb45cbe0ed5c5d8f77cfa67b78f8c7c856629e6731321126d12ebe70603fe28d4281f5c9165e60749bc8688d7ff3cd509d958dcd1ac7b068a3548af0a3e20b984bf4406a6c6d55c674bc83240757e3a0515bbb626b6b6b863fa7029c5043d67ebcd531601d39b8b6b7e507a176216a264fb80f574d7a6a587d5ebba7c355007630fc49127368f6b672bd12fba956db75f189bd438f5034badc04d396b04a021608a7888434eefffd082efb0c3196698d1b015b42eb6f5a2123c085d37841c0cd6c7a1d77c55d61d76426758cdadcad3d7b1037b5d94c29962d4fe218c9f98c89ecdd9a50a48bc534bc167d536e11906bf594876aea01683269255701b705ac454b250abd45d10f4f8b881c6f4a360014b450132d750099100e15c70ef65ab0eeea340604f3c7b3eb3c51292036a6cc2843989ea418631fb595ca9d7e23a88a3a7d0a8dd5ec1212fc786dcf7107214d5c4d3f2cffffe2b1fbef09816033fdb616b9318b80b32f045ec1bec678dd1500480154ccdd87365d602f0126b57015784abee6fdb9b7f22ba135cb882dfa738baa17233fa53c68d35e4102f185c07a1310b710ff2d63db7238d36487ab504e64e239d0641137cd75cb282e831621f101663efb2f9de2520049c0d08a7c965477fef9d575ba31e101faaea20f96e40046132cd3aa981e8f360cb6a9bac68844d07c7d1741e56b104f634fa1a0c31d64ca4526de4f1754effc40aa04b9dfdb6f53ef77540d66fc9d0a4058fb46518433a1467a71873a03600b47c81e0d452cc44728e3a625235c84f7f62d753faf91fdfa1aea056925168616516e5c4357a7e82c94127fe8a4626c868bb7bcc13a0422e15e31c82935a4e1b9cac496bb456d1318e2e3e704627f1dbe646f5f1590283833193cb03f28e00f5020fbd43c5fdc39fd35498cf4fb7471417e814974331500d8e4ec8af34af39b457d20843d897d0e015456600325ef702c1bdfa7bbb3f2e7a74dcd8fd77beb3df4022320108d0f48a7a9b2a32e1587eb01094e20cb7c002d3de08f481a1d7cdc6b3f9c7c20185bc7ee65a12aa6003a58b83aa90d90baef64c7d324c662ea5138fbb4bd063720f8f9111e20def54a90755537e44fd506737cf1fcc4d0b320bef16eedbd750f8b2002e3af2f477e9d2912e50e4f03e43742c19e6e94c1ccf84ab04f0eff342bfc1020a544d7e745fa08a740418aa6da398bf10870a860427dcdbb857b6b41cec8e1342059eabb84637e61530039918cca60b860ef24c0df9e586b7ee9ce89336d5f1f7a210f0385ce2ad9f83a8534ab0325a42495a9cc4cd4774c9e8910bbf4e7192c2e98001fc3aae0957bc822f2aca9be874e1493a0dbad2ceaf959996060a2bc1781c23520cd178b0cfdcaa92929b9daef0c4dc752225792454327bc351b63765208972820203954fb6c5f5fa020f757c1bac415fec7e1bad3b7c19a93ba5ba53dc12f5ffc6720e7572f4709fdb74cdf2dbbf50a7bec9c10ee302cc2c5dd878bd109d1c87bfe3e20883ea4bb25deffe4bed0029e066230311cd71a4beafa608d43d615652cf9c146204c98a01cfce1f0ab321105b2f2659d375f691e2f6cd9eb821adc512718acdd6120d5f64f7950ad04508b36baeadb52228ed3a1139512125834c1f449b2a9613d67204550d2bb9a9333d567b6c2ab154f1fb4bde51d4b5c50307989ed07100095485720109b0b89090c8a2fb0c51f3f9ca1eab0e36b4d9200396e7958523e57b705d11b209195a60cb9f034fced26b3336b0c49872fa13d56cc410f59463e4f312c93423d20f52bbd6ff9d724752b5ecdb148a47e86c1378111d76e77a2f4434816e325c62920510cf87382f2a4c1417203c4e17511170ac616fca2caa49b521dd721a8183f1120ee7ab5b0de3332ec1fb81c2d5baaf0509eda6de26147b081866c2a9aaa3435882011ce790ec01637ff533a68dfd8fa22733bd7e4fefe18a5796e9bfaf996adf54b20509ea869f679c1ae8b074619487fedad28874cf9c93d01003e9dd0df2f45b66b20380577ba031eb78adb2c71bedc8154056b157ae0d01203c19d7df420a424f71521d04f8ed62e7175b7e70d7f92dafff586e5a60be02901f9f67e97374bcecfe7830020c6be51acd068843922b23415a1c7b39f4846da7584a496483a36612dc295968c204e43611bdc897913427fae390deb145024077c5808238d960f93c62a62f70e6520e2f6ace6ca1e8b6b73dc2fc3c43f3d1e740f1c663e3e0ff9415b2d282e1d377420bf6b145630d553e8c6e40d84626f01964e3d6befc33ac284263ee2c35d76003721a9d895ebe788be0a6988027ae6b33a26bee33016d8f4b64255f320d34deba4900020c8bbe181b5482c9352b9d607eed48dde4a2759d765bc2da53cbc1c03d68da30a20818a0a41e03fcaf75f38766bfa81738f0c8ec2fabed7142e998e7b55e014300120b61bcde758db04bc126bc67a1c87b9fb4f8eb2c3958b9e4106306508e2b22c5220cec0548ab3977bd4bbc802483f0c2ce3b1d6dae43bc0f8c79167e4a76ee6f7522007f74fab71a89f6b94bdc8b5128de93ef2be214d04fbb8ac8c2832e1f2ea6371213c26fe406e69b2060b60c6d0eb7759fe85f19cb2288344239f2a398f04b2b8900020689efa2cc2baa15ce24852b1f71d08200fb1f2ab8e10be0c4db8c8263a85032220b1f2737db4e6e55370ce004319e7c5e137f3567bc4d5603e4ed1f5c636db6a6b206dc3dc994eb2ed126f1a76892117b18b24028dce73ab6d35924ec69df9bc361121fb6e92175d959528d8326cafddb4ee94fb9ffc2d9c8a413898f14cec99d4299a0020349eb2d5c10d69df3bfc41f99d6cafff21ce69323637565e1cefa60173ad0d78201818225053a5e2d51745c5002281935d4dca8ad82ff76c5ca2dee6be84fb767021e8d31f169295ce6052584692ed9e4da87e637ca0b52455f216d5036c55d45c85002100c51c6bcf08c0a3926b2be93b47d0ed8955386d369ab8d6849957efb23904a20020b4b64e35cfcbde2461db3ce57292361aa30d136b0e7bbc766f4aa3ab4a72b66821e802f9eb8168e002b64b2b7ccdaba46e73e5c422dda18311788b19eec7af5782002062e41e97d6c8f4c6ac4c0f978f6f8488c92a51eb5793ad109abee4d6c009256920009f7f1755459578897b027bc2b282466aa1d3b9b0983fe4cdf51c9090548d5120760bc9a32ab7facf7f3c1d1aea1b6cab28bb65276e269472cb24ded949256c0121ea8ed0306eb39aeefdd4f57e98d25cef9ee6288a915cc96207ddff402bc2fe81002138caf88bb834204209e39e6c9430ceda0294f1253f8be81917370a34eb28149200203aa2e1582961cbf9b22333ed51aa4512dddeb3cef2e4fdc3644dfa5145366618203a0ac51cf2e1a98ea343e9ca5fbead275516bc20e9bc421e967be939026677682164c2810712d79c82e75116a7e173af1208478e8ab6763c7ee58551b4f323f78200209943e959b9228e61e5def9b2ea18de1688565a2c49dd98f1c0a7d43e81a6800820c4ab26007fa076e1ecd22f104a860f10b22b1ce2ad1229eaa2cf204815f7246e204090f4ac3d42b2793d83aa79103e0d0488ae1297109a6f47bcb29d8c8b6d383d2058ceed8e9f61bca997b4fef220feba451f5401ff7186d1348aab81d7951b5e1d2014994a638981db8c53c2a364c744009050975bfc23fe7bcfc5c8c36b3df0bc2120e26863332f579b931156f0559025f7f96a4d72a9fd108fbfd5c29a71b13df72520e84680238c100c359efc6bfbb7e97d3ddd4d1d24ef3fe8a5208a714580e9aa552108c2aed76de2cc32cfd4d0fa02eea4be069e74da620ba09461e63b5127cf9d8e0020626acd1bbc9c821f30e5ad5b682272cc4b87a9e05323357a367e170ebdae283c20ff8a87793a1dd9996598d50333ace7856916b2add797fb4cd7cb7fdf24245a642165e43a1aa272317475fddbb370cb547766ee44842ab25d83cef370304213eb8b002121565e2ca6c09a263cfcd17fed20fb6aa06e1e798276a5f0ca8cc16dfc5c309600203e8034276a6f2379f8374e8fbc709a692b2dfdf271c0726c4f890282a40eec2920fa335f7d0543267026ecca4424d15cd07755568dc93c6557f850031098fa55572036cea7284ab1abcbe3c7a83ea779392fec2fd8c1524f95c5bb481c67e300600521444c7d18c458aa1e62e5efc656b27d6396c2fc9299befe67e3eef807711f948c0020829cf5a6319d4467027f9057dfa46e1de952a01233e068f7fd18cdadf9a3534820b622a3b592686cde240a056840080c5116582bf166f4655ace84b34145a78b0520fddc9480245d4ac36034ada99a182ada449373ea0fe16557c3c6d9d55b21942720e59aec0bc8b52c1ea0179ef1bece2bf3275164c8b14ef9528a1335dbe13def6620af95305f745778a84f8405956a82816f366957d31e74af5509ff0b7f7dee737221424e7838cbff036afc7e59ba67c6add560e242a1772ff069a31e5e320c08428e0020f4855f5217b4a20c250b75c69d494ec450321911f9d7841e489d43b3739eca792010f35a5fc740eccaa7fa007f33b028089dbb8bf539f560df19627d5bdba6fe3f213c35de64e6b2d64bdb6ba246a5c9b6a91047e35240db65c383a1be9210b7838c0050fd000184213201216f3c4d446f38e3733348efdc4a4dfd79febf41f03567e0ec2b5a8acb93639951dc506d8649ca6d1926a25b4f19549d2b3700cff07c100c47c456ae23df2e60e27f8822c427e2de12038e00293de92621bc4a27d104803e48d076e3ffcf8c31f549fe9f95c6b9233be396cbd3c557efdfca11fdd91397e52f35d6b3a2130fd46ae505dc66bc987d8e93689d41be09a1df024af243b843e18f298de218ab87e557c782bd20648dc4fd4a5e654f1e9fa59626663adb179f51fec2f5534f2f4929ceaccd34d6928ca3d42fcc5efa32490428abc3296147147eabffda85ec2be06be4512448aabe1829d0219902fe1e9cd2bef29a8f89ed8eb1966b89bffd00015e46cc825850532b9bd9d584441772b3963c554af1424ffa9ccc7d7de55dbef9456a3fbf7b4be985d185eb898e166ba646daa12cca359ea77bb7a59e45e8a6cf2708c16b5ccacb708839eef2ee4b5b6597ba3c5899c9f5214bdace3a521fe36cd39b77952d1bcc81f9e5edfae34f1cce40369b7ee492701351d34e231af8a3768cc158a796e900d0cac462f5204da5cde3abcf561ad60f91fc8951e4fb37166ce37261649f840aa51e6ff9be749d72f1f5b5ae3349f14d572860dcf36aa4655788b8cf5108dff7a4e231d2e2a3bec0a159a1e16c9bb38c4052439f2b6b1a62addf739772a1d51057c89751f7518a3b1cd7b37c4ff7d621a54bca5b3936b137c3fd0001b4a09bfe02e11668f8f6204cb9ffa9817ec412c820b6b394ce2cc3a12546ebc5052ec1c74876f3de8fa22e19158198cd04c1336a792ea47e63c2bf4e85dbe7460501606c97409a906dae4e7ad84517a7955793365ca4f49b5f6829efe61f52069fa19cb30ce0a74d415898e7e134ed8c4106cc9d6be32577e9a024b9dfd8129cb5efb1ef282802fe0066aa41e587ac9ee20d6416010e1b0772a44b6d6d1eb4ddb32b80b288952b26323bbec16614c227e4599cf721484443f56571c7b048e58fe48b1786c44e4979e105196eb9b5803795b8aa3d3e3a8f85e10abeb0f7b43b9a62dbd0905af620309ef74f281f39b241c4b4c109ca7e0ec55b13eef8ee0544c820b833fcfd65b4a897458cb9aa376c6bdccf1032e099598452130bd38d28096322fd0001b6c9b1d3d60a49cdf61f9031694f9f790a4e0118ae39cce789df472c13bba207d101410af89ba4b2a88cff5eb09e53593bfdc69a4e2706633a45c77a6d8f4baff22c7e5e6b32278dbfe97100271b1fd1e64463fa6a757c4cf4b19954d1d363494664750fcde0f0b6e35f9e0f08f4fb1e438669f78b10e800943840b15541a31c3090437364159681974adba314bcac37e6cb3ec97c3d8e0cd52241a4c162787c199a256599b97e488b8a75a46d2c3d8af1951ddac43b0d338e739deed1ba26a8616f04d124244f365573b602697aab0b427a8f26f1a22de77c563cda13fc03a030817a837c497732c6108eac62102641176f62d5b8e73d0e27e17586d6e2c2c3fd0001ca7e381637425fa77d95f92b8b491ea1398f53d763a6ce32a2b2ff972ab74bd377723dde5302c487fcf7eac93b089457761ca341f143b7c5aeb51b33cf103f2f4ce4c282bc5bb8c7290c2a9dcc82fce3dcd9669f660872f602de46e869fccdd99c85c06d1d7bc9af4e93502e4ffc385b9641df8f21a25a14b8e4cf99cca023da529f8689d86418c37dbe3d4116e08069f3d08b9c952f4e2164dada9e62908d57cc68b264df8fcbfe449c21cae576adb8169a97294a6bbd369a58654cd182c8403080b3a5a916c107d7271311a291841f3e35e065d48f6023ec4118a3a88d417507ae86b6c74e6ced02c768e0f879844e22862c357a9fe22574e30c179e2243e62192455f24a5c214eba9746d2a8f6a47625b53e5e62bcecade179907fccb08e4b200218a8694fa7fa94c9c174c8e1525bb14dd946981e66b1c02178cde818615e3b6910021fca3cbe539842d405545a71cdacd7a795b6f6fd1c1095497ee8ee215d5cb8eca002039f76b8f7067cd9cf8bde783c8ea6edee7638113ec6729093749c2cc0c15d13c2091ecd1eb4b171326a6a26d764e72e09160ff2112e577149bc7c24e6c0907324421cdebc7f780b73aef4358c6f7e5c9a78ee95dce81c4c1bbbf570cde3c43ff5ca00021c6955241d2f09d2e90e33601fc139d0603919a1011ea7ee89f0e5d53727c45a3002037be468901ac92d7c3481ad63d364a93e2daa023bbcfaf4f6fa40cc54974de19209178cde568ebc660a7698cdd7f83d5932364eb8ac144e62cdc045a00ccda5201fd0001a3347a2cd4b1f5337653554be5fea273a5bf0292c0fd60908c6e699802079912372435166baf2f71b35ec0e00a714e9f7c10096d0f39a65f66f220057f369ce7c8221dddd00e90604f9de897ebf9f06afd41814b57350ca2d7ff7e08f60c94dc1aa087b7975936832a7050ae4a14b47b1986e70d61223cdc37dfeabc5e4212a869d953450202c9c12db23f8819faad59252173ac11c54020fd7324b4d39673befd2b585437affe9398e7967772d408c6db860ec512f94d02b0cbe9c9d414bb4be4b08c3a420a549efaedaa6f0aed7c74044785b611b2d65abfa33374748adb690a54c8719131e9dda9d46afc9dd09c8abc498d6de657920642d3d47a55f17fd3fd000189cc7f3247c6ede0becf1ec89f5d92142d25a713e037454409ccd146dc18c58319550275149342c690f00b1e060db34bf6ea34473c661ff9f32a40214a4715bbcea59a9ad6ff976e43b325135ca16377be53be39255f9e45ab4b3652d629a30500f9e37671d56952b215628ef42c1e49046476e9a760041003742751b158aa85c4e95f70e267d2631491c60cf09a174816890a2dcc2f8a38f296389c3ea66a6d577301297c242a6b9ca7465d8282f2e72e7673f9e1f1c8f470949c970854134de397c08c06d5d21fc487b2b9529c901c052e838a087f15b802d081e869cbb311efed66ba31c17c73cce1e0fc32d58d35e54bc5524776ee3f45d7b8f81fdad4d621b95f023b2202f5d7c814aa89c27b5e0bf5cd64b444c711c2e1c4b805af4fd58300208214f56ae87b268fd16d6df77efaa715fccd1fb1e6298c1c4f7a4f18d39c2f75fd00013fddc975fb72a66d5a7f3ba6df9c1ff55c7abb5a8f08317c6b346875e850223d87d72d71d9b17c11bdbe6eca34b521fe98dc8d31bef6fcfacec74e5c4a6c5f00ac769363df4720e17c3b687a42f29e8edbc20184dc9274d8546368e3fd2408bb68dd224b73a1487c10f4e29b0d8b9823f7c73db26ed16baa4c5d75b162cb5a97caff3cc8957072902538e0e698fad2e90b471777c5e8d90e5cd313933c2f3b30e49b6cf7ae7dcc09ab2e5e464601f093973d99c0815c3ea587d1803b4ca1d9bcc2ac20cb818f95d9239fbbe62c3356ba41f7dc9d2232f6221447fc4858bbf11ee382bfc639d9101943d958a59ff9b81007508c0879c4bf16885bcd6eb2383898fd00018ef6741f57def1a55a42b565ebe0451f0208762da626acaf0cbaac6d9bcec14e22c15660c2a0c5a23b27c445ad968a7e6b2daab11463040eb50799858b7a5063965f947234beb0d42fe1c2dc7bdce7fcda3de1bae384b62e798995eba4836dbee41a8a4de269c026018a22687ad77985d049bda1d39b79528b320ad3d22d893c547b4dc4acb57fac4e0603468beafdf54408ddb7d4c5db0cf14162d0d735b3fad10888f0048035481e5713b846761838504a36c1f956b072b46f11c7c6fc86c766c36fc7dfd5068855335b96162d8b299e3cd21060fed730310d7748c3d16f228b0eea1f37f5eee5453e3a19ebe1e60e4f2834d3338bf1887969de57ef02a1bbfd0001b50e09d8212707f0035a46513208bdba63a5d1fd7a7ea88e181d7a3de81b8afb4ea4b0428eff67c04b27372d73896fd9df443aa9b2a42cdb8665271bb1c54833454504dbc80645bc02366dc998d334b2723bbf5ee1e139265c107db84774b4884f26b57818d39d05b47d5bdab25778965a0627a96ec303c743ba57c0d32d0337a6d3dbe982284109eb0a7976221816cf3fa7f2e3cf739f30be83f253564af7f411358d9c5340fa5578120c48b617c088d9a3d0811a575bb1e96b746e25312ff6d87b9705c04a10123c7606b4c5d6658244121310ce3f1d062250b57ee51e35e0d7b787d38206fd9d26c70ef94f337cc7108ae8ffddf5fa314e6c4e5e1538fbb0fd000192fa78c12fea8a45902ac86e2878e8327b5f4c2ad9bec7d6167bc5590df49cbddda4d4d652f6f087f2ddc7a2813aa5b33048adc5f900eb4acd983bc86ea7b89f630b647a9ef42ab1bb6484ebc20989603144912ffd80158aff7e7d40a1a76489afb21f5f711ecc3ba613868a7aac4f28f2cd5440dfd064c4874d4e398a4718e10de8f7055e452271f01b27544af7035aed32259796962035d7bc37ef843cdccfacf969a1659e354ec27efda8756b09c0bf855f4fbf7598273154b3f517303b6169bdbe4ede890a90d3b34c917311027afeaca7dcd27158b04886dfd81803594292cdb3dbb77ad3a8df97df63095fd28c275b956cc61faddf770608b898d74ca9fd000138b1c34ee04d01368d9cd8ec4d70b97633951ada508c031470cb4cfb15a62426276c7442e7679f926e93325e287ab9ed87446c144be02e559dd076c9c5e1d6a6a7de45438076b1bd7b9ff5a821070877c1d9626925c1f47838e56bb6982b54fb9e131741bab5ea38aabbe4003538c454980dc3be9a436283edc32a3de0321f114ad32cf34e860ca36f18d476980910e564af57da498cd16d08af8b4d4696a9962adeb298d7af4c8c4ad9cb911d80a2115e833f0856833232f92f94cc9c3a6a14270f4c30ada671bacc9aa35bcd3c945dacbb01f4556ebac4e52adf8790dd49fa799584d227ab95afd2da9e67e5642a90e54fc8b693a07384577d04cc40861cc9fd00012c7977632367de82810aecc22a1a802a1aeb6ac54a4a79d8e61606a6eb72effe1abd74a0a18ef83709355e77cef5666f16d94c0d710f5ae3973bb26d07c0a436aebca4d156d42952cc8cedca94162225b5b4780cf47a6426757758957dcf2f638ead2312e67e140ee8c380949c0885c68b396517ba122d90a0ed184564bbe67bf1d0115c77857fbb29945ea00d8e809c295295494dc8cca091d900837b60b31a79bacfae59fe638ee8a2208942e27f605c452be3a4a43cf21e8e0f78e7cc20ffd2c546a0bbcc404b415e4eae2c7b9c2f9121bc741a6f8c5c28c9df3e0169cd86809f5e235ce17fb625f913f94b75b360f9097b625a5305040c55a4057dc4a68efd0001d594cafd37d880576656265ebb737602d6f3b9d0feb3147d8c1f6e1514ca64e3ee046bea5e6892d8db9c094198fb3f697ae6fc880075bbd461e9aab7425ac3a7fad84071b170739e7815d0c76d2ae7fefb718ec132b32a842c58cde33605488043aad9c5df6e58401994a38dc50779e348cd18582ca74aaadcce6df39b221d4235a88000eb2e3efed6744abd0b68836d56ff5d9ec973be549d52a195195725caf3b8cb683da9b96868d35727bf4b74dcbee838d9c39a33d15609fde1c2b345974a93c030952f52da7ac20b8c26b340fd9c08f56b97ccce21004af28ebe4946a91682f9738085cdd9a53e160346cfcf396cfb59465d114c600d9571634aa3c880fd00016c714ccc93e995491adb6718e9ef69fb2cc36044d9e6b73130606b9c845dfdc56e9518a120237f58fb6f2546bec6f47ea6a11f3d898baa47682fe3f537542fe9f6d3679398064a48a3ef8abc92e89eea84c6d8ca956b3c40e9b82b2a496bb1230f8789fc7b0befc061468049d416aca3fb41d4272304a728322684f9ca6125b91dea97bbbba8c83045b5ce8b3110d429d65998b3aed570ecfedd176e98a91eb3410cd501ba52d176bd2c8d05e94acc8f352b3cbe12adefb82d34e174765ef1197f8a93fd4e03c98967ddd0332733e5aa0ce87f63aff78a2b44d10ac63c1549d9a9c6808b743e6f785173924d736bc0890f2e68c1df72b0fb1632bd4679e87690fd0001e736a64f64d58af8d43f0980d29ba57dccea56ff13040270c926a2703401b83859a94d6cbf4cd62109053c9e6ddd1cc61ac649ebb1243037adec455891642de8f43b57afe96cb63736e3e7d735bbeec8d1dc2c698f37f0bcd85bd84cc6b7e25eab500c03f1c62ec730c24208e2df2830d32842277d9e5c9416a91217cbdad60d6a77a7127b09e7463354266a1130d1f04de9041f0f83d41246766027700fa9a02d10d2b0fcb0bd534d22ed38278ce177bbc1c429c09030f105a67db70011d3eb24754bbb31f8a6a98bde215f635409e4cb8c3d769efdd7f1561976bd29876c8a11a130cbb8e3fdf15fd9ad0329852dffd794f499345c3fee09eee21997a5c8a021b993d7cea74a8935b42df2d55606a8841b1a4a8fc0321701d5de9bce1dd1bee100fd00016e510cf2b787c67956990655ca7ba97aaea25163317e7ebfbcf2681b29b0b821aedb8f9b2e309d37f660ac7169dc8234a7e7e4d01e79164eb75f28d284c52ea3d7edff718aa96db6b0b6781366ab985d202823130ca53c2a8e5186fc18862348952e967b1a3e3636517c3e3c48d8fe5e4ef1d5e230ab584964c888c61393ee3d6e34c50446b86e68ebfd048ac86065f9a9c4bbfc2474027b612dcafbbb0416f12d3d856a96557529d1144a852ade77f50f600ecf3c0e00296576eb49a0b211baaa815cea78e1b7a56a1c698ff58488378722f58fdb1ab8bb0063654a8de8344041fddd04be1b4b2b1944ce9d1ebda667caf9ca00e5c2de892a9063a449edd8c1fd00017140014f19e2474e1cc4b40a5a8033f3ddfd960ef33c7e35432deabd85a5b2c18a27b85477c9e6fbb2d8a5e6c686078009b1f7868768fbb5f569ae3429fd64e49cb3a62f32fada09059982a03a20494bd2fd2caa75a01b3ecdbc32acbbd648905d56cd2aefc714af1c24bd6e8f06b1ddbb85e9882ddf8f0e67c654402bfe2e0d9ba404bb3da2d58305184949ce513b3784c3234b39d25e0c6df741683750cb46d7856e67a0d45f4839b5305f9965808d41cbcfb2ad3ad18cdba6744eab0148dd0c1d5e687b6e76bdb9408766b57297be6fdbcf9d7e3e6918c194ef32bb776b5ab85f3a6a164baf866d93ecc3acaefbac43a9fa267bc623cc136ad712b00d788efd00016d3db1d97d5d429249f5cd6dc35f61d0b1b44f1e433cd5e01d28afa6cd6718fc1432b77e8eb6418b0a4b6fcc6707cd3d5c6661cf57b3b73b78feae7c89e25eff2ec1d465be91a18b2f3bc4311919f410cfafe8ae8a06b4b947f9b6fd8ac17453c2f558dfd67f71829be66250d40c3aad6e3ff52d1c0c455e7a4f646445405cf1ad45ba65392bb622a770ab0f5a57e73d32d98ee49d73ac7dde7dad9b9c8e1da9d1e6aef0f9cd120bc1d2cbc5ad819190b758be385f2b0dd736184382af8aa419ac7d734881ab4728ea927b6655b7d3faa4a1e14caf6d38cc706a940bff9c9021e4b6c3ce516d0257deda35d2ac4e0423f01ac849b19b919b773a58f34a06c6b1fd0001464885fd950d292d5aaa6155164216521d2113d795d9aff389771e8f39ff3d96afb5e2359fc52aa6d6cbc3921a7a21e6f3fab62e748337e2cd212456e2b2f52dcb352a75902ac6fdcce93dcf027138be788aad5e09490bbce637751cfd5bda9ec540d7daa92eed7b27ff1bf66585fbe3b39db3de9dd386ecd7671f2395522c1c9006908afd04fe68d88194cbbc3216377cfc27c4fce55c8e558ebc4943cbb477a1172aa8b344c08d6fb853e64ff0f986b7f3e7cc3b2c3d8b2abbc43e08eff15787bfd6a9a8f98b207d8e2530c0c37a37ffcec2fecbd726b4b845ff48a44c1ec7031e4e663ec0042663ff81b9a7fe7d599695737511a685148fce0c3eb01513bb20ffe083f86d11f3c552efeba225a93eb8ea756f45c2e49a4dd08467b79a14000220fd4b0ae4d4c5a165ab75af08922366e89721ba7ee52751e0b3e64017787b9445fd0001d0a8ddbc130bbf7bcd20bdbc8728e812a45cdbe602dcea5826f753b94e1220cf698c1212464a17ed846b29db82b1cf5373241d4117b07a8b7d279d4e8511d22c47be22291e9ab56454becf533b771e5e602542d07828952d5ef900e2548739d57cbb6254c667bc50a0f97c11b7f3dc1624111f9d32cb0d85ac4106b41cd6d82db7aea1135334220163c190744b6daefa456f69c331facf9083af360db6f2a2c80c423357c8fd1bc28fcfd42db69d733efa9ecdff9df079bde1b73a63ee74e5af5c75b67a4824a72466e17b501f6057a68efc19627d115f19fbcb48c0e0889857f0d9191db5875ad6d336adfbf7f09989b2aecfe868c2efc2cc64af46d07c44b9fd000151620fcd4091a3d3e78e8a1f1b5ac9ae8bdd3760320ed9bea2e1b237110d7747e9894704336b958fc92eb200f06507cb56a12f202a8b098bcec5b7b6941dccc18d2bd968538185dbfdbb6ea61eaee22aa8ad24d73df0adfcfafaec181fda3626479710bc19835a5aa7a3b9afd8166b89f5aee8d52589059eda61f19f6319335dfac7765a9a9e22cce0fb3236eeba6ce250ea0b7cfc4a021ca3c88859f556dc1137349a7ad5a628bc47267ad91ff86174a2fe74e3ab298ae8917d6a57a916f00b16bc0f3584b12b0d63141a20b1ed54c6551c6dfa5647783dd9acc68ed75044faf6745161c1ee4abcd9969ff9e01f14791de7d0c8e44e77a5b249e2da833ad3bbfd000119884821e74482b639ec1f8484eb6199a01d6e0a3606e25527d7a9fdbdabaad9f378a9aab04a153b1003d520d03f25a9e41c82504ad6de9fa6cda30a3ee1128c35f49d469a79b3c190ab0fab92d9977478c7ddabb6a66f291b58756e040892f44ebf6c8d0ba3cdf5d9335c8b05b0d34a8f4e832c54979274f5d4554af2d05aec3d51a3cbe03282c9c104f664fc39863c3a23396e762a5a8b5ba18b3c84f0f49f8b7cb6f627905a4fec65e5ab41e868561dba5cc8bcaa8c201d613eb678342aaba5e5d44f7ad7a58810129aaea2e6bb9850ef022e54a50b18e5fbbb76b93f050c31d279e66cd51a29c42591b18db05e88283e52070e6eeffd8fa447bce2f22eb921b2e3f53f38bb201511b9e1d5bbf22bf9e23be137543e34d81a0b194f373dbaa600fd00010ef2d9c59966c760260612842d16526b4352a5f05de655fd0bf50546be1d893d90f4b8264488467880be2c4d7f3c577e6b68335f1e0764fdb16d6fc44fc5bc2c1660798e6b31bdcafbd33a9edb44e48dc37306abe9ea761cd2077e977a6d5b92aff3cdc33f644f47d90fa9a4b172faf29e264c9d55d27cc4e7b7e5e891adb566d172207406ac400348c386e74716a518cfde36169e4cbd8d3093c8d85b99e54474fb7c7ebec5a3beb18b664b953f4d9037041c45738e87c53d55eb92fe862a78627a8f4f3f8f3ef01102b8df05c9c1d81da85e36bff90943e0dd93efc00edbec664fe16f03c8e79d3e105803c50a606f9812d3717921477e224efc9c38604e932116d048938c5e50af925d722ada7f78de5fb1020ef09d45e001364d0ac87ea4b800fd00015a2d2be1965546e9ffe759719faf9960648c9183812a7db83a24b0ea52bc26e3774ac36fcef82756ad386332d49551a147e87ca68fedb289b965a4088b3de6506378af1f858c1c995313e189684bbd3e484499dd7a26097ee6e89ff1d0b6d4c6c03917c53a95d9fd1b6f9a8e59e1687506a5499adea9c1bbf2f63b14207daebf64c8bd5742bec97f6364560cb3d687f8a1ce12a197e87df8b23eff607c97849672fcd5803b43bc8b6dc2090d7b99299248007a3207621c0b40e6fcecc3f68b2888f5abc842a44e6f019391449a7356f9e2637c77612b342fe61e76b307ffb780f529c6c84a6e6ad8d7553021070499e85542cd288a543bb0d318b7abca62c48721047951dd48307113449db6a3f997999f421c62b4524e5baebffef1ed21a6a1a800fd0001c4594030b3c64f25c491e6a5ab25b959438c11265e0fb2e5b2c58e3d59c86d642aff89f4e5137f08ee4871df4098a5748795d596baeadd2db0b006e02822ae2c5f6a93c5281b82a7d60f8c390a264d293f1769d55a5b1f78f0f65f7694477ca79ec16677993d5b6606298171df85a7a8c9bc74a2438618d2aeb384de706b797758dd418b2b5d59dff089f7d4f7f21a8390a3a707c08ea4a238682670a0d80297ba68febea9c4c5afccc241d9f95aa77366880ccf891174b33d95f462839d2820451611e8453d981a703595e8fc73df0a4963c1baab591b7f3dbbbf62b50dc7bbfeb05ec2ecdb1a63d06ec20c4311451b4129a08b94049263e180411ce7b8b7b120a96a6184af4c9ac706b2fb01d6389d94cc4e3930925a5b2bdac9e96dbfa42a78fd0001cb6e8b1c0da834c4fc0d797fc3521eda3ddacade8a4ecfeb518a2e2e8a234d2f6901a7eb4d117404328c70a5c24f36236e88eb1dd19a7e9cf4ec7582f472da053e283dc193d70bc71867d2a348521a860fccebe0b45958f1b5919cb833d79802640b7665b7ec45135b24108eac8a882121b6e6734dcd327b506a434cea9298e6067c36457858e3c18d88a320ce33cc3861f5329bc1f9a8f4af57caf2c134051b12dbb58e309d7bfe9028c3b9b4179759095fa531cf20bdf18134c517ceddfb38b4d94849c3d7eae9b46c353bc43d2f8345e345f818e328875c1bdcd754b706258779c7c30592d0a4c4b5cb557eaad484d1cef2bd4d98d12e70b8fda33f3a5e8320486c47e0fc0d0e679b413041e800ed28ca862ceec23c1a937954131b960fdb3320a3e4a1a7ded3a425d38b144b090d9fb0aeb9ba631622041a702626583d41142520f6fded7e7b2408e70ac4ab0b4d23bc2571cfb9048b0bc738dfd0d6507549a451fd0001656bbe84db36e12bf3c78c07ba3f561d2c2687aaeeef2e2da17cb00f060e025a993c12551c12bd4d75c4c116d18951515f42c5628b0858d85b8afe5deb0aa14a2e33d147eb62b9a52bd4769b6a97382d6c39e5f70e727ee0c9a4684fcf9a03e7232394b4ecc6a2d32e27fe2b436b1081bcb4f3c8bc844e9cb1cf9b828bfc155a2467d506be2f89c9a36bfe2d19125fb21666ed4625cf881ffba75e67d209fde2742d5a46634019cf1f96c1e649a9dd58edbc374b9d6220bccf20055104e8ae8917537fd07f69e12e0b8200af3d924997ee33a7d1e9eb3ebba741f0edd1b59f05e1f279d0c4f7106276968fac8055088bd5f57840438a2776bb21693d9587d188fd00011ae402863053cdeb79711a4c4e7472a1559d86f89bc4daaa6865eced1126197f3c43d78ba16fb2d6508632ab4712b13c3f45d674064a51867bcba96a71b7683fa69ad337fd0c50e7927b913f025fcc47adddf1376a053280cd0fc45bbf29767b4555bbc4054a824c11dc93c3648b3e1c2ca42a5dc5f536eca084370ef65c5a6229bcac295c3fa578fc22eab793205cdc8d37fe3cbf7dc6e1a7c53ff3c7e4d226f0a0d4667bad282c625695d4a1ad88931ca894d4931b19b09c56d2f72e72f62600c97348ed8814f0841baea716d1e0d90f26f8c3932a1e66dd1e8c8e039d3e891ab7be0a887c16ea9fbf3dd7f2c7200587ef56cd0e75e8e828aedcef6198e6d6fd0001a265b258bad27b42d12a12e43a25bf566f2b77f6f0924e8e0c32f294768fd6d9f92e5a15dcc94a2067c71a1f9740700ee0e7f626d35fad2c441b176a077fe681515cb0c8613b0b43c708895c9ca5a41745ca87cc5e8d02e272a484be75cd9afc7478b98bd4c030c3dd4885c5214efbe70cf9a1f8a2616e2eadc150f979e1ca8c389b6fa288889464886bbdaee7539c6bba71ad0927e455d45db2c7371a5b15ccd8c7e7e91592e9bd057d85a18a9a9d1165a84329a6c7beab031f2819cf36a26aeaa4cbef4e7871c472b9b363a1a5d597005cb98e6828a7b6b7ae81cbd036f8d8afbc6289efacedbe51c10f27462c81525b18d119ab4527d9c6db52cde19cfdddfd000143a024d1136d92c3d0de09ce4a3837fff20ce4c4307fca87b1a09acdba6a1df122cc69dca4ec3ca89118ade730ec8959d0dd84db0eff5ab2ff71793af68a2d6bf3a301edce9cf1088046b3177c18d90f6318e2bea3a071469873d1a320d5036a6ea1375ce17113721f01852e1c436745536bba80365d2ee1060a73098f99983d18511059ecac21b84131d845bddfc589a1a4c195ee1d89ba9845c09d681a87c3fe2322cdf571b4a31756d2de38276b97ecef325f4ada73b747d78899c3f84aeec26fc9732ef5843f8b2d7af0fee0f04e01f85731eb9fde3f0a69c4c0aad09f51db5cd03db3627cce5d2a1dfe9910817efc2e53ebf9f79afaf09521a3f14980cd20d6c0a0d48152ef8efbf7a58b809f5c28186addc9690ba0857cbf2c96e395e92afd0001781c2002615bfb1b7d35b2e208a1df0bd8f95f2d639422c213683828227885660bb231058546848fd01763a1ae99e0a7a1040a0f398d8171b45ffc4a58a4439a5addbad802fe79c71a4a27fea8ed229c51d6cb26c3e21127aeccd2f0e31ff1c7d38987c95b917d4c86439a7a0d54b3985ccc3072727c654bea4d473676a45f13ac693de273581cd6f864fba7ff00f3e61cabfb689689c49849419ca1cf489cf9db2138f65c1445b74a0e0cc83e8368e6e79149e699b6e64c77c70ac5156bfa98794cd0c561732fc7e3623673e1f0ebc09de026d9745c4986d498762be6799cf99143fca5d69d04283b504c8d8e325c8811853d467b72e204417c7e35064ce7bcfd0001e42f7a528e3e1de18bdde6e2d9888886aea8ab8eb149299c32c68f6c93a19889efc05e641037687a091933cc83bfbdfac77d48a2828bac6b0625260d4b9da29b0d215d403c6d3aaaf33addeeac4d46875cbc4e3edc1a865d6fe0b6a588631188897cfd131672a8c5d098c0ff8ec5b17bcaecac1d78f83b5296408c994c1e81f93021da25ca403ab6694fb3e82ed260e88067e3edcd8cd0e70ce4c79b49962096eeb1eeb2b17be69033a338ebfadaaa1293f7fe17fb7ddce051d9eb82c84d639bab4a415353ae82dfed2996a26d184189ff1f25d3196c67f34ae50a39bdc2f711ed6468b364d1f0ea8eb29f486dc6c2f6dc59a24acf4772bd542557e9ef4b5b93fd00015ea90490404b84482136f214f4497c09a0ca87dcebecc03ac3152ae1c3e87b945f721137c7a309e1036d0e5d94c6cce38c36b1645a62c7160ca45abcfb5c165eb696154ae38684002a150ab45f1b8f1bb69b28757e2503967a1432090b6c90ce7e3671860e40e95200f8a1ac1ad927b49bdc0a66472eac7123c383ba28578b149f121ad8b1ead1e1908858a640ebebd2e1b8a4f787e5f41d573168493115448edb0de580a8c281b783afe2b62ac6ea243d021187367df9fd28f97e2ca7c8856d89c64a4c3c5e2147aea8120b4bc8b2d0b8ec5b7edeed35d24a800760e82ab19cb7363c7b2fcde6200b8e63e2b698489e9dc0bc4c8f1ba9ff68b59a7277038eb920fe94cb66b60142227c026662ddbc3dc29b373c5c805c365b245c2f69152f1e2b2007e3634d06ba18b973add33bf3e23a7175729a9442fc4213adcb0e5a52e2cc272096af7547b4220ef6335cf5fe47c75896ef2d27e58acb6d7b444b3645ad1a9370fd0001f3642f6dab0eedbdf04e554106719fde491a9bfe00e8228f250e0035f5a95782bef5680a15e6148467d4c7db9e22d9a4bc766ba884940645845b25ace95d405744929adc2c63e41e4c807e33a0d514919c09e855c9d77690be00720d83dcf2b2276e157d39b7acb3ae262e65a8a09ff49478fcd67765dd03b545d7e83bf194ee6b0c5f83c41f7c470e0a1c3f1014a7afc2b7149c01b3f1181eeee4ce8a9be47f0f7f897a05683629d6164fb882e1b67765e7560e7f5c6be76ad9902a755c6af0c455156b93f14e618f533feb9d351bb956da352b7a9d63cc5082d6f9768d80f1bb703c84be2bd75b848f06925f47e226c424c0ae8ad4293b0e9c330cdb16bbddfd000158cc2778c31d0436228349fd19e0428de5916401bbed1f3668014cc51cc6b42381c32e5a7d5ec4d194037b0544284c52e151b652e2733b17065b2a98fc1f5ff30a8acf5dc7522b64eb94a8a71526e2aff0acba058b541fa412bb5344eaae4945ace66b4f9251e842e4b52feae5ad89ae1ce3617debbb551c09c87c6e6e69b7942f1178db84dd758fcb3f63d658e3f48c1a47b725cd2e003a59b8e318950a45fd908c6c4d53d706dfcc2041c046324e3925c9ca032f62cc825f0c11c9fff3fed99f616837244c7d71b3a8d79a8d5bba3284280b59ca1d0ed044801b7d4d6148f014c8c04e8859d9c3d074a7ad77b86ea0ae39fde03fb33eda53fe5c1728cf2daafd0001991691f21883d17fbf12b24e86f0f639967240dcf120c01713ad612ce32a7b8a9c911248414d8a2b836e82c827270f60f154d5de0efb7aec5b3db3f8b9cedbcda84aedea5fd0988ed0069871fda9db5ec2a1ba0f04592048f0040599023ac01e758886daa3fdd9190bb1c8ea29898cdc711e8e8e6c4497ae95ee2e5a6730bd8388d8fa812b21c9b97af84a7cc9f790139b0005dc86efe16436e63982fe8d8ca6bc5bea86b2297a0726dab7704fdcc8a3f6c273eb0f8aa008ad1e6c4a985d7cefe05d3b24cfd195d2fae5392a48be5bd50386244fa9002f95ce18efe97be356a3c5b3990172f812987d0fa10d7fdf9afcd20e165697e3e8f1fa564d1f03010f8f20d53cbea6789dd88800af410af54c3c346483fa085c6e02c088092372ce828e2afd0001f903226d53becf91fe3ee0e556a7ddd652e179dc3c2de5d7af38f380c9916791a37e149ec620429b47e5e0c016b898ee1dc45db857c93a718daeab3167c3c336b22692da51bbf7ef1bc42cba25af0b1fa89df2adcf41535803ad6e80ff1e1f57c80514f1d091040aec55e87c4810ea31ba22e4f93a101d71e324cfb2d84e381f1a59a601ea97907013e119a24a468b27558b68de170690e71bc3c2d7361ca7f77d116b642516d9cb128a70d6bfba83b2c22420059e8c22c9f794f2e144a3a065c942d3276253013efaa88a80e3d7f3c32dc249347a6df656df62d4f9482cc8470bd1bebda925d47c5cd9a54ce81d7bda23c2d0434a98a1f73911c6172facb08a21cb1b1935a2667416f5cb810c787fac55fe8e14146721452870f27b1d5a14fcce0021600ca51450ca3b29e9ff6b388f4df5286db585d027f68fbbd1d4a95fd756318700fd0001b941fa0b95cbce10d49d29c80ac6c45bb624fbcef94b9bcc75d3593a5e11ed85488b6332f9d059e0123d5df6486dd2f57a9239d137d46f3b9c0120d391d1f06cd48c13a0b847020d0832b15162461811662eadcaa2757e2b6b2240d478e7411c7e807e09ef824ecfb3681b49aa72a3319dd310d8efd930ffa7751a222e73f198032f2dfa00e978c9542dc476ca44b161fe470a5f63759de5086a18fc92aa375c608841c6ea41ebc6fb86dea22a8987f7d9abac948d5e67a173eee3b9b90c323c4f2624f53fcaaadd79427a36f560ce6cbd99872d8119acd2173935b0331217e33ceb4a0ef314e45c1a90ad06a46948a38a00feac8f58d8780e6d15ff6e013dbe214cc25d39b2def7e68e2d5c7afd69c5d629265f750b683dd660040dd3220a2ad600209901a1f845f602ecd4f341e7b68c6787561b4f18d7819c8b319dba6836a42517216578b022ea0911e03a47f0814046cea045fa63bb506730b0b491614417df91f900fd0001ab0bc038363d54f8e9e8ebed6a498b0f989c8ee56c81720bd66f71d0d97477d0db56d4481cc0e57c2316f4bd3a6843f86e5b28152436ba23f377dac267c7bb6501666efefb705be623d0491dd42a1a5397f45b6be6ceb1e0499842914f56296a34f304320f5b623ae7e16379d89394b7057d1b92de4c913265ba231d81dbf9e4b586704803baf1cf0fd474a721bd65206df02888dd77df03c831f8433f3b2c7cf7e1211c7c85a975d129f33734fcf77c09aaf68b681da7c506e8c89ac5394589d185117b722b757e307ccf31e9ddc1b9131633bd684ad458fdef09d346566eb4df920801ec4ac019081feda518cafe1ff9f9197b1473ddb18d3349652db870e0fd00014168b6756041ac27a58e4160cdc78c2cca30e144cfac7c58d40b5521c76ae171399e4e22bd3eb5a59d0a44666cbd8d38c4983f8e2bac6fa5ee84c86adbf9679bf8631dec545667463df5fbfdd5ff2d0f6f9021a4a03510e291253bd133520d5366e3c272bf8ec41a14b15aea97c420ff263bb52dacb3c3962359312e5a4690483434ba5f592057dc449727f03768f3756c24c85814e1204d2d90cad6e656d39e7dd7b9c4bfcd103dac62415b0d64901b01278602553c149f6d64342f16757b5ca1033494395404fe1ac7ae33d796be12b0d4f986de23186ecdbdc8477a742fdc973b34b312f5984b74faa42f5f62dd4938f76f5d7ec141249902825d81ab2d88fd000193a2f7ea915b7e506459b8484c43d305deca93a84b3c313a6cea7286d485395158806c3d7f3155f3ddadbfc913cbd7673193a951b356ae208319ecf406172cfbbad940f9fb6accde0c73ea5a19037d5d8644b4de22db968851a303717f473c491465712da34aba4e5dbc31361163ae1492bb3435779569598639943640b6e14233d8888aefa72580e22091b6848592d5b2273cd2df009d614da03d0c0803b4320800094dcbaaa337fd210820e07a8deabbd595d2b59b3bee5c7a27e585a1cef44e532de83f08f3df43d68f5934ca69776b8ee8ddc42969970d64145c093d4e989cf32ba71f750cd03b5c818c33462d05c6477ec1f0ec81c48e4b119d05d31cd5fd00010b1cbcbad09df7edcebe79a913033895c66686f078402893059e4a986fb76d28e89324f5b04ead2c2aa0a05786faa1e8f66fbaf66b7ca94bf52123e6afe4dab54bc33e5860e8766031dd256edf40b294f55062e613cafac04f77e284f5097e88cd7aaf46fd28f4fc24ef1ec1aa0bebbb3da5b504e4dca8dfc13ed99e4371dea6f3bc0f78cdf6ff47548e5a426c92095da045e5ccf45f446116e2c9567ed01d62c1fd3a78c62aa359118f77705174db6a4faacaa0b6d49b4da38e42b20e9cd2ed7979a0f6da28964e0b3fb755046f0a5477dda7465e00d13c8751c36255b7f922dbfe108a14e359257ad607dd7e1c9ccf330779135714b01746a25ddd11e565c821713c8702e50956f6f6ed9c283cfdd08938c8ccf08ada04309e47fe9ddf5cf5dc00fd0001f5e2772a310c56c1343104f8f618a6408dbdd5e421e31856f271fb33329d5055c4c73e06870feb8f7b298cb9b403dd6c4e87dd2137ac84df583c58ffd627b0c52996bea39da5959ccce7968455d3b4ac512c3d552cf63f2da74d6de8e8f037eca47a715d0d9398502b287e5f1034e536a84839bac21ae9958da81fe54c67165fc650f19679c9a628b74fb40c65257dc5b50f690263f9c1eaf41c3365c0e23f91aa7ade32a352c8b88b40b2d7acabbd085185f7737be6f5a7c342e2c908e8a4f997ec949e4bfd6ca7b3da894fd4581c81141cbfc01afc3eb86bc91c304c18c1dccde49d8b61e41d922fcb02162e444dd4931a076bb072ee3a6d5cebbe8683e992fd0001197c654987867c3fa8c71ce3718a70756063be0179e55bf302daafbbdfdd5669ec38e140e3204b260b1d54373f84657101147e672120d735c179fe2dc5fdf7dbb8aadee63b867e31d9d1242e45bd23ed510dc7a6d44ea7806159895a2293c0898bfa6d0018d9864f584a59003a8d4e3a38eae3ec7c9e35d1e5985acd544547ccc1e31024f05bfd5dc07ccbf535b2e0a0a703548e355eb9d1acd8a59684ba0fb674075011c7be7cc899c30ea6c1df0dc34c9366f47743aa12991192cc836db7fd5a3b0306d6905a82e13b729038c70fd3bea01def02b93cd2e71439a9fdd5fb93da6a72436ce9f5b431b7e18c76d8173964d5a02e24195e761d888a6a2532d785fd000177fcd4e94194a3c34efea9f8f04faccbdeae9f0eb8662b0195db08e2b41a08fed52ab7060465ecebdb2eeed7a768c4f65ed7b5193151a56c752cdf97cd4c6be8daf95c16c6703ec95b6cb541506c715983be6384031685d48190e611ddbded6fa377affb759a1f289523e56761857afb3c33b68f743c59a6bf2098a2f5bf3c1c8a3d41224dd12ef247874ff5ab6257cc3e29a65a1c16f3ecc26108142d1899a340be3db55556258a348f51ef61e8e73fa9db743f08df3d31919f2d9484603f0281422c2cfbd66959dc138c2176d6e242a5a04d5b1cbd8c39ed051486869f8b0f3ecdaf42498be6016dc9d49ae52220612a32f50259fb9a8be0cab88deafd56b12024a02c37de6161be5eee8cf63c3b0d16d0bdca07ad59fa0532678b864c13bc20c87f061046d5218e768801c69818dadd24e62aa4625d5a2af09b08f9eba8efb5e29fc2977184990fcd81a0a3f896691051da425d7b99292d8ad7a3bbae5ed9503c5b761907097dd6457a5444b9c85ccc829d5901d46cd917ead52a3316f96588a7b508501be9f69c73d97e0db9d615f20e50712f377663f6c47f42d005db72b225048abeb8d6e3a60afbe21cd13d4539b7d39806952d745a7c49a552175135d840a3f9b9e9ef6ba3c46e9f4bd42f9bbde0d5d5abda8cf06538d3492aa856d8708285c0aa99567adaf6e0d4b28feec0a55e49879ac8ee966e4520e5eeae3e6b4473cb710b4213973850ea6ff0331b04ccffab2c2c3f55ff28b8bdf644e4c19982916d01e28d745302b079066c61fc0e8e1c90931f122893f7f5eb86e9111f98cf36e719129668dcc4718c52d0c4c6a1a941b939e5e744d61aa9fb1aed6eca5fe062cd109b15abfc5851ff77c026e9ba7023b298d40acb1ae9884671ff1776aedabd859f2a481eb18b963afae2e2ec41e3f3671e9dfaeab234d6aaf97249f4702e706437d8e36f517b3b227bae68b79064a3fbe999bc2d75912d970826908dc17b815ec0f030e20b24f17c527557e36695abd05f67c60475a0c74aff42b5e7fd13efc3b1c3bf5ae6e3251731ffaad110c4210c9d6d78d69d6f68cc31ca99fe783a12fca506af01e28234f01e1b30dcdccdfbe696bd5703ddbf0554c8c4863edb0f252e2a9daa54d4900d8fd6aef61b8a8f1e165eea79a3f20663b08722762f2c548e0cd10cf0d2d8e8b9239b638d02c49749d55b721a6c81d2554b51b49191518150664b7e4dbde3ae02b36ab3ec9250290c8cee6b7371e0d3c7ad64d267cf463c4c82cc5a325aa72461345c4f45c8753de23bc148527b0e982511bed9e4bcd4cc37e1e3b5089373fcbfbef9a111c96cc79e9f37d619c0d68598541f2a37ea0fd614ca310c915c0d412d2eeee8f7d71e4a250bcc60d68ab3f296a5f75d75d627e5913aedf3646f733701218878049c420ce0089ded11b087f16111e397d0f2e354f1df0a858aaf07eeb8e80002210305cf1786fa950ae9b553390d6d62e2b285ebaeb978822439e0922403f9cc7dbc473045022100b807fa7bc196a7b2d7a3000e5e1870e2ff488bfd6e2850aeaefb3c606f28379e022009c3cec446550e5cb04483404a677c4b8406d85c62cb4d714e5ca3a50aa02f260028e8073a460a05174876e8001a1976a914dbe6d470fa9fe4d037043533eff4f80aeef0c8d288ac222244524271336345713233515955416d48736f634336664452764a453753723548455a" ) func init() { diff --git a/bchain/coins/qtum/qtumparser.go b/bchain/coins/qtum/qtumparser.go index e43b7aba6f..8bc2d5e943 100644 --- a/bchain/coins/qtum/qtumparser.go +++ b/bchain/coins/qtum/qtumparser.go @@ -45,9 +45,11 @@ type QtumParser struct { // NewQtumParser returns new DashParser instance func NewQtumParser(params *chaincfg.Params, c *btc.Configuration) *QtumParser { - return &QtumParser{ + p := &QtumParser{ BitcoinParser: btc.NewBitcoinParser(params, c), } + p.VSizeSupport = false + return p } // GetChainParams contains network parameters for the main Qtum network, diff --git a/bchain/coins/ravencoin/ravencoinparser_test.go b/bchain/coins/ravencoin/ravencoinparser_test.go index 4dbefebc69..dafa126b99 100644 --- a/bchain/coins/ravencoin/ravencoinparser_test.go +++ b/bchain/coins/ravencoin/ravencoinparser_test.go @@ -74,10 +74,10 @@ func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) { var ( testTx1 bchain.Tx - testTxPacked1 = "0a20d4d3a093586eae0c3668fd288d9e24955928a894c20b551b38dd18c99b123a7c12e1010200000001c171348ffc8976074fa064e48598a816fce3798afc635fb67d99580e50b8e614000000006a473044022009e07574fa543ad259bd3334eb365c655c96d310c578b64c24d7f77fa7dc591c0220427d8ae6eacd1ca2d1994e9ec49cb322aacdde98e4bdb065e0fce81162fb3aa9012102d46827546548b9b47ae1e9e84fc4e53513e0987eeb1dd41220ba39f67d3bf46affffffff02f8137114000000001976a914587a2afa560ccaeaeb67cb72a0db7e2573a179e488ace0c48110000000001976a914d85e6ab66ab0b2c4cfd40ca3b0a779529da5799288ac0000000018c7e1b3e5052000288491283298010a00122014e6b8500e58997db65f63fc8a79e3fc16a89885e464a04f077689fc8f3471c11800226a473044022009e07574fa543ad259bd3334eb365c655c96d310c578b64c24d7f77fa7dc591c0220427d8ae6eacd1ca2d1994e9ec49cb322aacdde98e4bdb065e0fce81162fb3aa9012102d46827546548b9b47ae1e9e84fc4e53513e0987eeb1dd41220ba39f67d3bf46a28ffffffff0f3a470a04147113f810001a1976a914587a2afa560ccaeaeb67cb72a0db7e2573a179e488ac222252484d31746d64766b6b3776446f69477877554a414d4e4e6d447179775a3574456e3a470a041081c4e010011a1976a914d85e6ab66ab0b2c4cfd40ca3b0a779529da5799288ac2222525631463939623955424272434d38614e4b7567737173444d3869716f4371374d744002" + testTxPacked1 = "0a20d4d3a093586eae0c3668fd288d9e24955928a894c20b551b38dd18c99b123a7c12e1010200000001c171348ffc8976074fa064e48598a816fce3798afc635fb67d99580e50b8e614000000006a473044022009e07574fa543ad259bd3334eb365c655c96d310c578b64c24d7f77fa7dc591c0220427d8ae6eacd1ca2d1994e9ec49cb322aacdde98e4bdb065e0fce81162fb3aa9012102d46827546548b9b47ae1e9e84fc4e53513e0987eeb1dd41220ba39f67d3bf46affffffff02f8137114000000001976a914587a2afa560ccaeaeb67cb72a0db7e2573a179e488ace0c48110000000001976a914d85e6ab66ab0b2c4cfd40ca3b0a779529da5799288ac0000000018c7e1b3e50528849128329401122014e6b8500e58997db65f63fc8a79e3fc16a89885e464a04f077689fc8f3471c1226a473044022009e07574fa543ad259bd3334eb365c655c96d310c578b64c24d7f77fa7dc591c0220427d8ae6eacd1ca2d1994e9ec49cb322aacdde98e4bdb065e0fce81162fb3aa9012102d46827546548b9b47ae1e9e84fc4e53513e0987eeb1dd41220ba39f67d3bf46a28ffffffff0f3a450a04147113f81a1976a914587a2afa560ccaeaeb67cb72a0db7e2573a179e488ac222252484d31746d64766b6b3776446f69477877554a414d4e4e6d447179775a3574456e3a470a041081c4e010011a1976a914d85e6ab66ab0b2c4cfd40ca3b0a779529da5799288ac2222525631463939623955424272434d38614e4b7567737173444d3869716f4371374d744002" testTx2 bchain.Tx - testTxPacked2 = "0a208e480d5c1bf7f11d1cbe396ab7dc14e01ea4e1aff45de7c055924f61304ad43412f40202000000029e2e14113b2f55726eebaa440edec707fcec3a31ce28fa125afea1e755fb6850010000006a47304402204034c3862f221551cffb2aa809f621f989a75cdb549c789a5ceb3a82c0bcc21c022001b4638f5d73fdd406a4dd9bf99be3dfca4a572b8f40f09b8fd495a7756c0db70121027a32ef45aef2f720ccf585f6fb0b8a7653db89cacc3320e5b385146851aba705fefffffff3b240ae32c542786876fcf23b4b2ab4c34ef077912898ee529756ed4ba35910000000006a47304402204d442645597b13abb85e96e5acd34eff50a4418822fe6a37ed378cdd24574dff02205ae667c56eab63cc45a51063f15b72136fd76e97c46af29bd28e8c4d405aa211012102cde27d7b29331ea3fef909a8d91f6f7753e99a3dd129914be50df26eed73fab3feffffff028447bf38000000001976a9146d7badec5426b880df25a3afc50e476c2423b34b88acb26b556a740000001976a914b3020d0ab85710151fa509d5d9a4e783903d681888ac83080a0018c7e1b3e50520839128288491283298010a0012205068fb55e7a1fe5a12fa28ce313aecfc07c7de0e44aaeb6e72552f3b11142e9e1801226a47304402204034c3862f221551cffb2aa809f621f989a75cdb549c789a5ceb3a82c0bcc21c022001b4638f5d73fdd406a4dd9bf99be3dfca4a572b8f40f09b8fd495a7756c0db70121027a32ef45aef2f720ccf585f6fb0b8a7653db89cacc3320e5b385146851aba70528feffffff0f3298010a0012201059a34bed569752ee98289177f04ec3b42a4b3bf2fc76687842c532ae40b2f31800226a47304402204d442645597b13abb85e96e5acd34eff50a4418822fe6a37ed378cdd24574dff02205ae667c56eab63cc45a51063f15b72136fd76e97c46af29bd28e8c4d405aa211012102cde27d7b29331ea3fef909a8d91f6f7753e99a3dd129914be50df26eed73fab328feffffff0f3a470a0438bf478410001a1976a9146d7badec5426b880df25a3afc50e476c2423b34b88ac2222524b4735747057776a6874716464546741335168556837516d4b637576426e6842583a480a05746a556bb210011a1976a914b3020d0ab85710151fa509d5d9a4e783903d681888ac222252526268564d624c6675657a485077554d756a546d4446417a76363459396d4a71644002" + testTxPacked2 = "0a208e480d5c1bf7f11d1cbe396ab7dc14e01ea4e1aff45de7c055924f61304ad43412f40202000000029e2e14113b2f55726eebaa440edec707fcec3a31ce28fa125afea1e755fb6850010000006a47304402204034c3862f221551cffb2aa809f621f989a75cdb549c789a5ceb3a82c0bcc21c022001b4638f5d73fdd406a4dd9bf99be3dfca4a572b8f40f09b8fd495a7756c0db70121027a32ef45aef2f720ccf585f6fb0b8a7653db89cacc3320e5b385146851aba705fefffffff3b240ae32c542786876fcf23b4b2ab4c34ef077912898ee529756ed4ba35910000000006a47304402204d442645597b13abb85e96e5acd34eff50a4418822fe6a37ed378cdd24574dff02205ae667c56eab63cc45a51063f15b72136fd76e97c46af29bd28e8c4d405aa211012102cde27d7b29331ea3fef909a8d91f6f7753e99a3dd129914be50df26eed73fab3feffffff028447bf38000000001976a9146d7badec5426b880df25a3afc50e476c2423b34b88acb26b556a740000001976a914b3020d0ab85710151fa509d5d9a4e783903d681888ac83080a0018c7e1b3e505208391282884912832960112205068fb55e7a1fe5a12fa28ce313aecfc07c7de0e44aaeb6e72552f3b11142e9e1801226a47304402204034c3862f221551cffb2aa809f621f989a75cdb549c789a5ceb3a82c0bcc21c022001b4638f5d73fdd406a4dd9bf99be3dfca4a572b8f40f09b8fd495a7756c0db70121027a32ef45aef2f720ccf585f6fb0b8a7653db89cacc3320e5b385146851aba70528feffffff0f32940112201059a34bed569752ee98289177f04ec3b42a4b3bf2fc76687842c532ae40b2f3226a47304402204d442645597b13abb85e96e5acd34eff50a4418822fe6a37ed378cdd24574dff02205ae667c56eab63cc45a51063f15b72136fd76e97c46af29bd28e8c4d405aa211012102cde27d7b29331ea3fef909a8d91f6f7753e99a3dd129914be50df26eed73fab328feffffff0f3a450a0438bf47841a1976a9146d7badec5426b880df25a3afc50e476c2423b34b88ac2222524b4735747057776a6874716464546741335168556837516d4b637576426e6842583a480a05746a556bb210011a1976a914b3020d0ab85710151fa509d5d9a4e783903d681888ac222252526268564d624c6675657a485077554d756a546d4446417a76363459396d4a71644002" ) func init() { diff --git a/bchain/coins/snowgem/snowgemparser_test.go b/bchain/coins/snowgem/snowgemparser_test.go index 2d8068f110..e6cac14357 100644 --- a/bchain/coins/snowgem/snowgemparser_test.go +++ b/bchain/coins/snowgem/snowgemparser_test.go @@ -19,8 +19,8 @@ import ( var ( testTx1, testTx2 bchain.Tx - testTxPacked1 = "0a20241803e368d7459f31286a155191ee386896d366d57c19d8e67a8f040d6ff71f12f4010400008085202f890119950c49d69b37d5f4fbb390d852387559e6a6d3fce9f390a409e4acf3f06381020000006a4730440220452aedf599e575598eb36d27ed98a6d388efda6e9be2bab96f16d0644e7df3060220669f4f3a4976ed73fa3ca9ecaad84dcf6ec35099c3bad631499985ea6a378d19012102ed9fb7fb61ec514be890ab45a925d554ff12050f099514251d5ebe904accc93ffeffffff02d3d0a146000000001976a9141a78c04d87f553545ba225b7bc7a271731f659d688ac7c54ae02000000001976a914b86f4b063545ebc2e80522a59d2dd206b707401b88aca68d0e00c58d0e00000000000000000000000018aba4b8ed0520a69b3a28b19b3a3298010a0012208163f0f3ace409a490f3e9fcd3a6e659753852d890b3fbf4d5379bd6490c95191802226a4730440220452aedf599e575598eb36d27ed98a6d388efda6e9be2bab96f16d0644e7df3060220669f4f3a4976ed73fa3ca9ecaad84dcf6ec35099c3bad631499985ea6a378d19012102ed9fb7fb61ec514be890ab45a925d554ff12050f099514251d5ebe904accc93f28feffffff0f3a480a0446a1d0d310001a1976a9141a78c04d87f553545ba225b7bc7a271731f659d688ac2223733150636953644665724a78665673397451353571446f3839695676466f7162436a7a3a480a0402ae547c10011a1976a914b86f4b063545ebc2e80522a59d2dd206b707401b88ac22237331653177736d6f7955625673794b726745374b73714c5164374c69755961685261524000" - testTxPacked2 = "0a2071dd4d998b0a711fe5ed21f8661ed27ca8b99afc488f5bbe149ec3c6492ec50312d2010400008085202f89017308714b21338783a435c5e420542a0f6243da5be6dc8bdf19e2d526a318d6a8000000006a47304402207ce5ebcb2dc5e8027b5d672babd2e6aaa186a917caf2b44eec63f7db16277b8b02207a89214d825fae08ebc86bca1f46579e770e830bd31b8101498207a2d901fd74012103c3fe8969a7b08f1d586a68da70d6aeff61aa3b4cbe7ca2cb5aae11529ca2af12feffffff014dd45023000000001976a914cef34ec02e80351cf4f9d63843fc79a77c9ab71888acaa8d0e00c98d0e00000000000000000000000018f9a6b8ed0520aa9b3a28b59b3a3298010a001220a8d618a326d5e219df8bdce65bda43620f2a5420e4c535a4838733214b7108731800226a47304402207ce5ebcb2dc5e8027b5d672babd2e6aaa186a917caf2b44eec63f7db16277b8b02207a89214d825fae08ebc86bca1f46579e770e830bd31b8101498207a2d901fd74012103c3fe8969a7b08f1d586a68da70d6aeff61aa3b4cbe7ca2cb5aae11529ca2af1228feffffff0f3a480a042350d44d10001a1976a914cef34ec02e80351cf4f9d63843fc79a77c9ab71888ac2223733167347a74585446447751326b506253385431666755334c645075666376354d764d4000" + testTxPacked1 = "0a20241803e368d7459f31286a155191ee386896d366d57c19d8e67a8f040d6ff71f12f4010400008085202f890119950c49d69b37d5f4fbb390d852387559e6a6d3fce9f390a409e4acf3f06381020000006a4730440220452aedf599e575598eb36d27ed98a6d388efda6e9be2bab96f16d0644e7df3060220669f4f3a4976ed73fa3ca9ecaad84dcf6ec35099c3bad631499985ea6a378d19012102ed9fb7fb61ec514be890ab45a925d554ff12050f099514251d5ebe904accc93ffeffffff02d3d0a146000000001976a9141a78c04d87f553545ba225b7bc7a271731f659d688ac7c54ae02000000001976a914b86f4b063545ebc2e80522a59d2dd206b707401b88aca68d0e00c58d0e00000000000000000000000018aba4b8ed0520a69b3a28b19b3a32960112208163f0f3ace409a490f3e9fcd3a6e659753852d890b3fbf4d5379bd6490c95191802226a4730440220452aedf599e575598eb36d27ed98a6d388efda6e9be2bab96f16d0644e7df3060220669f4f3a4976ed73fa3ca9ecaad84dcf6ec35099c3bad631499985ea6a378d19012102ed9fb7fb61ec514be890ab45a925d554ff12050f099514251d5ebe904accc93f28feffffff0f3a460a0446a1d0d31a1976a9141a78c04d87f553545ba225b7bc7a271731f659d688ac2223733150636953644665724a78665673397451353571446f3839695676466f7162436a7a3a480a0402ae547c10011a1976a914b86f4b063545ebc2e80522a59d2dd206b707401b88ac22237331653177736d6f7955625673794b726745374b73714c5164374c6975596168526152" + testTxPacked2 = "0a2071dd4d998b0a711fe5ed21f8661ed27ca8b99afc488f5bbe149ec3c6492ec50312d2010400008085202f89017308714b21338783a435c5e420542a0f6243da5be6dc8bdf19e2d526a318d6a8000000006a47304402207ce5ebcb2dc5e8027b5d672babd2e6aaa186a917caf2b44eec63f7db16277b8b02207a89214d825fae08ebc86bca1f46579e770e830bd31b8101498207a2d901fd74012103c3fe8969a7b08f1d586a68da70d6aeff61aa3b4cbe7ca2cb5aae11529ca2af12feffffff014dd45023000000001976a914cef34ec02e80351cf4f9d63843fc79a77c9ab71888acaa8d0e00c98d0e00000000000000000000000018f9a6b8ed0520aa9b3a28b59b3a3294011220a8d618a326d5e219df8bdce65bda43620f2a5420e4c535a4838733214b710873226a47304402207ce5ebcb2dc5e8027b5d672babd2e6aaa186a917caf2b44eec63f7db16277b8b02207a89214d825fae08ebc86bca1f46579e770e830bd31b8101498207a2d901fd74012103c3fe8969a7b08f1d586a68da70d6aeff61aa3b4cbe7ca2cb5aae11529ca2af1228feffffff0f3a460a042350d44d1a1976a914cef34ec02e80351cf4f9d63843fc79a77c9ab71888ac2223733167347a74585446447751326b506253385431666755334c645075666376354d764d" ) func init() { diff --git a/bchain/coins/vertcoin/vertcoinparser.go b/bchain/coins/vertcoin/vertcoinparser.go index 35d6f830d0..fca5282dcf 100644 --- a/bchain/coins/vertcoin/vertcoinparser.go +++ b/bchain/coins/vertcoin/vertcoinparser.go @@ -40,7 +40,9 @@ type VertcoinParser struct { // NewVertcoinParser returns new VertcoinParser instance func NewVertcoinParser(params *chaincfg.Params, c *btc.Configuration) *VertcoinParser { - return &VertcoinParser{BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c)} + p := &VertcoinParser{BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c)} + p.VSizeSupport = true + return p } // GetChainParams contains network parameters for the main Vertcoin network, diff --git a/bchain/coins/vertcoin/vertcoinparser_test.go b/bchain/coins/vertcoin/vertcoinparser_test.go index aec4cbcc40..ce1cbd672f 100644 --- a/bchain/coins/vertcoin/vertcoinparser_test.go +++ b/bchain/coins/vertcoin/vertcoinparser_test.go @@ -90,6 +90,7 @@ func init() { Blocktime: 1529925180, Txid: "d58c11aa970449c3e0ee5e0cdf78532435a9d2b28a2da284a8dd4dd6bdd0331c", LockTime: 952180, + VSize: 223, Version: 1, Vin: []bchain.Vin{ { diff --git a/bchain/coins/zec/zcashparser_test.go b/bchain/coins/zec/zcashparser_test.go index 9e4758ca78..2dee9c66a8 100644 --- a/bchain/coins/zec/zcashparser_test.go +++ b/bchain/coins/zec/zcashparser_test.go @@ -18,8 +18,8 @@ import ( var ( testTx1, testTx2 bchain.Tx - testTxPacked1 = "0a20e64aac0c211ad210c90934f06b1cc932327329e41a9f70c6eb76f79ef798b7b812ab1002000000019c012650c99d0ef761e863dbb966babf2cb7a7a2b5d90b1461c09521c473d23d000000006b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c83ffffffff018eec1a3c040000001976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac000000000162b4fc6b0000000000000000000000006ffa88c89b74f0f82e24744296845a0d0113b132ff5dfc2af34e6418eb15206af53078c4dd475cf143cd9a427983f5993622464b53e3a37d2519a946492c3977e30f0866550b9097222993a439a39260ac5e7d36aef38c7fdd1df3035a2d5817a9c20526e38f52f822d4db9d2f0156c4119d786d6e3a060ca871df7fae9a5c3a9c921b38ddc6414b13d16aa807389c68016e54bd6a9eb3b23a6bc7bf152e6dba15e9ec36f95dab15ad8f4a92a9d0309bbd930ef24bb7247bf534065c1e2f5b42e2c80eb59f48b4da6ec522319e065f8c4e463f95cc7fcad8d7ee91608e3c0ffcaa44129ba2d2da45d9a413919eca41af29faaf806a3eeb823e5a6c51afb1ec709505d812c0306bd76061a0a62d207355ad44d1ffce2b9e1dfd0818f79bd0f8e4031116b71fee2488484f17818b80532865773166cd389929e8409bb94e3948bd2e0215ef96d4e29d094590fda0de50715c11ff47c03380bb1d31b14e5b4ad8a372ca0b03364ef85f086b8a8eb5c56c3b1aee33e2cfbf1b2be1a3fb41b14b2c432b5d04d54c058fa87a96ae1d65d61b79360d09acc1e25a883fd7ae9a2a734a03362903021401c243173e1050b5cdb459b9ffc07c95e920f026618952d3a800b2e47e03b902084aed7ee8466a65d34abdbbd292781564dcd9b7440029d48c2640ebc196d4b40217f2872c1d0c1c9c2abf1147d6a5a9501895bc92960bfa182ceeb76a658224f1022bc53c4c1cd6888d72a152dc1aec5ba8a1d750fb7e498bee844d3481e4b4cd210227f94f775744185c9f24571b7df0c1c694cb2d3e4e9b955ed0b1caad2b02b5702139c4fbba03f0e422b2f3e4fc822b4f58baf32e7cd217cdbdec8540cb13d6496f271959b72a05e130eeffbe5b9a7fcd2793347cd9c0ea695265669844c363190f690c52a600cf413c3f00bdc5e9d1539e0cc63f4ec2945e0d86e6304a6deb5651e73eac21add5a641dfc95ab56200ed40d81f76755aee4659334c17ed3841ca5a5ab22f923956be1d264be2b485a0de55404510ece5c73d6626798be688f9dc18b69846acfe897a357cc4afe31f57fea32896717f124290e68f36f849fa6ecf76e02087f8c19dbc566135d7fa2daca2d843b9cc5bc3897d35f1de7d174f6407658f4a3706c12cea53d880b4d8c4d45b3f0d210214f815be49a664021a4a44b4a63e06a41d76b46f9aa6bad248e8d1a974ae7bbae5ea8ac269447db91637a19346729083cad5aebd5ff43ea13d04783068e9136da321b1152c666d2995d0ca06b26541deac62f4ef91f0e4af445b18a5c2a17c96eada0b27f85bb26dfb8f16515114c6b9f88037e2b85b3b84b65822eb99c992d99d12dcf9c71e5b46a586016faf5758483a716566db95b42187c101df68ca0554824e1c23cf0302bea03ad0a146af57e91794a268b8c82d78211718c8b5fea286f5de72fc7dfffecddcc02413525c472cb26022641d4bec2b8b7e71a7beb9ee18b82632799498eeee9a351cb9431a8d1906d5164acdf351bd538c3e9d1da8a211fe1cd18c44e72d8cdf16ce3fc9551552c05d52846ea7ef619232102588395cc2bcce509a4e7f150262a76c15475496c923dfce6bfc05871467ee7c213b39ea365c010083e0b1ba8926d3a9e586d8b11c9bab2a47d888bc7cb1a226c0086a1530e295d0047547006f4c8f1c24cdd8e16bb3845749895dec95f03fcda97d3224f6875b1b7b1c819d2fd35dd30968a3c82bc480d10082caf9d9dda8f9ec649c136c7fa07978099d97eaf4abfdc9854c266979d3cfc868f60689b6e3098b6c52a21796fe7c259d9a0dadf1b6efa59297d4c8c902febe7acf826eed30d40d2ac5119be91b51f4839d94599872c9a93c3e2691294914034001d3a278cb4a84d4ae048c0201a97e4cf1341ee663a162f5b586355018b9e5e30624ccdbeacf7d0382afacaf45f08e84d30c50bcd4e55c3138377261deb4e8c2931cd3c51cee94a048ae4839517b6e6537a5c0148d3830a33fea719ef9b4fa437e4d5fecdb646397c19ee56a0973c362a81803895cdc67246352dc566689cb203f9ebda900a5537bbb75aa25ddf3d4ab87b88737a58d760e1d271f08265daae1fe056e71971a8b826e5b215a05b71f99315b167dd2ec78874189657acafac2b5eeb9a901913f55f7ab69e1f9b203504448d414e71098b932a2309db57257eb3fef9de2f2a5a69aa46747d7b827df838345d38b95772bdab8c178c45777b92e8773864964b8e12ae29dbc1b21bf6527589f6bec71ff1cbb9928477409811c2e8150c79c3f21027ee954863b716875d3e9adfc6fdb18cd57a49bb395ca5c42da56f3beb78aad3a7a487de34a870bca61f3cdec422061328c83c910ab32ea7403c354915b7ebee29e1fea5a75158197e4a68e103f017fd7de5a70148ee7ce59356b1a74f83492e14faaa6cd4870bcc004e6eb0114d3429b74ea98fe2851b4553467a7660074e69b040aa31220d0e405d9166dbaf15e3ae2d8ec3b049ed99d17e0743bb6a1a7c3890bbdb7117f7374ad7a59aa1ab47d10445b28f4bc033794a71f88a8bf024189e9d27f9dc5859a4296437585b215656f807aca9dad35747494a43b8a1cf38be2b18a13de32a262ab29f9ba271c4fbce1a470a8243ebf9e7fd37b09262314afbb9a7e180218a0f1c9d505200028b0eb113299010a0012203dd273c42195c061140bd9b5a2a7b72cbfba66b9db63e861f70e9dc95026019c1800226b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c8328ffffffff0f3a490a05043c1aec8e10001a1976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac222374315934794c31344143486141626a656d6b647057376e594e48576e763179516244414000" - testTxPacked2 = "0a20bb47a9dd926de63e9d4f8dac58c3f63f4a079569ed3b80e932274a80f60e58b512e20101000000019cafb5c287980e6e5afb47339f6c1c81136d8255f5bd5226b36b01288494c46f000000006b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea22880feffffff0223a7a784010000001976a914826f87806ddd4643730be99b41c98acc379e83db88ac80969800000000001976a914e395634b7684289285926d4c64db395b783720ec88ac6e75040018e4b1c9d50520eeea1128f9ea113299010a0012206fc4948428016bb32652bdf555826d13811c6c9f3347fb5a6e0e9887c2b5af9c1800226b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea2288028feffffff0f3a490a050184a7a72310001a1976a914826f87806ddd4643730be99b41c98acc379e83db88ac22237431566d4854547770457477766f6a786f644e32435351714c596931687a59336341713a470a0398968010011a1976a914e395634b7684289285926d4c64db395b783720ec88ac222374316563784d587070685554525158474c586e56684a367563714433445a69706464674000" + testTxPacked1 = "0a20e64aac0c211ad210c90934f06b1cc932327329e41a9f70c6eb76f79ef798b7b812ab1002000000019c012650c99d0ef761e863dbb966babf2cb7a7a2b5d90b1461c09521c473d23d000000006b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c83ffffffff018eec1a3c040000001976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac000000000162b4fc6b0000000000000000000000006ffa88c89b74f0f82e24744296845a0d0113b132ff5dfc2af34e6418eb15206af53078c4dd475cf143cd9a427983f5993622464b53e3a37d2519a946492c3977e30f0866550b9097222993a439a39260ac5e7d36aef38c7fdd1df3035a2d5817a9c20526e38f52f822d4db9d2f0156c4119d786d6e3a060ca871df7fae9a5c3a9c921b38ddc6414b13d16aa807389c68016e54bd6a9eb3b23a6bc7bf152e6dba15e9ec36f95dab15ad8f4a92a9d0309bbd930ef24bb7247bf534065c1e2f5b42e2c80eb59f48b4da6ec522319e065f8c4e463f95cc7fcad8d7ee91608e3c0ffcaa44129ba2d2da45d9a413919eca41af29faaf806a3eeb823e5a6c51afb1ec709505d812c0306bd76061a0a62d207355ad44d1ffce2b9e1dfd0818f79bd0f8e4031116b71fee2488484f17818b80532865773166cd389929e8409bb94e3948bd2e0215ef96d4e29d094590fda0de50715c11ff47c03380bb1d31b14e5b4ad8a372ca0b03364ef85f086b8a8eb5c56c3b1aee33e2cfbf1b2be1a3fb41b14b2c432b5d04d54c058fa87a96ae1d65d61b79360d09acc1e25a883fd7ae9a2a734a03362903021401c243173e1050b5cdb459b9ffc07c95e920f026618952d3a800b2e47e03b902084aed7ee8466a65d34abdbbd292781564dcd9b7440029d48c2640ebc196d4b40217f2872c1d0c1c9c2abf1147d6a5a9501895bc92960bfa182ceeb76a658224f1022bc53c4c1cd6888d72a152dc1aec5ba8a1d750fb7e498bee844d3481e4b4cd210227f94f775744185c9f24571b7df0c1c694cb2d3e4e9b955ed0b1caad2b02b5702139c4fbba03f0e422b2f3e4fc822b4f58baf32e7cd217cdbdec8540cb13d6496f271959b72a05e130eeffbe5b9a7fcd2793347cd9c0ea695265669844c363190f690c52a600cf413c3f00bdc5e9d1539e0cc63f4ec2945e0d86e6304a6deb5651e73eac21add5a641dfc95ab56200ed40d81f76755aee4659334c17ed3841ca5a5ab22f923956be1d264be2b485a0de55404510ece5c73d6626798be688f9dc18b69846acfe897a357cc4afe31f57fea32896717f124290e68f36f849fa6ecf76e02087f8c19dbc566135d7fa2daca2d843b9cc5bc3897d35f1de7d174f6407658f4a3706c12cea53d880b4d8c4d45b3f0d210214f815be49a664021a4a44b4a63e06a41d76b46f9aa6bad248e8d1a974ae7bbae5ea8ac269447db91637a19346729083cad5aebd5ff43ea13d04783068e9136da321b1152c666d2995d0ca06b26541deac62f4ef91f0e4af445b18a5c2a17c96eada0b27f85bb26dfb8f16515114c6b9f88037e2b85b3b84b65822eb99c992d99d12dcf9c71e5b46a586016faf5758483a716566db95b42187c101df68ca0554824e1c23cf0302bea03ad0a146af57e91794a268b8c82d78211718c8b5fea286f5de72fc7dfffecddcc02413525c472cb26022641d4bec2b8b7e71a7beb9ee18b82632799498eeee9a351cb9431a8d1906d5164acdf351bd538c3e9d1da8a211fe1cd18c44e72d8cdf16ce3fc9551552c05d52846ea7ef619232102588395cc2bcce509a4e7f150262a76c15475496c923dfce6bfc05871467ee7c213b39ea365c010083e0b1ba8926d3a9e586d8b11c9bab2a47d888bc7cb1a226c0086a1530e295d0047547006f4c8f1c24cdd8e16bb3845749895dec95f03fcda97d3224f6875b1b7b1c819d2fd35dd30968a3c82bc480d10082caf9d9dda8f9ec649c136c7fa07978099d97eaf4abfdc9854c266979d3cfc868f60689b6e3098b6c52a21796fe7c259d9a0dadf1b6efa59297d4c8c902febe7acf826eed30d40d2ac5119be91b51f4839d94599872c9a93c3e2691294914034001d3a278cb4a84d4ae048c0201a97e4cf1341ee663a162f5b586355018b9e5e30624ccdbeacf7d0382afacaf45f08e84d30c50bcd4e55c3138377261deb4e8c2931cd3c51cee94a048ae4839517b6e6537a5c0148d3830a33fea719ef9b4fa437e4d5fecdb646397c19ee56a0973c362a81803895cdc67246352dc566689cb203f9ebda900a5537bbb75aa25ddf3d4ab87b88737a58d760e1d271f08265daae1fe056e71971a8b826e5b215a05b71f99315b167dd2ec78874189657acafac2b5eeb9a901913f55f7ab69e1f9b203504448d414e71098b932a2309db57257eb3fef9de2f2a5a69aa46747d7b827df838345d38b95772bdab8c178c45777b92e8773864964b8e12ae29dbc1b21bf6527589f6bec71ff1cbb9928477409811c2e8150c79c3f21027ee954863b716875d3e9adfc6fdb18cd57a49bb395ca5c42da56f3beb78aad3a7a487de34a870bca61f3cdec422061328c83c910ab32ea7403c354915b7ebee29e1fea5a75158197e4a68e103f017fd7de5a70148ee7ce59356b1a74f83492e14faaa6cd4870bcc004e6eb0114d3429b74ea98fe2851b4553467a7660074e69b040aa31220d0e405d9166dbaf15e3ae2d8ec3b049ed99d17e0743bb6a1a7c3890bbdb7117f7374ad7a59aa1ab47d10445b28f4bc033794a71f88a8bf024189e9d27f9dc5859a4296437585b215656f807aca9dad35747494a43b8a1cf38be2b18a13de32a262ab29f9ba271c4fbce1a470a8243ebf9e7fd37b09262314afbb9a7e180218a0f1c9d50528b0eb1132950112203dd273c42195c061140bd9b5a2a7b72cbfba66b9db63e861f70e9dc95026019c226b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c8328ffffffff0f3a470a05043c1aec8e1a1976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac222374315934794c31344143486141626a656d6b647057376e594e48576e76317951624441" + testTxPacked2 = "0a20bb47a9dd926de63e9d4f8dac58c3f63f4a079569ed3b80e932274a80f60e58b512e20101000000019cafb5c287980e6e5afb47339f6c1c81136d8255f5bd5226b36b01288494c46f000000006b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea22880feffffff0223a7a784010000001976a914826f87806ddd4643730be99b41c98acc379e83db88ac80969800000000001976a914e395634b7684289285926d4c64db395b783720ec88ac6e75040018e4b1c9d50520eeea1128f9ea1132950112206fc4948428016bb32652bdf555826d13811c6c9f3347fb5a6e0e9887c2b5af9c226b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea2288028feffffff0f3a470a050184a7a7231a1976a914826f87806ddd4643730be99b41c98acc379e83db88ac22237431566d4854547770457477766f6a786f644e32435351714c596931687a59336341713a470a0398968010011a1976a914e395634b7684289285926d4c64db395b783720ec88ac222374316563784d587070685554525158474c586e56684a367563714433445a6970646467" ) func init() { diff --git a/bchain/tx.pb.go b/bchain/tx.pb.go index d271806936..e17d8f8dfa 100644 --- a/bchain/tx.pb.go +++ b/bchain/tx.pb.go @@ -1,230 +1,426 @@ // Code generated by protoc-gen-go. DO NOT EDIT. -// source: tx.proto +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.5 +// source: bchain/tx.proto -/* -Package bchain is a generated protocol buffer package. - -It is generated from these files: - tx.proto - -It has these top-level messages: - ProtoTransaction -*/ package bchain -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) type ProtoTransaction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + Txid []byte `protobuf:"bytes,1,opt,name=Txid,proto3" json:"Txid,omitempty"` Hex []byte `protobuf:"bytes,2,opt,name=Hex,proto3" json:"Hex,omitempty"` - Blocktime uint64 `protobuf:"varint,3,opt,name=Blocktime" json:"Blocktime,omitempty"` - Locktime uint32 `protobuf:"varint,4,opt,name=Locktime" json:"Locktime,omitempty"` - Height uint32 `protobuf:"varint,5,opt,name=Height" json:"Height,omitempty"` - Vin []*ProtoTransaction_VinType `protobuf:"bytes,6,rep,name=Vin" json:"Vin,omitempty"` - Vout []*ProtoTransaction_VoutType `protobuf:"bytes,7,rep,name=Vout" json:"Vout,omitempty"` - Version int32 `protobuf:"varint,8,opt,name=Version" json:"Version,omitempty"` + Blocktime uint64 `protobuf:"varint,3,opt,name=Blocktime,proto3" json:"Blocktime,omitempty"` + Locktime uint32 `protobuf:"varint,4,opt,name=Locktime,proto3" json:"Locktime,omitempty"` + Height uint32 `protobuf:"varint,5,opt,name=Height,proto3" json:"Height,omitempty"` + Vin []*ProtoTransaction_VinType `protobuf:"bytes,6,rep,name=Vin,proto3" json:"Vin,omitempty"` + Vout []*ProtoTransaction_VoutType `protobuf:"bytes,7,rep,name=Vout,proto3" json:"Vout,omitempty"` + Version int32 `protobuf:"varint,8,opt,name=Version,proto3" json:"Version,omitempty"` + VSize int64 `protobuf:"varint,9,opt,name=VSize,proto3" json:"VSize,omitempty"` } -func (m *ProtoTransaction) Reset() { *m = ProtoTransaction{} } -func (m *ProtoTransaction) String() string { return proto.CompactTextString(m) } -func (*ProtoTransaction) ProtoMessage() {} -func (*ProtoTransaction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (x *ProtoTransaction) Reset() { + *x = ProtoTransaction{} + if protoimpl.UnsafeEnabled { + mi := &file_bchain_tx_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -func (m *ProtoTransaction) GetTxid() []byte { - if m != nil { - return m.Txid +func (x *ProtoTransaction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProtoTransaction) ProtoMessage() {} + +func (x *ProtoTransaction) ProtoReflect() protoreflect.Message { + mi := &file_bchain_tx_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProtoTransaction.ProtoReflect.Descriptor instead. +func (*ProtoTransaction) Descriptor() ([]byte, []int) { + return file_bchain_tx_proto_rawDescGZIP(), []int{0} +} + +func (x *ProtoTransaction) GetTxid() []byte { + if x != nil { + return x.Txid } return nil } -func (m *ProtoTransaction) GetHex() []byte { - if m != nil { - return m.Hex +func (x *ProtoTransaction) GetHex() []byte { + if x != nil { + return x.Hex } return nil } -func (m *ProtoTransaction) GetBlocktime() uint64 { - if m != nil { - return m.Blocktime +func (x *ProtoTransaction) GetBlocktime() uint64 { + if x != nil { + return x.Blocktime } return 0 } -func (m *ProtoTransaction) GetLocktime() uint32 { - if m != nil { - return m.Locktime +func (x *ProtoTransaction) GetLocktime() uint32 { + if x != nil { + return x.Locktime } return 0 } -func (m *ProtoTransaction) GetHeight() uint32 { - if m != nil { - return m.Height +func (x *ProtoTransaction) GetHeight() uint32 { + if x != nil { + return x.Height } return 0 } -func (m *ProtoTransaction) GetVin() []*ProtoTransaction_VinType { - if m != nil { - return m.Vin +func (x *ProtoTransaction) GetVin() []*ProtoTransaction_VinType { + if x != nil { + return x.Vin } return nil } -func (m *ProtoTransaction) GetVout() []*ProtoTransaction_VoutType { - if m != nil { - return m.Vout +func (x *ProtoTransaction) GetVout() []*ProtoTransaction_VoutType { + if x != nil { + return x.Vout } return nil } -func (m *ProtoTransaction) GetVersion() int32 { - if m != nil { - return m.Version +func (x *ProtoTransaction) GetVersion() int32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *ProtoTransaction) GetVSize() int64 { + if x != nil { + return x.VSize } return 0 } type ProtoTransaction_VinType struct { - Coinbase string `protobuf:"bytes,1,opt,name=Coinbase" json:"Coinbase,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Coinbase string `protobuf:"bytes,1,opt,name=Coinbase,proto3" json:"Coinbase,omitempty"` Txid []byte `protobuf:"bytes,2,opt,name=Txid,proto3" json:"Txid,omitempty"` - Vout uint32 `protobuf:"varint,3,opt,name=Vout" json:"Vout,omitempty"` + Vout uint32 `protobuf:"varint,3,opt,name=Vout,proto3" json:"Vout,omitempty"` ScriptSigHex []byte `protobuf:"bytes,4,opt,name=ScriptSigHex,proto3" json:"ScriptSigHex,omitempty"` - Sequence uint32 `protobuf:"varint,5,opt,name=Sequence" json:"Sequence,omitempty"` - Addresses []string `protobuf:"bytes,6,rep,name=Addresses" json:"Addresses,omitempty"` + Sequence uint32 `protobuf:"varint,5,opt,name=Sequence,proto3" json:"Sequence,omitempty"` + Addresses []string `protobuf:"bytes,6,rep,name=Addresses,proto3" json:"Addresses,omitempty"` +} + +func (x *ProtoTransaction_VinType) Reset() { + *x = ProtoTransaction_VinType{} + if protoimpl.UnsafeEnabled { + mi := &file_bchain_tx_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProtoTransaction_VinType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProtoTransaction_VinType) ProtoMessage() {} + +func (x *ProtoTransaction_VinType) ProtoReflect() protoreflect.Message { + mi := &file_bchain_tx_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *ProtoTransaction_VinType) Reset() { *m = ProtoTransaction_VinType{} } -func (m *ProtoTransaction_VinType) String() string { return proto.CompactTextString(m) } -func (*ProtoTransaction_VinType) ProtoMessage() {} -func (*ProtoTransaction_VinType) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} } +// Deprecated: Use ProtoTransaction_VinType.ProtoReflect.Descriptor instead. +func (*ProtoTransaction_VinType) Descriptor() ([]byte, []int) { + return file_bchain_tx_proto_rawDescGZIP(), []int{0, 0} +} -func (m *ProtoTransaction_VinType) GetCoinbase() string { - if m != nil { - return m.Coinbase +func (x *ProtoTransaction_VinType) GetCoinbase() string { + if x != nil { + return x.Coinbase } return "" } -func (m *ProtoTransaction_VinType) GetTxid() []byte { - if m != nil { - return m.Txid +func (x *ProtoTransaction_VinType) GetTxid() []byte { + if x != nil { + return x.Txid } return nil } -func (m *ProtoTransaction_VinType) GetVout() uint32 { - if m != nil { - return m.Vout +func (x *ProtoTransaction_VinType) GetVout() uint32 { + if x != nil { + return x.Vout } return 0 } -func (m *ProtoTransaction_VinType) GetScriptSigHex() []byte { - if m != nil { - return m.ScriptSigHex +func (x *ProtoTransaction_VinType) GetScriptSigHex() []byte { + if x != nil { + return x.ScriptSigHex } return nil } -func (m *ProtoTransaction_VinType) GetSequence() uint32 { - if m != nil { - return m.Sequence +func (x *ProtoTransaction_VinType) GetSequence() uint32 { + if x != nil { + return x.Sequence } return 0 } -func (m *ProtoTransaction_VinType) GetAddresses() []string { - if m != nil { - return m.Addresses +func (x *ProtoTransaction_VinType) GetAddresses() []string { + if x != nil { + return x.Addresses } return nil } type ProtoTransaction_VoutType struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + ValueSat []byte `protobuf:"bytes,1,opt,name=ValueSat,proto3" json:"ValueSat,omitempty"` - N uint32 `protobuf:"varint,2,opt,name=N" json:"N,omitempty"` + N uint32 `protobuf:"varint,2,opt,name=N,proto3" json:"N,omitempty"` ScriptPubKeyHex []byte `protobuf:"bytes,3,opt,name=ScriptPubKeyHex,proto3" json:"ScriptPubKeyHex,omitempty"` - Addresses []string `protobuf:"bytes,4,rep,name=Addresses" json:"Addresses,omitempty"` + Addresses []string `protobuf:"bytes,4,rep,name=Addresses,proto3" json:"Addresses,omitempty"` } -func (m *ProtoTransaction_VoutType) Reset() { *m = ProtoTransaction_VoutType{} } -func (m *ProtoTransaction_VoutType) String() string { return proto.CompactTextString(m) } -func (*ProtoTransaction_VoutType) ProtoMessage() {} -func (*ProtoTransaction_VoutType) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 1} } +func (x *ProtoTransaction_VoutType) Reset() { + *x = ProtoTransaction_VoutType{} + if protoimpl.UnsafeEnabled { + mi := &file_bchain_tx_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -func (m *ProtoTransaction_VoutType) GetValueSat() []byte { - if m != nil { - return m.ValueSat +func (x *ProtoTransaction_VoutType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProtoTransaction_VoutType) ProtoMessage() {} + +func (x *ProtoTransaction_VoutType) ProtoReflect() protoreflect.Message { + mi := &file_bchain_tx_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProtoTransaction_VoutType.ProtoReflect.Descriptor instead. +func (*ProtoTransaction_VoutType) Descriptor() ([]byte, []int) { + return file_bchain_tx_proto_rawDescGZIP(), []int{0, 1} +} + +func (x *ProtoTransaction_VoutType) GetValueSat() []byte { + if x != nil { + return x.ValueSat } return nil } -func (m *ProtoTransaction_VoutType) GetN() uint32 { - if m != nil { - return m.N +func (x *ProtoTransaction_VoutType) GetN() uint32 { + if x != nil { + return x.N } return 0 } -func (m *ProtoTransaction_VoutType) GetScriptPubKeyHex() []byte { - if m != nil { - return m.ScriptPubKeyHex +func (x *ProtoTransaction_VoutType) GetScriptPubKeyHex() []byte { + if x != nil { + return x.ScriptPubKeyHex } return nil } -func (m *ProtoTransaction_VoutType) GetAddresses() []string { - if m != nil { - return m.Addresses +func (x *ProtoTransaction_VoutType) GetAddresses() []string { + if x != nil { + return x.Addresses } return nil } -func init() { - proto.RegisterType((*ProtoTransaction)(nil), "bchain.ProtoTransaction") - proto.RegisterType((*ProtoTransaction_VinType)(nil), "bchain.ProtoTransaction.VinType") - proto.RegisterType((*ProtoTransaction_VoutType)(nil), "bchain.ProtoTransaction.VoutType") -} - -func init() { proto.RegisterFile("tx.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 346 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0x41, 0x6e, 0xea, 0x30, - 0x14, 0x94, 0x89, 0x09, 0xe1, 0x7d, 0xd0, 0x47, 0x5e, 0x54, 0x16, 0xea, 0x22, 0x65, 0x95, 0x55, - 0x16, 0x54, 0x3d, 0x40, 0xdb, 0x0d, 0x52, 0x2b, 0x84, 0x1c, 0x94, 0x7d, 0x12, 0x2c, 0xb0, 0x4a, - 0x6d, 0x9a, 0x38, 0x12, 0x48, 0xbd, 0x51, 0x8f, 0xd0, 0xcb, 0x55, 0x7e, 0x84, 0x50, 0x90, 0xba, - 0xf3, 0x8c, 0xdf, 0x64, 0xe6, 0x4d, 0x0c, 0x81, 0xdd, 0xc7, 0xbb, 0xd2, 0x58, 0xc3, 0xfc, 0xbc, - 0xd8, 0x64, 0x4a, 0x4f, 0xbe, 0x29, 0x8c, 0x16, 0x8e, 0x59, 0x96, 0x99, 0xae, 0xb2, 0xc2, 0x2a, - 0xa3, 0x19, 0x03, 0xba, 0xdc, 0xab, 0x15, 0x27, 0x21, 0x89, 0x06, 0x02, 0xcf, 0x6c, 0x04, 0xde, - 0x4c, 0xee, 0x79, 0x07, 0x29, 0x77, 0x64, 0xb7, 0xd0, 0x7f, 0xda, 0x9a, 0xe2, 0xcd, 0xaa, 0x77, - 0xc9, 0xbd, 0x90, 0x44, 0x54, 0x9c, 0x09, 0x36, 0x86, 0xe0, 0xf5, 0x74, 0x49, 0x43, 0x12, 0x0d, - 0x45, 0x8b, 0xd9, 0x0d, 0xf8, 0x33, 0xa9, 0xd6, 0x1b, 0xcb, 0xbb, 0x78, 0xd3, 0x20, 0x36, 0x05, - 0x2f, 0x55, 0x9a, 0xfb, 0xa1, 0x17, 0xfd, 0x9b, 0x86, 0xf1, 0x31, 0x62, 0x7c, 0x1d, 0x2f, 0x4e, - 0x95, 0x5e, 0x1e, 0x76, 0x52, 0xb8, 0x61, 0xf6, 0x00, 0x34, 0x35, 0xb5, 0xe5, 0x3d, 0x14, 0xdd, - 0xfd, 0x2d, 0x32, 0xb5, 0x45, 0x15, 0x8e, 0x33, 0x0e, 0xbd, 0x54, 0x96, 0x95, 0x32, 0x9a, 0x07, - 0x21, 0x89, 0xba, 0xe2, 0x04, 0xc7, 0x5f, 0x04, 0x7a, 0x8d, 0x83, 0x5b, 0xe2, 0xd9, 0x28, 0x9d, - 0x67, 0x95, 0xc4, 0x32, 0xfa, 0xa2, 0xc5, 0x6d, 0x49, 0x9d, 0x5f, 0x25, 0xb1, 0x26, 0x8c, 0x87, - 0x6b, 0x1d, 0x9d, 0x26, 0x30, 0x48, 0x8a, 0x52, 0xed, 0x6c, 0xa2, 0xd6, 0xae, 0x41, 0x8a, 0xf3, - 0x17, 0x9c, 0xf3, 0x49, 0xe4, 0x47, 0x2d, 0x75, 0x21, 0x9b, 0x4a, 0x5a, 0xec, 0x6a, 0x7e, 0x5c, - 0xad, 0x4a, 0x59, 0x55, 0xb2, 0xc2, 0x6a, 0xfa, 0xe2, 0x4c, 0x8c, 0x3f, 0x21, 0x38, 0x6d, 0xe6, - 0xbe, 0x92, 0x66, 0xdb, 0x5a, 0x26, 0x99, 0x6d, 0x7e, 0x5d, 0x8b, 0xd9, 0x00, 0xc8, 0x1c, 0xa3, - 0x0e, 0x05, 0x99, 0xb3, 0x08, 0xfe, 0x1f, 0xfd, 0x17, 0x75, 0xfe, 0x22, 0x0f, 0x2e, 0x96, 0x87, - 0x82, 0x6b, 0xfa, 0xd2, 0x9d, 0x5e, 0xb9, 0xe7, 0x3e, 0x3e, 0xa6, 0xfb, 0x9f, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xa1, 0x51, 0x2e, 0xba, 0x58, 0x02, 0x00, 0x00, +var File_bchain_tx_proto protoreflect.FileDescriptor + +var file_bchain_tx_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x62, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x74, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x06, 0x62, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x22, 0xd1, 0x04, 0x0a, 0x10, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x54, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x54, 0x78, + 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x48, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x03, 0x48, 0x65, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x69, + 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x6b, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x4c, 0x6f, 0x63, 0x6b, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, + 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x32, 0x0a, 0x03, 0x56, 0x69, 0x6e, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x62, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x56, 0x69, + 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x03, 0x56, 0x69, 0x6e, 0x12, 0x35, 0x0a, 0x04, 0x56, 0x6f, + 0x75, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x56, 0x6f, 0x75, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x56, 0x6f, 0x75, + 0x74, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x56, + 0x53, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x56, 0x53, 0x69, 0x7a, + 0x65, 0x1a, 0xab, 0x01, 0x0a, 0x07, 0x56, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x78, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x54, 0x78, 0x69, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x56, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x56, 0x6f, 0x75, + 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x69, 0x67, 0x48, 0x65, + 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, + 0x69, 0x67, 0x48, 0x65, 0x78, 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, + 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x1a, + 0x7c, 0x0a, 0x08, 0x56, 0x6f, 0x75, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x53, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x53, 0x61, 0x74, 0x12, 0x0c, 0x0a, 0x01, 0x4e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x01, 0x4e, 0x12, 0x28, 0x0a, 0x0f, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, + 0x75, 0x62, 0x4b, 0x65, 0x79, 0x48, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x48, 0x65, 0x78, 0x12, + 0x1c, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x09, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x42, 0x09, 0x5a, + 0x07, 0x62, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_bchain_tx_proto_rawDescOnce sync.Once + file_bchain_tx_proto_rawDescData = file_bchain_tx_proto_rawDesc +) + +func file_bchain_tx_proto_rawDescGZIP() []byte { + file_bchain_tx_proto_rawDescOnce.Do(func() { + file_bchain_tx_proto_rawDescData = protoimpl.X.CompressGZIP(file_bchain_tx_proto_rawDescData) + }) + return file_bchain_tx_proto_rawDescData +} + +var file_bchain_tx_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_bchain_tx_proto_goTypes = []interface{}{ + (*ProtoTransaction)(nil), // 0: bchain.ProtoTransaction + (*ProtoTransaction_VinType)(nil), // 1: bchain.ProtoTransaction.VinType + (*ProtoTransaction_VoutType)(nil), // 2: bchain.ProtoTransaction.VoutType +} +var file_bchain_tx_proto_depIdxs = []int32{ + 1, // 0: bchain.ProtoTransaction.Vin:type_name -> bchain.ProtoTransaction.VinType + 2, // 1: bchain.ProtoTransaction.Vout:type_name -> bchain.ProtoTransaction.VoutType + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_bchain_tx_proto_init() } +func file_bchain_tx_proto_init() { + if File_bchain_tx_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_bchain_tx_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProtoTransaction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_bchain_tx_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProtoTransaction_VinType); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_bchain_tx_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProtoTransaction_VoutType); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_bchain_tx_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_bchain_tx_proto_goTypes, + DependencyIndexes: file_bchain_tx_proto_depIdxs, + MessageInfos: file_bchain_tx_proto_msgTypes, + }.Build() + File_bchain_tx_proto = out.File + file_bchain_tx_proto_rawDesc = nil + file_bchain_tx_proto_goTypes = nil + file_bchain_tx_proto_depIdxs = nil } diff --git a/bchain/tx.proto b/bchain/tx.proto index cd5c7bc559..d64e844583 100644 --- a/bchain/tx.proto +++ b/bchain/tx.proto @@ -1,6 +1,7 @@ syntax = "proto3"; package bchain; - + option go_package = "bchain/"; + message ProtoTransaction { message VinType { string Coinbase = 1; @@ -24,4 +25,5 @@ syntax = "proto3"; repeated VinType Vin = 6; repeated VoutType Vout = 7; int32 Version = 8; + int64 VSize = 9; } \ No newline at end of file diff --git a/bchain/types.go b/bchain/types.go index 713b30be60..2faba03212 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -82,6 +82,7 @@ type Tx struct { Txid string `json:"txid"` Version int32 `json:"version"` LockTime uint32 `json:"locktime"` + VSize int64 `json:"vsize,omitempty"` Vin []Vin `json:"vin"` Vout []Vout `json:"vout"` BlockHeight uint32 `json:"blockHeight,omitempty"` @@ -331,6 +332,8 @@ type BlockChainParser interface { UseAddressAliases() bool // MinimumCoinbaseConfirmations returns minimum number of confirmations a coinbase transaction must have before it can be spent MinimumCoinbaseConfirmations() int + // SupportsVSize returns true if vsize of a transaction should be computed and returned by API + SupportsVSize() bool // AmountToDecimalString converts amount in big.Int to string with decimal point in the correct place AmountToDecimalString(a *big.Int) string // AmountToBigInt converts amount in common.JSONNumber (string) to big.Int diff --git a/go.mod b/go.mod index 33b7b51d24..7634166d9a 100644 --- a/go.mod +++ b/go.mod @@ -19,9 +19,8 @@ require ( github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect github.com/flier/gorocksdb v0.0.0-20210322035443-567cc51a1652 - github.com/gogo/protobuf v1.3.2 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b - github.com/golang/protobuf v1.4.3 + github.com/golang/protobuf v1.5.0 github.com/gorilla/websocket v1.4.2 github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect @@ -37,6 +36,7 @@ require ( github.com/prometheus/client_golang v1.8.0 github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 + google.golang.org/protobuf v1.26.0-rc.1 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect ) @@ -66,7 +66,6 @@ require ( github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 // indirect - google.golang.org/protobuf v1.23.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect ) diff --git a/go.sum b/go.sum index 458f52bd7a..eaadd1a8a1 100644 --- a/go.sum +++ b/go.sum @@ -237,8 +237,6 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= @@ -258,8 +256,9 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -273,8 +272,9 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -378,7 +378,6 @@ github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= @@ -619,7 +618,6 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= @@ -677,7 +675,6 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -701,7 +698,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -823,8 +819,6 @@ golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -881,8 +875,9 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/static/templates/tx.html b/static/templates/tx.html index 6a260a793b..f139cba1c8 100644 --- a/static/templates/tx.html +++ b/static/templates/tx.html @@ -59,6 +59,12 @@

Summary

Total Output {{formatAmount $tx.ValueOutSat}} {{$cs}} + {{- if $tx.Size -}} + + Size/VSize + {{$tx.Size}}/{{$tx.VSize}} + + {{- end -}} {{- end -}} {{- if $tx.FeesSat -}} diff --git a/tests/rpc/testdata/bitcoin.json b/tests/rpc/testdata/bitcoin.json index ae426860d1..e4b840837b 100644 --- a/tests/rpc/testdata/bitcoin.json +++ b/tests/rpc/testdata/bitcoin.json @@ -1,195 +1,197 @@ { - "blockHeight": 529150, - "blockHash": "00000000000000000035835503f43c878ebb643f3b40bdfd0dfda760da74e73c", - "blockTime": 1529915213, - "blockTxs": [ - "8dd1379174e262d12a32d217e87a7caf09fa1b9e48a6fe010cac219f18c6de58", - "5fce44793b328ca5f142caadbf29efc78a0059d7a6379dff81fc6447b519a7c3", - "d5daab5d57ef089b0464932443bb52a818860e93c6a23d9a66e0749e0cc146da", - "96bf6e66ed65e6003b1c751a51ad6a4fa17465c73b64211989ebd50413a9cdc9", - "a81addeae3cddf2cb69a70f4dd85e2829bacc474b98a97ece6a7872cd15c37fe", - "dd6169e9227bc00e2f2bddf4f1eef6126998d984ce13083d7a11c6972ec6d25d", - "ca211af71c54c3d90b83851c1d35a73669040b82742dd7f95e39953b032f7d39", - "0d1463f05662ef6fa73f37c030908b8d890b8dabe217c25ebaf07057ecafafca", - "2276c68760b3ff3d32cf4ede7e4eb4be95b01d04629145a16bc57830b33fbc01", - "3abc8d3485a7505087997d63a72bb86d3cfce1b6b0057da722e2ae24715d8be5", - "88db71956b653875a06a84bbe6ef166df3b50c94a908f82d457192835be47c07", - "973e394c11a4339b25803eff85e9299688489c7e71b9110e77a6c469f996f9b4", - "7afbc04c39707ca334a6db6b94ec2421798770d6593b4ce1f19f64f6a6ae77b2", - "2a047219a5858c5a0068822b81775789ee07cdf0cdc91a2775dd2d520f390aa2", - "2ea3eebcdb11b46f0e5b42b7718eb1fb709b625bec98bee5e3bb7de32d456360", - "d37ee9bebeedb5aac15ece6e5d497371a43b4e93a060a92006530eb77d6fbd8a", - "a029561cbffc0b79bdc23fa987e463b16342aef976562ce13c213fa556d860ec", - "8f0e21edeff8b6654338c67b4e6980d82634c0d10509923afcab7a831a46683c", - "6884c257e43d4c7a81739acf852952b45d5bfb5fc07816fca326b94b174733c3", - "0206990ff4387238953164731fc7b3d216432e58db21a180bfd3eeeb8bf3e36a", - "f05d76253daaa0584feeac9b1ff7e57e8962dfa0731a8875a52871b201ce3bc5", - "65149c5de02e58416b7f923c0db4f1b3a945afbd8bd815beefc052e14bd7ac7d", - "e732c8fb6a2da39b22a39207d84ac0bd7450ced28617a47cc6b5b1166b9a74ec", - "24f4a06a88f234b63935ef74a7e42223ec8ab22689d497b2a29aee526dbdbb3e", - "6066e3885dd13f8ffdcb0d1a849a45bb79e0a2d0c140f7a45e957f9e9c1d7d39", - "43b3fa3c6e857df0a52ddc3d2e7fdb0c6593b1cda65251b5a4eef06bced31883", - "86c5b32ee229a59339caca990a364490fb9e2c7e5493810982849cb1d8e13f1a", - "95c9ea5d7c79cba2fd3ae868f1a73fe242ca3917ceef7027e9251b4d666fc43d", - "655296c13411a8973e97cab768f560f0dd297994c0189e9ca286adf4ec04393e", - "2cf86550a3a2497009b296e451cb94e58b713f3c2859e273fe2e4e160784672d", - "e89effee8a2787007b49d2fcdc880c66b2c1dc76bbf258e25f4dc07e8b362a8f", - "191f2d7ef3f7c2693a9930f4fcfa80769139fb7e10289dde71caa9102a329c39", - "6d5cf483c0013281a267bfa1d69fbe0a372c93b51eafc6c3b1ed2bb880b420e5", - "932684b0ce065488c8da5bc92b3a0c082971474ca48957c6c5c59bd20c37f285", - "33666f78256725d68a15b8e52c9c84ab6dbd7f74da474eb92e0909b3ff0f1c73", - "6e1df5ec9851f403b994d6a53e5aa8912838c9b775d250cdf31fcda0c2f1f4a4", - "ce87eccf8a294e2e0110d56a6ec1e5a4f854aea74a5bf7e7b00f9dd32ecb9341", - "820ed342a2e62613173c365b6ef9b35e4956d46a9f310fe4c4228488bf981a1e", - "4bd73f5b3e2f833e2922a62816d7fac391e7ecc3f628c7f15964efb64c4868fc", - "c3f2fab2af1efe1d8725497567cd6c791372ce3c71a2358e0f2ca175bdeaf9ca", - "aa4498ad03c15068515b21df561560fbbfc56d1bc05e8f396d2d22e023cfd19f", - "956e313eeb2a3364b6439d23338ea2e48a98ab04b9c82e2776d844c6247b4b77", - "22787283751d501687c42396110a59a390b45cc05ccf57493e523e2d66bfff31", - "a5a1c30d7b2e01e0b26179c74da3ecc1b68265583184fd548f5059d312ae3414", - "62ecf5ec79801ed7e4012ca8037d8deedc95e5c30ca7ec112a90d4ec5506eb6b", - "1898e3f94a7d68c73e828a0c87a74ff6f172f2b24240858229e63d603616fb21", - "cee2b19dc3021ecfb8a1f68060757d828088387708571c6a224e89a1ed9c14e6", - "930d785b0fa219930886cd5e93bab9f1f2111c67a5c089ee329745b2678c841d", - "7ad9179b7d990637f905ae3eb74b65792a3a7440aa7f59ba5274e211b26f649b", - "3e1ee4ddd2a990fc3195117005b4c53c9367316e85813ee437c43e927af08155", - "76cd3f44f4186757b0f2b83b66dfa02a85012f93a25d2ad670f30b97382adbb7", - "2e48b382fb84ac83f34e8c37baf89e6a87836b6eaf2a9180dcfd03d2816b4aee", - "dea813a8a0f702ba2800dd7046b138e81568edb089d26d93ee7a979225690e91", - "512c4c97ad1763bae3c0998d154e0e32897da633bc1558963d75dc57de164c8c", - "39d34eaec9b399df80b3d05b4b211f1b7b220578d13c9666a63f202afa8857f3", - "a79823ca003b64d78ccf28fad693eebba7e94c3089340c202199a459bc50dcbb", - "faef230df22406f367ba2e838b16eca0b581249a4476acc1a978613816dbec02", - "d99db7838ccb76c0ecfaa57b2c690cec56a711a5d880fe4e54853256bf213079", - "115122280ce9b781014584968e4b3e851e37141d81ebd403179c48c908672e92", - "7e08d9ec508fbaa69163e5aff05baf0e57af7503e11ac0dfe5dfeaf7b5f223c5", - "55c019b5aa708fe6c35e188db3f9561ff552ddb7edd142e3b4ca3743ee5ac6da", - "dff909d9469761d365083d1ba2e8702879d3455b03f09d98bbf3eee43ed155f6", - "81ce5eef1fcf57787da3c9deb75e715ba4039ea3c4a48cf5ed5206b889c3bc27", - "4d5d6fb00364fc40880f2912a28f1a6db506061dc85820d19af84ec31b5e5e60", - "4e9d2f8ff1b2e603d31449373d3b8a312661d6405e4be0aa2c679fecd486df7f", - "f7eb6eb8a3b5699956581c3e68b08f05da818e7bb1e2c28276cd638cacda7994", - "8b485e079126145df5704a813662e08f748fad098ea8d9b6b896879625724392", - "f7f05d889261ecc26968dbe03051e113584df5ca0c4bcff9e27664413c2e73d6", - "b4c531ea13369004393b5fc6835f5ac74bdc2f23adc5ad8e44a34256867496e2", - "3f63d00841e688e7006c7dff76c04a8caf183db36e00fb130b2a9bf64fad4cc3", - "edec5964af29256872caff91a8b74308346a10eaa1d17e5eeba4f5f0a4da8109", - "0d5db84f971ebdacae606c5f7c55a1d309ce6b45c5c1cab4097007047af4639b", - "2e438cad664dd495a7aaec45622880e3255f0822583b3ab5f7921800f49f962f", - "bb1c7b6b4c921283ac7f3683226d913824050bbb2150d58dae47c0dc541541a8", - "65d7aeaff3c481baca79cb2f3ca98026214258ca35bc78d4fa19aac8b01403ba", - "2ca7b1392be38314e4faffd4dd586327f27320fc7eeaaa8389f29a56a6e2d6ed", - "63da0eb29ad6c2523348f3bf97f78bd76124cd668a91b499553ac30aad7667d1", - "3f53be6d1ea2147f0c125da925b0bf95eadc74091e99fd9b79b562c507699446", - "023359558fa22bfa518f82e6a261079cce8b59b1cbe286d11ec741dae72a8d30", - "44d69736e6bef4885b6a2442ea4644da3c56586bc6cbabc984a23ce4a266cf54", - "e735844e2f88551f8da8a69171a05fda47fbdaa4ecd3d29b98eb167789f7a3ac", - "60428a78761e84826168626da18e7407780bd1188c13e6d4f12aaa67b632e0bb", - "ada31e8d2c0670ed091a0f75008ea3f0c37fc000f704c4b60575a69a0fda96c1", - "bc31227b15cf5ddcd5035c220dc9a37c39213dbaa97e536eb36339251750e202", - "90d51a46469bd8185245760ed25425dca259ffb1bda812c06bfabbf99dd54bf7", - "c46129d75b7ec2d85d166f1347a4c47c461d3dcd823a1f9b6e25ef41bd04d6a2", - "1ffe0fabb57af65df1dd8cf433298118deb9b10f7b91036e4accb94275d66801", - "6a7250ede081a5df65c263bef36954027cf4956e920f4dc8a143a9dc735f7470", - "f6a768252ef749a1ebc5a7240eae4367d1a0d1fbb7659839ed994c261c887ac6", - "01899428b0238563ec0e219bd96221aa9c2c24fd0287c174e147d5bab2a72ce2", - "d998a18380d0ff7a772e32b6a9d92236c0634965ae5f51a0a18e9136320554ef", - "85e95bada40bbf15df5929c8f052f6dc1eb12322c321441fa5aa009ac40f19d2", - "1b0dee271db263e03a7da56364773e89b1ba2153e7ccf0d4c1c80797eef8478c", - "19c80c35d5322c290c0ac68bd80507b826b17b6a9fca6f14ba5d054b573fb5e4", - "f9a4ccfad5e6c49c75d254d3bdb196c37731b99e99315551b6de111da09fb36e", - "edb417ef42cccd22f1e6f6fc6dc66c2c1217a155fab18c1ceb494f33c6e03c5d", - "75c25c7fd1f06d5d63e3265c3eb35c0e05aedaeac7e5b085285ddfab34f0a83d", - "92b420a8508fed0b94227f331aec6d6593444ff889724197973c89df609f504b", - "f9305e5fcbe1a303702388aaffb9a2cfd1b246a1db77bbd393b48a0276b54d54", - "f5eb58e2dc2fb8afb00508d1c407453f1e36a7a8cfa2e67006aad545a8558f63", - "67a7f5340f42fe61534716aa6b5fff1e04e31afb355b0431e2f61363ceef9095", - "4f1588d77167028e76db46ce3d0abb8722e02a8b1900a73d7bc0e1fef6c841be", - "456d721f9ea96d2cd7e60cec26eb294e897427bffa6a4c3bb25acfdc086173bf", - "eb68c506de42f1b59ec0ab7273872f78b0eee5d2ef57a09ece87b37c342fecda", - "0ffa190c414ffbb75a50853e9167f10778aca7fd7ac10d6b7178708812e143d2", - "c29a75edd34b55322b79c38a1a44be6e43e737539da389000b4d8b6e00d53c38", - "858b16eed193442d2ed01fa8687a5052d45ae27ef0fe285252bc7e734879497f", - "5bb96e0791ac797516e5fec6b70b469b1d1fd05bdd58c7d212b6a77af9bdff84", - "f141f208343ddeeb9da8797a2e15850979e71a1bbe69b225652fedb00c5e4987", - "a206757d3d27493d8ce80ed84ae907d283f7db7bf057aaccc182a15809b847b5" - ], - "txDetails": { - "ca211af71c54c3d90b83851c1d35a73669040b82742dd7f95e39953b032f7d39": { - "hex": "01000000014ce1dd2c07c07524ed102b5bf67d9eb601f65ccd848952042ed538c7bcf5ef830b0000006b483045022100f0beea3fada8a71b7dba04357112474e089bc1bd6726b520065a3ba244dc0dcc02200126f8cbbec0c21ea8fed38481391a4df43603c89736cbdc007e5280100f5fd401210242b47391c5b851486b7113ce30cbf60c45a8e8d2a6f7145a972100015e690a25ffffffff02d0b3fb02000000001976a914d39c85c954ae3002137fe718c2af835175352b5f88ac141b0000000000001976a914198ec3f7a57bc6a1dc929dc68464149108e272bf88ac00000000", - "txid": "ca211af71c54c3d90b83851c1d35a73669040b82742dd7f95e39953b032f7d39", - "blocktime": 1529915213, - "time": 1529915213, - "locktime": 0, - "version": 1, - "vin": [ - { - "txid": "83eff5bcc738d52e04528984cd5cf601b69e7df65b2b10ed2475c0072cdde14c", - "vout": 11, - "sequence": 4294967295, - "scriptSig": { - "hex": "483045022100f0beea3fada8a71b7dba04357112474e089bc1bd6726b520065a3ba244dc0dcc02200126f8cbbec0c21ea8fed38481391a4df43603c89736cbdc007e5280100f5fd401210242b47391c5b851486b7113ce30cbf60c45a8e8d2a6f7145a972100015e690a25" - } - } - ], - "vout": [ - { - "value": 0.50050000, - "n": 0, - "scriptPubKey": { - "hex": "76a914d39c85c954ae3002137fe718c2af835175352b5f88ac" - } - }, - { - "value": 0.00006932, - "n": 1, - "scriptPubKey": { - "hex": "76a914198ec3f7a57bc6a1dc929dc68464149108e272bf88ac" - } - } - ] + "blockHeight": 529150, + "blockHash": "00000000000000000035835503f43c878ebb643f3b40bdfd0dfda760da74e73c", + "blockTime": 1529915213, + "blockTxs": [ + "8dd1379174e262d12a32d217e87a7caf09fa1b9e48a6fe010cac219f18c6de58", + "5fce44793b328ca5f142caadbf29efc78a0059d7a6379dff81fc6447b519a7c3", + "d5daab5d57ef089b0464932443bb52a818860e93c6a23d9a66e0749e0cc146da", + "96bf6e66ed65e6003b1c751a51ad6a4fa17465c73b64211989ebd50413a9cdc9", + "a81addeae3cddf2cb69a70f4dd85e2829bacc474b98a97ece6a7872cd15c37fe", + "dd6169e9227bc00e2f2bddf4f1eef6126998d984ce13083d7a11c6972ec6d25d", + "ca211af71c54c3d90b83851c1d35a73669040b82742dd7f95e39953b032f7d39", + "0d1463f05662ef6fa73f37c030908b8d890b8dabe217c25ebaf07057ecafafca", + "2276c68760b3ff3d32cf4ede7e4eb4be95b01d04629145a16bc57830b33fbc01", + "3abc8d3485a7505087997d63a72bb86d3cfce1b6b0057da722e2ae24715d8be5", + "88db71956b653875a06a84bbe6ef166df3b50c94a908f82d457192835be47c07", + "973e394c11a4339b25803eff85e9299688489c7e71b9110e77a6c469f996f9b4", + "7afbc04c39707ca334a6db6b94ec2421798770d6593b4ce1f19f64f6a6ae77b2", + "2a047219a5858c5a0068822b81775789ee07cdf0cdc91a2775dd2d520f390aa2", + "2ea3eebcdb11b46f0e5b42b7718eb1fb709b625bec98bee5e3bb7de32d456360", + "d37ee9bebeedb5aac15ece6e5d497371a43b4e93a060a92006530eb77d6fbd8a", + "a029561cbffc0b79bdc23fa987e463b16342aef976562ce13c213fa556d860ec", + "8f0e21edeff8b6654338c67b4e6980d82634c0d10509923afcab7a831a46683c", + "6884c257e43d4c7a81739acf852952b45d5bfb5fc07816fca326b94b174733c3", + "0206990ff4387238953164731fc7b3d216432e58db21a180bfd3eeeb8bf3e36a", + "f05d76253daaa0584feeac9b1ff7e57e8962dfa0731a8875a52871b201ce3bc5", + "65149c5de02e58416b7f923c0db4f1b3a945afbd8bd815beefc052e14bd7ac7d", + "e732c8fb6a2da39b22a39207d84ac0bd7450ced28617a47cc6b5b1166b9a74ec", + "24f4a06a88f234b63935ef74a7e42223ec8ab22689d497b2a29aee526dbdbb3e", + "6066e3885dd13f8ffdcb0d1a849a45bb79e0a2d0c140f7a45e957f9e9c1d7d39", + "43b3fa3c6e857df0a52ddc3d2e7fdb0c6593b1cda65251b5a4eef06bced31883", + "86c5b32ee229a59339caca990a364490fb9e2c7e5493810982849cb1d8e13f1a", + "95c9ea5d7c79cba2fd3ae868f1a73fe242ca3917ceef7027e9251b4d666fc43d", + "655296c13411a8973e97cab768f560f0dd297994c0189e9ca286adf4ec04393e", + "2cf86550a3a2497009b296e451cb94e58b713f3c2859e273fe2e4e160784672d", + "e89effee8a2787007b49d2fcdc880c66b2c1dc76bbf258e25f4dc07e8b362a8f", + "191f2d7ef3f7c2693a9930f4fcfa80769139fb7e10289dde71caa9102a329c39", + "6d5cf483c0013281a267bfa1d69fbe0a372c93b51eafc6c3b1ed2bb880b420e5", + "932684b0ce065488c8da5bc92b3a0c082971474ca48957c6c5c59bd20c37f285", + "33666f78256725d68a15b8e52c9c84ab6dbd7f74da474eb92e0909b3ff0f1c73", + "6e1df5ec9851f403b994d6a53e5aa8912838c9b775d250cdf31fcda0c2f1f4a4", + "ce87eccf8a294e2e0110d56a6ec1e5a4f854aea74a5bf7e7b00f9dd32ecb9341", + "820ed342a2e62613173c365b6ef9b35e4956d46a9f310fe4c4228488bf981a1e", + "4bd73f5b3e2f833e2922a62816d7fac391e7ecc3f628c7f15964efb64c4868fc", + "c3f2fab2af1efe1d8725497567cd6c791372ce3c71a2358e0f2ca175bdeaf9ca", + "aa4498ad03c15068515b21df561560fbbfc56d1bc05e8f396d2d22e023cfd19f", + "956e313eeb2a3364b6439d23338ea2e48a98ab04b9c82e2776d844c6247b4b77", + "22787283751d501687c42396110a59a390b45cc05ccf57493e523e2d66bfff31", + "a5a1c30d7b2e01e0b26179c74da3ecc1b68265583184fd548f5059d312ae3414", + "62ecf5ec79801ed7e4012ca8037d8deedc95e5c30ca7ec112a90d4ec5506eb6b", + "1898e3f94a7d68c73e828a0c87a74ff6f172f2b24240858229e63d603616fb21", + "cee2b19dc3021ecfb8a1f68060757d828088387708571c6a224e89a1ed9c14e6", + "930d785b0fa219930886cd5e93bab9f1f2111c67a5c089ee329745b2678c841d", + "7ad9179b7d990637f905ae3eb74b65792a3a7440aa7f59ba5274e211b26f649b", + "3e1ee4ddd2a990fc3195117005b4c53c9367316e85813ee437c43e927af08155", + "76cd3f44f4186757b0f2b83b66dfa02a85012f93a25d2ad670f30b97382adbb7", + "2e48b382fb84ac83f34e8c37baf89e6a87836b6eaf2a9180dcfd03d2816b4aee", + "dea813a8a0f702ba2800dd7046b138e81568edb089d26d93ee7a979225690e91", + "512c4c97ad1763bae3c0998d154e0e32897da633bc1558963d75dc57de164c8c", + "39d34eaec9b399df80b3d05b4b211f1b7b220578d13c9666a63f202afa8857f3", + "a79823ca003b64d78ccf28fad693eebba7e94c3089340c202199a459bc50dcbb", + "faef230df22406f367ba2e838b16eca0b581249a4476acc1a978613816dbec02", + "d99db7838ccb76c0ecfaa57b2c690cec56a711a5d880fe4e54853256bf213079", + "115122280ce9b781014584968e4b3e851e37141d81ebd403179c48c908672e92", + "7e08d9ec508fbaa69163e5aff05baf0e57af7503e11ac0dfe5dfeaf7b5f223c5", + "55c019b5aa708fe6c35e188db3f9561ff552ddb7edd142e3b4ca3743ee5ac6da", + "dff909d9469761d365083d1ba2e8702879d3455b03f09d98bbf3eee43ed155f6", + "81ce5eef1fcf57787da3c9deb75e715ba4039ea3c4a48cf5ed5206b889c3bc27", + "4d5d6fb00364fc40880f2912a28f1a6db506061dc85820d19af84ec31b5e5e60", + "4e9d2f8ff1b2e603d31449373d3b8a312661d6405e4be0aa2c679fecd486df7f", + "f7eb6eb8a3b5699956581c3e68b08f05da818e7bb1e2c28276cd638cacda7994", + "8b485e079126145df5704a813662e08f748fad098ea8d9b6b896879625724392", + "f7f05d889261ecc26968dbe03051e113584df5ca0c4bcff9e27664413c2e73d6", + "b4c531ea13369004393b5fc6835f5ac74bdc2f23adc5ad8e44a34256867496e2", + "3f63d00841e688e7006c7dff76c04a8caf183db36e00fb130b2a9bf64fad4cc3", + "edec5964af29256872caff91a8b74308346a10eaa1d17e5eeba4f5f0a4da8109", + "0d5db84f971ebdacae606c5f7c55a1d309ce6b45c5c1cab4097007047af4639b", + "2e438cad664dd495a7aaec45622880e3255f0822583b3ab5f7921800f49f962f", + "bb1c7b6b4c921283ac7f3683226d913824050bbb2150d58dae47c0dc541541a8", + "65d7aeaff3c481baca79cb2f3ca98026214258ca35bc78d4fa19aac8b01403ba", + "2ca7b1392be38314e4faffd4dd586327f27320fc7eeaaa8389f29a56a6e2d6ed", + "63da0eb29ad6c2523348f3bf97f78bd76124cd668a91b499553ac30aad7667d1", + "3f53be6d1ea2147f0c125da925b0bf95eadc74091e99fd9b79b562c507699446", + "023359558fa22bfa518f82e6a261079cce8b59b1cbe286d11ec741dae72a8d30", + "44d69736e6bef4885b6a2442ea4644da3c56586bc6cbabc984a23ce4a266cf54", + "e735844e2f88551f8da8a69171a05fda47fbdaa4ecd3d29b98eb167789f7a3ac", + "60428a78761e84826168626da18e7407780bd1188c13e6d4f12aaa67b632e0bb", + "ada31e8d2c0670ed091a0f75008ea3f0c37fc000f704c4b60575a69a0fda96c1", + "bc31227b15cf5ddcd5035c220dc9a37c39213dbaa97e536eb36339251750e202", + "90d51a46469bd8185245760ed25425dca259ffb1bda812c06bfabbf99dd54bf7", + "c46129d75b7ec2d85d166f1347a4c47c461d3dcd823a1f9b6e25ef41bd04d6a2", + "1ffe0fabb57af65df1dd8cf433298118deb9b10f7b91036e4accb94275d66801", + "6a7250ede081a5df65c263bef36954027cf4956e920f4dc8a143a9dc735f7470", + "f6a768252ef749a1ebc5a7240eae4367d1a0d1fbb7659839ed994c261c887ac6", + "01899428b0238563ec0e219bd96221aa9c2c24fd0287c174e147d5bab2a72ce2", + "d998a18380d0ff7a772e32b6a9d92236c0634965ae5f51a0a18e9136320554ef", + "85e95bada40bbf15df5929c8f052f6dc1eb12322c321441fa5aa009ac40f19d2", + "1b0dee271db263e03a7da56364773e89b1ba2153e7ccf0d4c1c80797eef8478c", + "19c80c35d5322c290c0ac68bd80507b826b17b6a9fca6f14ba5d054b573fb5e4", + "f9a4ccfad5e6c49c75d254d3bdb196c37731b99e99315551b6de111da09fb36e", + "edb417ef42cccd22f1e6f6fc6dc66c2c1217a155fab18c1ceb494f33c6e03c5d", + "75c25c7fd1f06d5d63e3265c3eb35c0e05aedaeac7e5b085285ddfab34f0a83d", + "92b420a8508fed0b94227f331aec6d6593444ff889724197973c89df609f504b", + "f9305e5fcbe1a303702388aaffb9a2cfd1b246a1db77bbd393b48a0276b54d54", + "f5eb58e2dc2fb8afb00508d1c407453f1e36a7a8cfa2e67006aad545a8558f63", + "67a7f5340f42fe61534716aa6b5fff1e04e31afb355b0431e2f61363ceef9095", + "4f1588d77167028e76db46ce3d0abb8722e02a8b1900a73d7bc0e1fef6c841be", + "456d721f9ea96d2cd7e60cec26eb294e897427bffa6a4c3bb25acfdc086173bf", + "eb68c506de42f1b59ec0ab7273872f78b0eee5d2ef57a09ece87b37c342fecda", + "0ffa190c414ffbb75a50853e9167f10778aca7fd7ac10d6b7178708812e143d2", + "c29a75edd34b55322b79c38a1a44be6e43e737539da389000b4d8b6e00d53c38", + "858b16eed193442d2ed01fa8687a5052d45ae27ef0fe285252bc7e734879497f", + "5bb96e0791ac797516e5fec6b70b469b1d1fd05bdd58c7d212b6a77af9bdff84", + "f141f208343ddeeb9da8797a2e15850979e71a1bbe69b225652fedb00c5e4987", + "a206757d3d27493d8ce80ed84ae907d283f7db7bf057aaccc182a15809b847b5" + ], + "txDetails": { + "ca211af71c54c3d90b83851c1d35a73669040b82742dd7f95e39953b032f7d39": { + "hex": "01000000014ce1dd2c07c07524ed102b5bf67d9eb601f65ccd848952042ed538c7bcf5ef830b0000006b483045022100f0beea3fada8a71b7dba04357112474e089bc1bd6726b520065a3ba244dc0dcc02200126f8cbbec0c21ea8fed38481391a4df43603c89736cbdc007e5280100f5fd401210242b47391c5b851486b7113ce30cbf60c45a8e8d2a6f7145a972100015e690a25ffffffff02d0b3fb02000000001976a914d39c85c954ae3002137fe718c2af835175352b5f88ac141b0000000000001976a914198ec3f7a57bc6a1dc929dc68464149108e272bf88ac00000000", + "txid": "ca211af71c54c3d90b83851c1d35a73669040b82742dd7f95e39953b032f7d39", + "blocktime": 1529915213, + "time": 1529915213, + "locktime": 0, + "vsize": 226, + "version": 1, + "vin": [ + { + "txid": "83eff5bcc738d52e04528984cd5cf601b69e7df65b2b10ed2475c0072cdde14c", + "vout": 11, + "sequence": 4294967295, + "scriptSig": { + "hex": "483045022100f0beea3fada8a71b7dba04357112474e089bc1bd6726b520065a3ba244dc0dcc02200126f8cbbec0c21ea8fed38481391a4df43603c89736cbdc007e5280100f5fd401210242b47391c5b851486b7113ce30cbf60c45a8e8d2a6f7145a972100015e690a25" + } + } + ], + "vout": [ + { + "value": 0.5005, + "n": 0, + "scriptPubKey": { + "hex": "76a914d39c85c954ae3002137fe718c2af835175352b5f88ac" + } + }, + { + "value": 0.00006932, + "n": 1, + "scriptPubKey": { + "hex": "76a914198ec3f7a57bc6a1dc929dc68464149108e272bf88ac" + } + } + ] + }, + "faef230df22406f367ba2e838b16eca0b581249a4476acc1a978613816dbec02": { + "hex": "0200000002a73d71157ae5f4372fe4624681bd72946a026e87a90cb2f307675146d5883941000000006a47304402202a6339b584730131f07c0c69ea40f08bc5c44cb161036509d2d0bef103c178c702206c0536316244acfdb0a27bf9a2ba4a6830b19ddc0fafd92027619dd4aa290bf1012102964e49b139cf408a30d4fc15e079789491689be74d63797e6bcbbe4191c8b691fefffffff9709ad0025e3968919c638559d00f8c8240b9b26a6624cc008548047e7af488010000006a47304402201cac13c2cbac8e536bc922462f810ffc086e8cf4a51a7f73d8d08aaf56372d6902206cc3518b6024d9b7c4f2f30e5dbb954f6d4d77e6b76eccc1081a8ed505892a7001210209fa85c88fb0b628305a169ca62a103c0da2cace04300810ae57755ef31caae9feffffff02ea3a0f00000000001976a9143e3fc495d359f2d346af7b42f70ddc7bb4981c1788ac40c06503000000001976a914f4274a0adee47dfab83664493bf252e3da0b5f5988acfd120800", + "txid": "faef230df22406f367ba2e838b16eca0b581249a4476acc1a978613816dbec02", + "blocktime": 1529915213, + "time": 1529915213, + "locktime": 529149, + "vsize": 372, + "version": 2, + "vin": [ + { + "txid": "413988d546516707f3b20ca9876e026a9472bd814662e42f37f4e57a15713da7", + "vout": 0, + "sequence": 4294967294, + "scriptSig": { + "hex": "47304402202a6339b584730131f07c0c69ea40f08bc5c44cb161036509d2d0bef103c178c702206c0536316244acfdb0a27bf9a2ba4a6830b19ddc0fafd92027619dd4aa290bf1012102964e49b139cf408a30d4fc15e079789491689be74d63797e6bcbbe4191c8b691" + } + }, + { + "txid": "88f47a7e04488500cc24666ab2b940828c0fd05985639c9168395e02d09a70f9", + "vout": 1, + "sequence": 4294967294, + "scriptSig": { + "hex": "47304402201cac13c2cbac8e536bc922462f810ffc086e8cf4a51a7f73d8d08aaf56372d6902206cc3518b6024d9b7c4f2f30e5dbb954f6d4d77e6b76eccc1081a8ed505892a7001210209fa85c88fb0b628305a169ca62a103c0da2cace04300810ae57755ef31caae9" + } + } + ], + "vout": [ + { + "value": 0.00998122, + "n": 0, + "scriptPubKey": { + "hex": "76a9143e3fc495d359f2d346af7b42f70ddc7bb4981c1788ac" + } }, - "faef230df22406f367ba2e838b16eca0b581249a4476acc1a978613816dbec02": { - "hex": "0200000002a73d71157ae5f4372fe4624681bd72946a026e87a90cb2f307675146d5883941000000006a47304402202a6339b584730131f07c0c69ea40f08bc5c44cb161036509d2d0bef103c178c702206c0536316244acfdb0a27bf9a2ba4a6830b19ddc0fafd92027619dd4aa290bf1012102964e49b139cf408a30d4fc15e079789491689be74d63797e6bcbbe4191c8b691fefffffff9709ad0025e3968919c638559d00f8c8240b9b26a6624cc008548047e7af488010000006a47304402201cac13c2cbac8e536bc922462f810ffc086e8cf4a51a7f73d8d08aaf56372d6902206cc3518b6024d9b7c4f2f30e5dbb954f6d4d77e6b76eccc1081a8ed505892a7001210209fa85c88fb0b628305a169ca62a103c0da2cace04300810ae57755ef31caae9feffffff02ea3a0f00000000001976a9143e3fc495d359f2d346af7b42f70ddc7bb4981c1788ac40c06503000000001976a914f4274a0adee47dfab83664493bf252e3da0b5f5988acfd120800", - "txid": "faef230df22406f367ba2e838b16eca0b581249a4476acc1a978613816dbec02", - "blocktime": 1529915213, - "time": 1529915213, - "locktime": 529149, - "version": 2, - "vin": [ - { - "txid": "413988d546516707f3b20ca9876e026a9472bd814662e42f37f4e57a15713da7", - "vout": 0, - "sequence": 4294967294, - "scriptSig": { - "hex": "47304402202a6339b584730131f07c0c69ea40f08bc5c44cb161036509d2d0bef103c178c702206c0536316244acfdb0a27bf9a2ba4a6830b19ddc0fafd92027619dd4aa290bf1012102964e49b139cf408a30d4fc15e079789491689be74d63797e6bcbbe4191c8b691" - } - }, - { - "txid": "88f47a7e04488500cc24666ab2b940828c0fd05985639c9168395e02d09a70f9", - "vout": 1, - "sequence": 4294967294, - "scriptSig": { - "hex": "47304402201cac13c2cbac8e536bc922462f810ffc086e8cf4a51a7f73d8d08aaf56372d6902206cc3518b6024d9b7c4f2f30e5dbb954f6d4d77e6b76eccc1081a8ed505892a7001210209fa85c88fb0b628305a169ca62a103c0da2cace04300810ae57755ef31caae9" - } - } - ], - "vout": [ - { - "value": 0.00998122, - "n": 0, - "scriptPubKey": { - "hex": "76a9143e3fc495d359f2d346af7b42f70ddc7bb4981c1788ac" - } - }, - { - "value": 0.57000000, - "n": 1, - "scriptPubKey": { - "hex": "76a914f4274a0adee47dfab83664493bf252e3da0b5f5988ac" - } - } - ] + { + "value": 0.57, + "n": 1, + "scriptPubKey": { + "hex": "76a914f4274a0adee47dfab83664493bf252e3da0b5f5988ac" + } } + ] } + } } diff --git a/tests/rpc/testdata/bitcoin_testnet.json b/tests/rpc/testdata/bitcoin_testnet.json index ed0cde1723..b72cf1e23e 100644 --- a/tests/rpc/testdata/bitcoin_testnet.json +++ b/tests/rpc/testdata/bitcoin_testnet.json @@ -1,126 +1,128 @@ { - "blockHeight": 1325168, - "blockHash": "000000000000004ed0834f3de922e66d024ec4da9fcc2da17be61369cb6dc041", - "blockTime": 1528788394, - "blockTxs": [ - "e1179f205aabbf48dc2ce4ebd9ed255571b0578e4de551f6574a50cb81120007", - "00a5aa2891d41af9eb1dc30c940f142a609ecab8f370eb0874ba7d32252d1b1b", - "1c519d80804dd17258cfc801bf2c875607956fc9f065a664f43e88d53f80af6f", - "b10c1e2f7c8a6b10fddf94260aff0f8a5f56e33c8d0de48c49a72eb8418c3f6e", - "ba85ca543b290deb84cde9c4ca53614dbe557a3dede5d0adb141f803f8e82f34", - "60dfc2c9cc184ae68ca9e540ab4393d9d2179d060e2ac290f29560c6a1360f51", - "3a40bca678653ae8f7f6d2771b571d5ace1a258056b99e3fd361a10f1016bc53", - "4d4e495f3329801d92c7e3dc9874a372576bf3548decf884ede388143980ecab", - "74ba4bee8d559e4d8b4859c086b0ea5f2c36bcabc95d8578e775f065f70943b8", - "32bcc281f081e172dcb40ad137564724bd9486095813b78990d1d986173ac3c6", - "b6e77c59f4a988731d9b8520e0f4971223e622946eb12e28cc2bab72f1e9c2f8", - "0bc8f39da5d5300a2728b45edb18c8219e94a8b27a2e8074f6c5c10a00d99788", - "8329b31d2a490d57980afcf5c7df4574ce57f952aef6f5aecb3b7786f5c9f255", - "e559c2fd0f4e8aebe28fcb6dbb099fc6ee92d726d74fda28522f52bc1490a470", - "3005378ee85fc905a1812bdfae4b2e0e9bb09f5867a53fd73237bb319a1774aa", - "ebea245b9e4d96fab65c938547a9b3ffd03659b92b8ae4fcdfe4ac9bc325c0a2", - "a2d5fe23b50253dda9941dd6c97c04853d58f048acf347acf9ccf549ee215b51", - "017c2ccec866850521db877c1c7f6d095b7df668f891cfaf70a5e14ce39d010c", - "d101d3467a831cc4dfc87bdd19d0ff5d01b8c872e47b2096eeeac3b44c2a258a", - "3884180bab62d0f0498a8ad012b0005aceec778a18a617e5392d99cee5f21869", - "a00200e57bed4fbd193c4cad49549d311282fee9a82956083353a2874f9bfd9f", - "d32ce7a9413111fb2e3578472d520eb1437db701f20256e3afd37b7c0a6d67e6", - "fed1df6d23a40e1a1f26820bbe35febb668aa2240902f1fd17b31a84dde6eb39", - "5bef621ad6d0970939ae36270a3228d3c315f8008fb04eebffab5f7a3589d114", - "411e7f3f4cae4125c8933403809771ebfcaa088f6ef773e5a412ead8639fb515", - "d53858bbbbde4518ea92abda93ac5d01e5122d420a468e6d076244edd99bcea1", - "b5fc4d963805b439d11f06b5d5d89ce3aac225e7145d1673d20d3d37a12c61dd", - "c8d7332377d4bf43c232bc7afc3d7e3aacf13523d1c8488f68f530e58e6cfd88", - "3c21a6b7e3810ca10efac45446cd2b7ef0c9848ac589be7375b61ea5aabbbea4", - "5a25c2b70e2194e05a6208c99343ebe0fad970dd19f3f9cca88aaf77ab9e4658", - "031e3c08ebdcafccf6dc5d7ff1161cd5314424d0a943d2c22a5a2109286e332d", - "4992d16008aa3050b3e2e4aab67e488eb338850ff1c348367ae3d089d8d67a52", - "beb3e71b8da7da7917228f5ce8a88afdc45836c421b053dde24d367865326bd7" - ], - "txDetails": { - "e559c2fd0f4e8aebe28fcb6dbb099fc6ee92d726d74fda28522f52bc1490a470": { - "hex": "01000000000102a4b8c14f271cfa77d5ecaed9c3026472a55ea6bca119e2ff7b04975326f5974001000000171600147edbcdda98080eeb6e8a63c63da135498295c3cdffffffffabcdf96b8ba187c24d70f98a2edfbf100821506212637f28b30a08efa970a4eb0100000017160014df7d60680e984aae4052e24bce8f17e4bfdcc532ffffffff0292fe1e00000000001976a914abba3808b854c70b63ff038fcddfbafcb707713988ac13e796110100000017a914c9e67d2b78a38857c786ea9a2fc3e64cb6e775648702483045022100a6910d3a3b64545a44e097a3739b1206095602fa796afc51f81b249d1293ad0a02206cdae51853b59ca52003f4e54ea8ae418b6b4d036cbe1fdd78677efe8eddb318012102b45e239d96f8504ae45a32af7c80f6164f7b9658166e318521ee822192fee3ef0247304402202684a6f59ee255f3f5e9a9209a735bf2aaa818d47add7c3f7a5590623bd2211c0220452bf2fb8d0dc0380862988f0f098c21e861004e02da7bd1fc6bea4e7f33d2dc012103f308867fda821467f77d372791644225174ae16daba86e55754c150a8d5aa40d00000000", - "txid": "e559c2fd0f4e8aebe28fcb6dbb099fc6ee92d726d74fda28522f52bc1490a470", - "blocktime": 1528788394, - "time": 1528788394, - "locktime": 0, - "version": 1, - "vin": [ - { - "txid": "4097f5265397047bffe219a1bca65ea5726402c3d9aeecd577fa1c274fc1b8a4", - "vout": 1, - "sequence": 4294967295, - "scriptSig": { - "hex": "1600147edbcdda98080eeb6e8a63c63da135498295c3cd" - } - }, - { - "txid": "eba470a9ef080ab3287f63126250210810bfdf2e8af9704dc287a18b6bf9cdab", - "vout": 1, - "sequence": 4294967295, - "scriptSig": { - "hex": "160014df7d60680e984aae4052e24bce8f17e4bfdcc532" - } - } - ], - "vout": [ - { - "value": 0.02031250, - "n": 0, - "scriptPubKey": { - "hex": "76a914abba3808b854c70b63ff038fcddfbafcb707713988ac" - } - }, - { - "value": 45.90069523, - "n": 1, - "scriptPubKey": { - "hex": "a914c9e67d2b78a38857c786ea9a2fc3e64cb6e7756487" - } - } - ] + "blockHeight": 1325168, + "blockHash": "000000000000004ed0834f3de922e66d024ec4da9fcc2da17be61369cb6dc041", + "blockTime": 1528788394, + "blockTxs": [ + "e1179f205aabbf48dc2ce4ebd9ed255571b0578e4de551f6574a50cb81120007", + "00a5aa2891d41af9eb1dc30c940f142a609ecab8f370eb0874ba7d32252d1b1b", + "1c519d80804dd17258cfc801bf2c875607956fc9f065a664f43e88d53f80af6f", + "b10c1e2f7c8a6b10fddf94260aff0f8a5f56e33c8d0de48c49a72eb8418c3f6e", + "ba85ca543b290deb84cde9c4ca53614dbe557a3dede5d0adb141f803f8e82f34", + "60dfc2c9cc184ae68ca9e540ab4393d9d2179d060e2ac290f29560c6a1360f51", + "3a40bca678653ae8f7f6d2771b571d5ace1a258056b99e3fd361a10f1016bc53", + "4d4e495f3329801d92c7e3dc9874a372576bf3548decf884ede388143980ecab", + "74ba4bee8d559e4d8b4859c086b0ea5f2c36bcabc95d8578e775f065f70943b8", + "32bcc281f081e172dcb40ad137564724bd9486095813b78990d1d986173ac3c6", + "b6e77c59f4a988731d9b8520e0f4971223e622946eb12e28cc2bab72f1e9c2f8", + "0bc8f39da5d5300a2728b45edb18c8219e94a8b27a2e8074f6c5c10a00d99788", + "8329b31d2a490d57980afcf5c7df4574ce57f952aef6f5aecb3b7786f5c9f255", + "e559c2fd0f4e8aebe28fcb6dbb099fc6ee92d726d74fda28522f52bc1490a470", + "3005378ee85fc905a1812bdfae4b2e0e9bb09f5867a53fd73237bb319a1774aa", + "ebea245b9e4d96fab65c938547a9b3ffd03659b92b8ae4fcdfe4ac9bc325c0a2", + "a2d5fe23b50253dda9941dd6c97c04853d58f048acf347acf9ccf549ee215b51", + "017c2ccec866850521db877c1c7f6d095b7df668f891cfaf70a5e14ce39d010c", + "d101d3467a831cc4dfc87bdd19d0ff5d01b8c872e47b2096eeeac3b44c2a258a", + "3884180bab62d0f0498a8ad012b0005aceec778a18a617e5392d99cee5f21869", + "a00200e57bed4fbd193c4cad49549d311282fee9a82956083353a2874f9bfd9f", + "d32ce7a9413111fb2e3578472d520eb1437db701f20256e3afd37b7c0a6d67e6", + "fed1df6d23a40e1a1f26820bbe35febb668aa2240902f1fd17b31a84dde6eb39", + "5bef621ad6d0970939ae36270a3228d3c315f8008fb04eebffab5f7a3589d114", + "411e7f3f4cae4125c8933403809771ebfcaa088f6ef773e5a412ead8639fb515", + "d53858bbbbde4518ea92abda93ac5d01e5122d420a468e6d076244edd99bcea1", + "b5fc4d963805b439d11f06b5d5d89ce3aac225e7145d1673d20d3d37a12c61dd", + "c8d7332377d4bf43c232bc7afc3d7e3aacf13523d1c8488f68f530e58e6cfd88", + "3c21a6b7e3810ca10efac45446cd2b7ef0c9848ac589be7375b61ea5aabbbea4", + "5a25c2b70e2194e05a6208c99343ebe0fad970dd19f3f9cca88aaf77ab9e4658", + "031e3c08ebdcafccf6dc5d7ff1161cd5314424d0a943d2c22a5a2109286e332d", + "4992d16008aa3050b3e2e4aab67e488eb338850ff1c348367ae3d089d8d67a52", + "beb3e71b8da7da7917228f5ce8a88afdc45836c421b053dde24d367865326bd7" + ], + "txDetails": { + "e559c2fd0f4e8aebe28fcb6dbb099fc6ee92d726d74fda28522f52bc1490a470": { + "hex": "01000000000102a4b8c14f271cfa77d5ecaed9c3026472a55ea6bca119e2ff7b04975326f5974001000000171600147edbcdda98080eeb6e8a63c63da135498295c3cdffffffffabcdf96b8ba187c24d70f98a2edfbf100821506212637f28b30a08efa970a4eb0100000017160014df7d60680e984aae4052e24bce8f17e4bfdcc532ffffffff0292fe1e00000000001976a914abba3808b854c70b63ff038fcddfbafcb707713988ac13e796110100000017a914c9e67d2b78a38857c786ea9a2fc3e64cb6e775648702483045022100a6910d3a3b64545a44e097a3739b1206095602fa796afc51f81b249d1293ad0a02206cdae51853b59ca52003f4e54ea8ae418b6b4d036cbe1fdd78677efe8eddb318012102b45e239d96f8504ae45a32af7c80f6164f7b9658166e318521ee822192fee3ef0247304402202684a6f59ee255f3f5e9a9209a735bf2aaa818d47add7c3f7a5590623bd2211c0220452bf2fb8d0dc0380862988f0f098c21e861004e02da7bd1fc6bea4e7f33d2dc012103f308867fda821467f77d372791644225174ae16daba86e55754c150a8d5aa40d00000000", + "txid": "e559c2fd0f4e8aebe28fcb6dbb099fc6ee92d726d74fda28522f52bc1490a470", + "blocktime": 1528788394, + "time": 1528788394, + "locktime": 0, + "vsize": 259, + "version": 1, + "vin": [ + { + "txid": "4097f5265397047bffe219a1bca65ea5726402c3d9aeecd577fa1c274fc1b8a4", + "vout": 1, + "sequence": 4294967295, + "scriptSig": { + "hex": "1600147edbcdda98080eeb6e8a63c63da135498295c3cd" + } }, - "3005378ee85fc905a1812bdfae4b2e0e9bb09f5867a53fd73237bb319a1774aa": { - "hex": "01000000000102c997f74e9ad52a44446302381e0fa6de080dadadf55842588bde1be8a47b438000000000171600147edbcdda98080eeb6e8a63c63da135498295c3cdffffffff563c9674a40bf1aa1063f767a50d2288146116fe869012ad3dad03d71e74a8800100000017160014af97d082fd5de049bce2991d9dcaa5d3035a1b04ffffffff0290f4f700000000001976a914abba3808b854c70b63ff038fcddfbafcb707713988ac74b77b030100000017a914fb1e0f36e2d8e91a43c7faba7dae18a610070c4a87024730440220538fcd8fbbf39b813372a7ff6251f1d22c9e940f54272ab525e1d1dc5f03049b022066cc5a1c445573e7e069fdaf3aa33d6665ef5f7936cb155cfd9093e888ca9461012102b45e239d96f8504ae45a32af7c80f6164f7b9658166e318521ee822192fee3ef0248304502210089e579ce52f765c8de6033e1cee93c94aa9a5ef3a194fec12885ed163dc60688022050aadd6aa170c6cfb58f406497949b0140327b4889ca7e082bdcfa8cc03d487f012103fb1a838f38d587dea0532f4a15a39b96e411cdb37c5160ba576a4cb0072db01900000000", - "txid": "3005378ee85fc905a1812bdfae4b2e0e9bb09f5867a53fd73237bb319a1774aa", - "blocktime": 1528788394, - "time": 1528788394, - "locktime": 0, - "version": 1, - "vin": [ - { - "txid": "80437ba4e81bde8b584258f5adad0d08dea60f1e38026344442ad59a4ef797c9", - "vout": 0, - "sequence": 4294967295, - "scriptSig": { - "hex": "1600147edbcdda98080eeb6e8a63c63da135498295c3cd" - } - }, - { - "txid": "80a8741ed703ad3dad129086fe16611488220da567f76310aaf10ba474963c56", - "vout": 1, - "sequence": 4294967295, - "scriptSig": { - "hex": "160014af97d082fd5de049bce2991d9dcaa5d3035a1b04" - } - } - ], - "vout": [ - { - "value": 0.16250000, - "n": 0, - "scriptPubKey": { - "hex": "76a914abba3808b854c70b63ff038fcddfbafcb707713988ac" - } - }, - { - "value": 43.53406836, - "n": 1, - "scriptPubKey": { - "hex": "a914fb1e0f36e2d8e91a43c7faba7dae18a610070c4a87" - } - } - ] + { + "txid": "eba470a9ef080ab3287f63126250210810bfdf2e8af9704dc287a18b6bf9cdab", + "vout": 1, + "sequence": 4294967295, + "scriptSig": { + "hex": "160014df7d60680e984aae4052e24bce8f17e4bfdcc532" + } } + ], + "vout": [ + { + "value": 0.0203125, + "n": 0, + "scriptPubKey": { + "hex": "76a914abba3808b854c70b63ff038fcddfbafcb707713988ac" + } + }, + { + "value": 45.90069523, + "n": 1, + "scriptPubKey": { + "hex": "a914c9e67d2b78a38857c786ea9a2fc3e64cb6e7756487" + } + } + ] + }, + "3005378ee85fc905a1812bdfae4b2e0e9bb09f5867a53fd73237bb319a1774aa": { + "hex": "01000000000102c997f74e9ad52a44446302381e0fa6de080dadadf55842588bde1be8a47b438000000000171600147edbcdda98080eeb6e8a63c63da135498295c3cdffffffff563c9674a40bf1aa1063f767a50d2288146116fe869012ad3dad03d71e74a8800100000017160014af97d082fd5de049bce2991d9dcaa5d3035a1b04ffffffff0290f4f700000000001976a914abba3808b854c70b63ff038fcddfbafcb707713988ac74b77b030100000017a914fb1e0f36e2d8e91a43c7faba7dae18a610070c4a87024730440220538fcd8fbbf39b813372a7ff6251f1d22c9e940f54272ab525e1d1dc5f03049b022066cc5a1c445573e7e069fdaf3aa33d6665ef5f7936cb155cfd9093e888ca9461012102b45e239d96f8504ae45a32af7c80f6164f7b9658166e318521ee822192fee3ef0248304502210089e579ce52f765c8de6033e1cee93c94aa9a5ef3a194fec12885ed163dc60688022050aadd6aa170c6cfb58f406497949b0140327b4889ca7e082bdcfa8cc03d487f012103fb1a838f38d587dea0532f4a15a39b96e411cdb37c5160ba576a4cb0072db01900000000", + "txid": "3005378ee85fc905a1812bdfae4b2e0e9bb09f5867a53fd73237bb319a1774aa", + "blocktime": 1528788394, + "time": 1528788394, + "locktime": 0, + "vsize": 259, + "version": 1, + "vin": [ + { + "txid": "80437ba4e81bde8b584258f5adad0d08dea60f1e38026344442ad59a4ef797c9", + "vout": 0, + "sequence": 4294967295, + "scriptSig": { + "hex": "1600147edbcdda98080eeb6e8a63c63da135498295c3cd" + } + }, + { + "txid": "80a8741ed703ad3dad129086fe16611488220da567f76310aaf10ba474963c56", + "vout": 1, + "sequence": 4294967295, + "scriptSig": { + "hex": "160014af97d082fd5de049bce2991d9dcaa5d3035a1b04" + } + } + ], + "vout": [ + { + "value": 0.1625, + "n": 0, + "scriptPubKey": { + "hex": "76a914abba3808b854c70b63ff038fcddfbafcb707713988ac" + } + }, + { + "value": 43.53406836, + "n": 1, + "scriptPubKey": { + "hex": "a914fb1e0f36e2d8e91a43c7faba7dae18a610070c4a87" + } + } + ] } + } } diff --git a/tests/rpc/testdata/digibyte.json b/tests/rpc/testdata/digibyte.json index 96abd9f02f..edf8b12bf9 100644 --- a/tests/rpc/testdata/digibyte.json +++ b/tests/rpc/testdata/digibyte.json @@ -1,53 +1,54 @@ { - "blockHeight": 7000000, - "blockHash": "03c6664b250c3e3b688f5779ce791384b35acaa38c4461f0458a4674bd762f63", - "blockTime": 1532239864, - "blockTxs": [ - "759433255ce45b4bbc7a961767a14b383753ffa197e8228c29c16d7cf0008766", - "d4fe2eea4e62b3705ac5fda29deeaaa4d2dea446077e4153ae84398c3d62ccb4" - ], - "txDetails": { - "d4fe2eea4e62b3705ac5fda29deeaaa4d2dea446077e4153ae84398c3d62ccb4": { - "hex": "010000000203326d7375759a5863ea2cfb324a6c4201425dfd336a508900a804c22a51e125000000006a47304402200632ff38dc836b95e81f2022d2e39222c4261e85e64d909f53969d94c0febc9c022065000e5660744b699e9fb6f28a0a46e06b3870a2fe382f80c867e1ee3606056401210392091d4341e4b15692234ead7d374c4fa5dc636c30f0042526366ea70f5dc889feffffff3e2ed13beb3d3f34b0d9593c11c7d8e74e43bfe504426aea11626a09bda0fd9d000000006b4830450221009077f18dcbe30dda597f8a503414d443cb6e8ad45948df6d320e1fda9b1fd0b40220603dac961f16b86e9c65ab8c127f696192ffb6856705a4913c0c92c2d9f5a0f201210392091d4341e4b15692234ead7d374c4fa5dc636c30f0042526366ea70f5dc889feffffff02cd67b551000000001976a914035d5bd3df669b4a50839d004d4301d0edfa793c88acb7984525230000001976a9140f165c712e18717410e5ef952529dc470e34b73b88ac8ccf6a00", - "txid": "d4fe2eea4e62b3705ac5fda29deeaaa4d2dea446077e4153ae84398c3d62ccb4", - "blocktime": 1532239864, - "time": 1532239864, - "locktime": 6999948, - "version": 1, - "vin": [ - { - "txid": "25e1512ac204a80089506a33fd5d4201426c4a32fb2cea63589a7575736d3203", - "vout": 0, - "scriptSig": { - "hex": "47304402200632ff38dc836b95e81f2022d2e39222c4261e85e64d909f53969d94c0febc9c022065000e5660744b699e9fb6f28a0a46e06b3870a2fe382f80c867e1ee3606056401210392091d4341e4b15692234ead7d374c4fa5dc636c30f0042526366ea70f5dc889" - }, - "sequence": 4294967294 - }, - { - "txid": "9dfda0bd096a6211ea6a4204e5bf434ee7d8c7113c59d9b0343f3deb3bd12e3e", - "vout": 0, - "scriptSig": { - "hex": "4830450221009077f18dcbe30dda597f8a503414d443cb6e8ad45948df6d320e1fda9b1fd0b40220603dac961f16b86e9c65ab8c127f696192ffb6856705a4913c0c92c2d9f5a0f201210392091d4341e4b15692234ead7d374c4fa5dc636c30f0042526366ea70f5dc889" - }, - "sequence": 4294967294 - } - ], - "vout": [ - { - "value": 13.70843085, - "n": 0, - "scriptPubKey": { - "hex": "76a914035d5bd3df669b4a50839d004d4301d0edfa793c88ac" - } - }, - { - "value": 1509.49173431, - "n": 1, - "scriptPubKey": { - "hex": "76a9140f165c712e18717410e5ef952529dc470e34b73b88ac" - } - } - ] + "blockHeight": 7000000, + "blockHash": "03c6664b250c3e3b688f5779ce791384b35acaa38c4461f0458a4674bd762f63", + "blockTime": 1532239864, + "blockTxs": [ + "759433255ce45b4bbc7a961767a14b383753ffa197e8228c29c16d7cf0008766", + "d4fe2eea4e62b3705ac5fda29deeaaa4d2dea446077e4153ae84398c3d62ccb4" + ], + "txDetails": { + "d4fe2eea4e62b3705ac5fda29deeaaa4d2dea446077e4153ae84398c3d62ccb4": { + "hex": "010000000203326d7375759a5863ea2cfb324a6c4201425dfd336a508900a804c22a51e125000000006a47304402200632ff38dc836b95e81f2022d2e39222c4261e85e64d909f53969d94c0febc9c022065000e5660744b699e9fb6f28a0a46e06b3870a2fe382f80c867e1ee3606056401210392091d4341e4b15692234ead7d374c4fa5dc636c30f0042526366ea70f5dc889feffffff3e2ed13beb3d3f34b0d9593c11c7d8e74e43bfe504426aea11626a09bda0fd9d000000006b4830450221009077f18dcbe30dda597f8a503414d443cb6e8ad45948df6d320e1fda9b1fd0b40220603dac961f16b86e9c65ab8c127f696192ffb6856705a4913c0c92c2d9f5a0f201210392091d4341e4b15692234ead7d374c4fa5dc636c30f0042526366ea70f5dc889feffffff02cd67b551000000001976a914035d5bd3df669b4a50839d004d4301d0edfa793c88acb7984525230000001976a9140f165c712e18717410e5ef952529dc470e34b73b88ac8ccf6a00", + "txid": "d4fe2eea4e62b3705ac5fda29deeaaa4d2dea446077e4153ae84398c3d62ccb4", + "blocktime": 1532239864, + "time": 1532239864, + "locktime": 6999948, + "vsize": 373, + "version": 1, + "vin": [ + { + "txid": "25e1512ac204a80089506a33fd5d4201426c4a32fb2cea63589a7575736d3203", + "vout": 0, + "scriptSig": { + "hex": "47304402200632ff38dc836b95e81f2022d2e39222c4261e85e64d909f53969d94c0febc9c022065000e5660744b699e9fb6f28a0a46e06b3870a2fe382f80c867e1ee3606056401210392091d4341e4b15692234ead7d374c4fa5dc636c30f0042526366ea70f5dc889" + }, + "sequence": 4294967294 + }, + { + "txid": "9dfda0bd096a6211ea6a4204e5bf434ee7d8c7113c59d9b0343f3deb3bd12e3e", + "vout": 0, + "scriptSig": { + "hex": "4830450221009077f18dcbe30dda597f8a503414d443cb6e8ad45948df6d320e1fda9b1fd0b40220603dac961f16b86e9c65ab8c127f696192ffb6856705a4913c0c92c2d9f5a0f201210392091d4341e4b15692234ead7d374c4fa5dc636c30f0042526366ea70f5dc889" + }, + "sequence": 4294967294 } + ], + "vout": [ + { + "value": 13.70843085, + "n": 0, + "scriptPubKey": { + "hex": "76a914035d5bd3df669b4a50839d004d4301d0edfa793c88ac" + } + }, + { + "value": 1509.49173431, + "n": 1, + "scriptPubKey": { + "hex": "76a9140f165c712e18717410e5ef952529dc470e34b73b88ac" + } + } + ] } -} \ No newline at end of file + } +} diff --git a/tests/rpc/testdata/litecoin.json b/tests/rpc/testdata/litecoin.json index 4f5a5f5a42..f1797989f1 100644 --- a/tests/rpc/testdata/litecoin.json +++ b/tests/rpc/testdata/litecoin.json @@ -1,49 +1,50 @@ { - "blockHeight": 1377592, - "blockHash": "bddb1cfbd474e9516399b373e411bd33c1a71cb01aa8469a27d397ef0a891c7d", - "blockTime": 1519947864, - "blockTxs": [ - "84e9147bf6e171adbda3b3961e467652286d9d9c2933d19326bf84766d047922", - "8d6e628b891dd17bfe3bb5a24a6c7f02ebc2cf499a85515d0325033aa74ab53a", - "5b77ca9735f65d110b086be410658d0239e1fcee13231942a262b35a5b8d6a91", - "19ad3daa2447be4e000d822f79ce252f274b016dacf1418b433d36c0aaf24f18", - "5bffbf0c8ff66d298d94dc323c3644e21932dfc733603d6637ff46cb8d34466c", - "90d587e35b23905f0125111f41d69bb6c7eed44f0944caad2903aae1f174ac49" - ], - "txDetails": { - "19ad3daa2447be4e000d822f79ce252f274b016dacf1418b433d36c0aaf24f18": { - "hex": "010000000001011f9216c16c78386540d7ae7d32657c388ef5f204596f84ea0851dcb78c479a87010000001716001432d094c8e2efd308d2c69affd6712ebbf7a5a286ffffffff0289544a110000000017a91457ca840d6c811dd6808722babe3f88d2fdb2ca14875bb1c9180000000017a91482696a9b4188eda8f93b120315cad4260cbb90db8702483045022100aa256153317133fa719180935017671e33ca77df6f5426554f5b0855f07a392b02202684e8c30623e1c4e753ea23a95bd571e1fda7d15dc0b1e2d54ff5bc50329256012103399a1d98f0733ef400ff8d4f43fe4543065f7f387c863361c77a8826321ca6fb00000000", - "txid": "19ad3daa2447be4e000d822f79ce252f274b016dacf1418b433d36c0aaf24f18", - "blocktime": 1519947864, - "time": 1519947864, - "locktime": 0, - "version": 1, - "vin": [ - { - "txid": "879a478cb7dc5108ea846f5904f2f58e387c65327daed7406538786cc116921f", - "vout": 1, - "scriptSig": { - "hex": "16001432d094c8e2efd308d2c69affd6712ebbf7a5a286" - }, - "sequence": 4294967295 - } - ], - "vout": [ - { - "value": 2.90083977, - "n": 0, - "scriptPubKey": { - "hex": "a91457ca840d6c811dd6808722babe3f88d2fdb2ca1487" - } - }, - { - "value": 4.15871323, - "n": 1, - "scriptPubKey": { - "hex": "a91482696a9b4188eda8f93b120315cad4260cbb90db87" - } - } - ] + "blockHeight": 1377592, + "blockHash": "bddb1cfbd474e9516399b373e411bd33c1a71cb01aa8469a27d397ef0a891c7d", + "blockTime": 1519947864, + "blockTxs": [ + "84e9147bf6e171adbda3b3961e467652286d9d9c2933d19326bf84766d047922", + "8d6e628b891dd17bfe3bb5a24a6c7f02ebc2cf499a85515d0325033aa74ab53a", + "5b77ca9735f65d110b086be410658d0239e1fcee13231942a262b35a5b8d6a91", + "19ad3daa2447be4e000d822f79ce252f274b016dacf1418b433d36c0aaf24f18", + "5bffbf0c8ff66d298d94dc323c3644e21932dfc733603d6637ff46cb8d34466c", + "90d587e35b23905f0125111f41d69bb6c7eed44f0944caad2903aae1f174ac49" + ], + "txDetails": { + "19ad3daa2447be4e000d822f79ce252f274b016dacf1418b433d36c0aaf24f18": { + "hex": "010000000001011f9216c16c78386540d7ae7d32657c388ef5f204596f84ea0851dcb78c479a87010000001716001432d094c8e2efd308d2c69affd6712ebbf7a5a286ffffffff0289544a110000000017a91457ca840d6c811dd6808722babe3f88d2fdb2ca14875bb1c9180000000017a91482696a9b4188eda8f93b120315cad4260cbb90db8702483045022100aa256153317133fa719180935017671e33ca77df6f5426554f5b0855f07a392b02202684e8c30623e1c4e753ea23a95bd571e1fda7d15dc0b1e2d54ff5bc50329256012103399a1d98f0733ef400ff8d4f43fe4543065f7f387c863361c77a8826321ca6fb00000000", + "txid": "19ad3daa2447be4e000d822f79ce252f274b016dacf1418b433d36c0aaf24f18", + "blocktime": 1519947864, + "time": 1519947864, + "locktime": 0, + "vsize": 166, + "version": 1, + "vin": [ + { + "txid": "879a478cb7dc5108ea846f5904f2f58e387c65327daed7406538786cc116921f", + "vout": 1, + "scriptSig": { + "hex": "16001432d094c8e2efd308d2c69affd6712ebbf7a5a286" + }, + "sequence": 4294967295 } + ], + "vout": [ + { + "value": 2.90083977, + "n": 0, + "scriptPubKey": { + "hex": "a91457ca840d6c811dd6808722babe3f88d2fdb2ca1487" + } + }, + { + "value": 4.15871323, + "n": 1, + "scriptPubKey": { + "hex": "a91482696a9b4188eda8f93b120315cad4260cbb90db87" + } + } + ] } -} \ No newline at end of file + } +} diff --git a/tests/rpc/testdata/namecoin.json b/tests/rpc/testdata/namecoin.json index 489f0c534d..4d1587eea4 100644 --- a/tests/rpc/testdata/namecoin.json +++ b/tests/rpc/testdata/namecoin.json @@ -1,72 +1,73 @@ { - "blockHeight": 404680, - "blockHash": "920fe53b840111f7e593d93ba58dc54e043e10f8fa4a678e86a98f5cb5b29614", - "blockTime": 1530003649, - "blockTxs": [ - "80b8477d10df9ece7d8dde2d30817e2855af1fb66b7a9ac860e592118ae33f5f", - "afcd8e3638b11b1ce52055474dcce78c4129e95ebee3305574acda48deea8a65", - "6ab2278f25fe3ce914fb49ad79679cdb337c0d38211a865db126ca90c6850a60", - "88628abcb7014532daa9a084998e2f770c92e81833f4cc419e8da17bdea29a2e", - "cf04d4d9f1ebcec2d005922628af77dad24d7d735b56befc8b60014259ebfe0f", - "161ea9e2056c86559d5e593554dbe90c8ae8e99b2b38ce4f78866b4d586bddfc", - "89866c18f8effe8f7b15bb2973907ec50d5e2e5d79d9c8054d2f286b66c7a318", - "7269a55d870661d51611e91afd5963a2df69cb7cb70e55a0019128db42c07393", - "e94ff789e7cea0a9935baae202018d41ab238e4536e397d194f307afc126fd54", - "f144f6346a8ce5f5f350753738958b716845fdd76841f2fb691b74206de20099", - "2b4f85cead2f60224deedf8b9a65312221ccd531c3324211a5ab7bd1e4b3592e", - "c1b3ffdae37b61052bd7f7bd14b697402208bc6eeedffd3ef05e3b8eeeccf623", - "25562c32931c4a5b045076609c211c0d9d9f50dd49d669c49cabd50473677fc0", - "1e41bdcb5bc4038cd5ca6fd8fcfa115ce5ed8b4bb26849d8129a8f8cd1493f28", - "5d84a38142d07de023114252ac8efd5172c8334818d95c71b8053544ce57f817", - "7fe0c2a2746c1237a9b1af7a595bf56ce23bf834ac0c2a6d29b480a0a4fbd524", - "784104089ce84977bb235b2509651677a84789c415d60eccc9dbf77be585ad9c", - "f00c1aef90779f58ffe451ce026ab491c1750e7eedecfb65925f46b971507f88", - "3b9c23fd6730cf2e11bc9f009db6f21f35df6b50941a46fc0a83cff423dd0695", - "7c6f996288a7d82df333b897301267519d033d889c8490b30785d526efe3e36d", - "fb8f9c1e3139f389ae7e21fd66bd506ae749cb303a1838b2738a4f8c63096c2b", - "4cdd7b9596862643df7c63a5b0b0e2deaa843e8251bfc60716b94ce0cb656660", - "435880f0d752539150dd828ebe1e4fb2ef92b3269b160278b3b98248c22036fd", - "9a2c3c096d630c1abf2b3fa0da367d954f2213ad5d8136375d42de3cc98132d1", - "cff4f408645e25f3f093d42684449fc51b31e897213bd98ebace7d49acdb5f4b", - "be1370f1e514fbb0d2345ee28d274081d56267bfddc6aa64090e3af4fcce6310", - "e422f60e56f36a03e263d8f812a99dcbc3c790f051a59ee5604e1aff97143b71", - "5082cc9765402eb1ca84273adcd264a9fb44cc427b921e5e776252eca2c6584a", - "08eb38fe093951404debe36ccc0e528dde6ee983c9d6f5d6752bcd160748fb7f" - ], - "txDetails": { - "08eb38fe093951404debe36ccc0e528dde6ee983c9d6f5d6752bcd160748fb7f": { - "hex": "010000000101292738408181cc445a937124bad53f0c1bfeff9e7fc6fb3f1713a6f74f3a22020000006b483045022100bc7c33f785866e688e7de7cdf385dd710159e9300e7c2702dcb556433c635e4f02202c9fa4659db2648061a14ef9108424866116695367d9042e7c1234a4556e900c012103fbb34dd0aca298fb23ba79af2c007880e593b6832e75784cdba1661f110f751efeffffff02a0870300000000001976a9145090f77ac9d008d11fe1da3283486b05a15920b688ac2827760c000000001976a91404a56a80df5913d9a65095d2498db77ad3bd690f88acc62c0600", - "txid": "08eb38fe093951404debe36ccc0e528dde6ee983c9d6f5d6752bcd160748fb7f", - "blocktime": 1530003649, - "time": 1530003649, - "locktime": 404678, - "version": 1, - "vin": [ - { - "txid": "223a4ff7a613173ffbc67f9efffe1b0c3fd5ba2471935a44cc81814038272901", - "vout": 2, - "sequence": 4294967294, - "scriptSig": { - "hex": "483045022100bc7c33f785866e688e7de7cdf385dd710159e9300e7c2702dcb556433c635e4f02202c9fa4659db2648061a14ef9108424866116695367d9042e7c1234a4556e900c012103fbb34dd0aca298fb23ba79af2c007880e593b6832e75784cdba1661f110f751e" - } - } - ], - "vout": [ - { - "value": 0.00231328, - "n": 0, - "scriptPubKey": { - "hex": "76a9145090f77ac9d008d11fe1da3283486b05a15920b688ac" - } - }, - { - "value": 2.09069864, - "n": 1, - "scriptPubKey": { - "hex": "76a91404a56a80df5913d9a65095d2498db77ad3bd690f88ac" - } - } - ] + "blockHeight": 404680, + "blockHash": "920fe53b840111f7e593d93ba58dc54e043e10f8fa4a678e86a98f5cb5b29614", + "blockTime": 1530003649, + "blockTxs": [ + "80b8477d10df9ece7d8dde2d30817e2855af1fb66b7a9ac860e592118ae33f5f", + "afcd8e3638b11b1ce52055474dcce78c4129e95ebee3305574acda48deea8a65", + "6ab2278f25fe3ce914fb49ad79679cdb337c0d38211a865db126ca90c6850a60", + "88628abcb7014532daa9a084998e2f770c92e81833f4cc419e8da17bdea29a2e", + "cf04d4d9f1ebcec2d005922628af77dad24d7d735b56befc8b60014259ebfe0f", + "161ea9e2056c86559d5e593554dbe90c8ae8e99b2b38ce4f78866b4d586bddfc", + "89866c18f8effe8f7b15bb2973907ec50d5e2e5d79d9c8054d2f286b66c7a318", + "7269a55d870661d51611e91afd5963a2df69cb7cb70e55a0019128db42c07393", + "e94ff789e7cea0a9935baae202018d41ab238e4536e397d194f307afc126fd54", + "f144f6346a8ce5f5f350753738958b716845fdd76841f2fb691b74206de20099", + "2b4f85cead2f60224deedf8b9a65312221ccd531c3324211a5ab7bd1e4b3592e", + "c1b3ffdae37b61052bd7f7bd14b697402208bc6eeedffd3ef05e3b8eeeccf623", + "25562c32931c4a5b045076609c211c0d9d9f50dd49d669c49cabd50473677fc0", + "1e41bdcb5bc4038cd5ca6fd8fcfa115ce5ed8b4bb26849d8129a8f8cd1493f28", + "5d84a38142d07de023114252ac8efd5172c8334818d95c71b8053544ce57f817", + "7fe0c2a2746c1237a9b1af7a595bf56ce23bf834ac0c2a6d29b480a0a4fbd524", + "784104089ce84977bb235b2509651677a84789c415d60eccc9dbf77be585ad9c", + "f00c1aef90779f58ffe451ce026ab491c1750e7eedecfb65925f46b971507f88", + "3b9c23fd6730cf2e11bc9f009db6f21f35df6b50941a46fc0a83cff423dd0695", + "7c6f996288a7d82df333b897301267519d033d889c8490b30785d526efe3e36d", + "fb8f9c1e3139f389ae7e21fd66bd506ae749cb303a1838b2738a4f8c63096c2b", + "4cdd7b9596862643df7c63a5b0b0e2deaa843e8251bfc60716b94ce0cb656660", + "435880f0d752539150dd828ebe1e4fb2ef92b3269b160278b3b98248c22036fd", + "9a2c3c096d630c1abf2b3fa0da367d954f2213ad5d8136375d42de3cc98132d1", + "cff4f408645e25f3f093d42684449fc51b31e897213bd98ebace7d49acdb5f4b", + "be1370f1e514fbb0d2345ee28d274081d56267bfddc6aa64090e3af4fcce6310", + "e422f60e56f36a03e263d8f812a99dcbc3c790f051a59ee5604e1aff97143b71", + "5082cc9765402eb1ca84273adcd264a9fb44cc427b921e5e776252eca2c6584a", + "08eb38fe093951404debe36ccc0e528dde6ee983c9d6f5d6752bcd160748fb7f" + ], + "txDetails": { + "08eb38fe093951404debe36ccc0e528dde6ee983c9d6f5d6752bcd160748fb7f": { + "hex": "010000000101292738408181cc445a937124bad53f0c1bfeff9e7fc6fb3f1713a6f74f3a22020000006b483045022100bc7c33f785866e688e7de7cdf385dd710159e9300e7c2702dcb556433c635e4f02202c9fa4659db2648061a14ef9108424866116695367d9042e7c1234a4556e900c012103fbb34dd0aca298fb23ba79af2c007880e593b6832e75784cdba1661f110f751efeffffff02a0870300000000001976a9145090f77ac9d008d11fe1da3283486b05a15920b688ac2827760c000000001976a91404a56a80df5913d9a65095d2498db77ad3bd690f88acc62c0600", + "txid": "08eb38fe093951404debe36ccc0e528dde6ee983c9d6f5d6752bcd160748fb7f", + "blocktime": 1530003649, + "time": 1530003649, + "locktime": 404678, + "vsize": 226, + "version": 1, + "vin": [ + { + "txid": "223a4ff7a613173ffbc67f9efffe1b0c3fd5ba2471935a44cc81814038272901", + "vout": 2, + "sequence": 4294967294, + "scriptSig": { + "hex": "483045022100bc7c33f785866e688e7de7cdf385dd710159e9300e7c2702dcb556433c635e4f02202c9fa4659db2648061a14ef9108424866116695367d9042e7c1234a4556e900c012103fbb34dd0aca298fb23ba79af2c007880e593b6832e75784cdba1661f110f751e" + } } + ], + "vout": [ + { + "value": 0.00231328, + "n": 0, + "scriptPubKey": { + "hex": "76a9145090f77ac9d008d11fe1da3283486b05a15920b688ac" + } + }, + { + "value": 2.09069864, + "n": 1, + "scriptPubKey": { + "hex": "76a91404a56a80df5913d9a65095d2498db77ad3bd690f88ac" + } + } + ] } + } } diff --git a/tests/rpc/testdata/vertcoin.json b/tests/rpc/testdata/vertcoin.json index fb18cacbd8..2add578fdb 100644 --- a/tests/rpc/testdata/vertcoin.json +++ b/tests/rpc/testdata/vertcoin.json @@ -1,97 +1,99 @@ { - "blockHeight": 952235, - "blockHash": "b2787dd022e3aa65b63dbf08af2c9bb4d4a362d95e3328c02743a5c8d75acb36", - "blockTime": 1529932850, - "blockTxs": [ - "366eca05fa8579465d8822ad6462762120b26239201a34981e5f9d9efac3cc31", - "e74c247a5a77d4edd96a5dbb2930c74d9ab550affde991731f78f3e3a2f4b559", - "84f9d1fb25882a8eacad3ba83ff6819531c1c489b176273e76d1e53ecf72e207", - "65a7e80d2f9b21d0003bac50dc529ceab842fb89208e7bf2b7fc20313ee6c999" - ], - "txDetails": { - "e74c247a5a77d4edd96a5dbb2930c74d9ab550affde991731f78f3e3a2f4b559": { - "hex": "0200000001241682f3f9b341163babb2e6a41b43699f9dee63687389c55965d1bd0404c78e000000006b483045022100f0bf400ae5245d99f168d0c0da054660fb22615c94c8ee00c1eebef86b32befc022027ceb32aa210563d2600d6a33f32158e17b5d64eed9829c67dd967d86108db9901210259921142e1b9e7b28e7cbb0449ca5753deeea3fb7f6be470292565cf1ef86d74feffffff024b43f500000000001976a914a58a32fdf2286f90c7a4e9e1c92eb57c4936bacf88ac7e619b00000000001976a914108f72087bfc3131c7c09feba913bf66b652f5c188aca8870e00", - "txid": "e74c247a5a77d4edd96a5dbb2930c74d9ab550affde991731f78f3e3a2f4b559", - "blocktime": 1529932850, - "time": 1529932850, - "locktime": 952232, - "version": 2, - "vin": [ - { - "txid": "8ec70404bdd16559c589736863ee9d9f69431ba4e6b2ab3b1641b3f9f3821624", - "vout": 0, - "sequence": 4294967294, - "scriptSig": { - "hex": "483045022100f0bf400ae5245d99f168d0c0da054660fb22615c94c8ee00c1eebef86b32befc022027ceb32aa210563d2600d6a33f32158e17b5d64eed9829c67dd967d86108db9901210259921142e1b9e7b28e7cbb0449ca5753deeea3fb7f6be470292565cf1ef86d74" - } - } - ], - "vout": [ - { - "value": 0.16073547, - "n": 0, - "scriptPubKey": { - "hex": "76a914a58a32fdf2286f90c7a4e9e1c92eb57c4936bacf88ac" - } - }, - { - "value": 0.10183038, - "n": 1, - "scriptPubKey": { - "hex": "76a914108f72087bfc3131c7c09feba913bf66b652f5c188ac" - } - } - ] + "blockHeight": 952235, + "blockHash": "b2787dd022e3aa65b63dbf08af2c9bb4d4a362d95e3328c02743a5c8d75acb36", + "blockTime": 1529932850, + "blockTxs": [ + "366eca05fa8579465d8822ad6462762120b26239201a34981e5f9d9efac3cc31", + "e74c247a5a77d4edd96a5dbb2930c74d9ab550affde991731f78f3e3a2f4b559", + "84f9d1fb25882a8eacad3ba83ff6819531c1c489b176273e76d1e53ecf72e207", + "65a7e80d2f9b21d0003bac50dc529ceab842fb89208e7bf2b7fc20313ee6c999" + ], + "txDetails": { + "e74c247a5a77d4edd96a5dbb2930c74d9ab550affde991731f78f3e3a2f4b559": { + "hex": "0200000001241682f3f9b341163babb2e6a41b43699f9dee63687389c55965d1bd0404c78e000000006b483045022100f0bf400ae5245d99f168d0c0da054660fb22615c94c8ee00c1eebef86b32befc022027ceb32aa210563d2600d6a33f32158e17b5d64eed9829c67dd967d86108db9901210259921142e1b9e7b28e7cbb0449ca5753deeea3fb7f6be470292565cf1ef86d74feffffff024b43f500000000001976a914a58a32fdf2286f90c7a4e9e1c92eb57c4936bacf88ac7e619b00000000001976a914108f72087bfc3131c7c09feba913bf66b652f5c188aca8870e00", + "txid": "e74c247a5a77d4edd96a5dbb2930c74d9ab550affde991731f78f3e3a2f4b559", + "blocktime": 1529932850, + "time": 1529932850, + "locktime": 952232, + "vsize": 226, + "version": 2, + "vin": [ + { + "txid": "8ec70404bdd16559c589736863ee9d9f69431ba4e6b2ab3b1641b3f9f3821624", + "vout": 0, + "sequence": 4294967294, + "scriptSig": { + "hex": "483045022100f0bf400ae5245d99f168d0c0da054660fb22615c94c8ee00c1eebef86b32befc022027ceb32aa210563d2600d6a33f32158e17b5d64eed9829c67dd967d86108db9901210259921142e1b9e7b28e7cbb0449ca5753deeea3fb7f6be470292565cf1ef86d74" + } + } + ], + "vout": [ + { + "value": 0.16073547, + "n": 0, + "scriptPubKey": { + "hex": "76a914a58a32fdf2286f90c7a4e9e1c92eb57c4936bacf88ac" + } + }, + { + "value": 0.10183038, + "n": 1, + "scriptPubKey": { + "hex": "76a914108f72087bfc3131c7c09feba913bf66b652f5c188ac" + } + } + ] + }, + "65a7e80d2f9b21d0003bac50dc529ceab842fb89208e7bf2b7fc20313ee6c999": { + "hex": "0100000003748dcb015356e8d6281ecec18c308a8198e20555c9b254a1addd2cd67ddb6557000000006b4830450221009bf690117c8624bc97849c49ebc37e02664545761d1d975a4a6636e462a75ecf02206b67bfd773a84a7a6f04905d7b358ee96f0f436674c49f6968b19654390f5763012103ec0f6a189b51640672d67f3b41a06c9b6b01e5c65c4ef56dda7581d1ca6ad2ebffffffff70361357af324b196f1ad09297d1a33a7b42f6a0cf0c54526a712be12364cb9c010000006a473044022007170c7944cee3facff0949fadfb85739dcca9e9dc9cde9cdf86b166169b6d75022072d329810a84d3a997630e742f638bade7734d0f6e2c45cc4c2c6a0797c3bb1f0121024783018d0b2d63a455bb9d71997b458c9dc6138dc38d32ce1cb5dce69699e3d9ffffffff0eb97971739b2d8a359a48bf2099c25976d876ed0b727f00315aa04a6faf500d000000006a4730440220201f8bccbae07bc065faa5c2602b80ed3b54b5d62c59f769bd70ccca34ba343e022013b70d43fcdb673d089265348548c107a1e25acdafeb7dc54ea3d367a7b8ab16012103286074c7bde46a2d5caa130669fe45146b8a3100bcd6f4cd1c80ae7d68e67babffffffff026cbae898020000001976a91413ef0a6e4c098b740aa38d13f5e12a8f9790769888ac04815402000000001976a9140f1800503d3cfe374df1200b3387bcbb43a874e688aca9870e00", + "txid": "65a7e80d2f9b21d0003bac50dc529ceab842fb89208e7bf2b7fc20313ee6c999", + "blocktime": 1529932850, + "time": 1529932850, + "locktime": 952233, + "vsize": 520, + "version": 1, + "vin": [ + { + "txid": "5765db7dd62cddada154b2c95505e298818a308cc1ce1e28d6e8565301cb8d74", + "vout": 0, + "sequence": 4294967295, + "scriptSig": { + "hex": "4830450221009bf690117c8624bc97849c49ebc37e02664545761d1d975a4a6636e462a75ecf02206b67bfd773a84a7a6f04905d7b358ee96f0f436674c49f6968b19654390f5763012103ec0f6a189b51640672d67f3b41a06c9b6b01e5c65c4ef56dda7581d1ca6ad2eb" + } + }, + { + "txid": "9ccb6423e12b716a52540ccfa0f6427b3aa3d19792d01a6f194b32af57133670", + "vout": 1, + "sequence": 4294967295, + "scriptSig": { + "hex": "473044022007170c7944cee3facff0949fadfb85739dcca9e9dc9cde9cdf86b166169b6d75022072d329810a84d3a997630e742f638bade7734d0f6e2c45cc4c2c6a0797c3bb1f0121024783018d0b2d63a455bb9d71997b458c9dc6138dc38d32ce1cb5dce69699e3d9" + } + }, + { + "txid": "0d50af6f4aa05a31007f720bed76d87659c29920bf489a358a2d9b737179b90e", + "vout": 0, + "sequence": 4294967295, + "scriptSig": { + "hex": "4730440220201f8bccbae07bc065faa5c2602b80ed3b54b5d62c59f769bd70ccca34ba343e022013b70d43fcdb673d089265348548c107a1e25acdafeb7dc54ea3d367a7b8ab16012103286074c7bde46a2d5caa130669fe45146b8a3100bcd6f4cd1c80ae7d68e67bab" + } + } + ], + "vout": [ + { + "value": 111.553235, + "n": 0, + "scriptPubKey": { + "hex": "76a91413ef0a6e4c098b740aa38d13f5e12a8f9790769888ac" + } }, - "65a7e80d2f9b21d0003bac50dc529ceab842fb89208e7bf2b7fc20313ee6c999": { - "hex": "0100000003748dcb015356e8d6281ecec18c308a8198e20555c9b254a1addd2cd67ddb6557000000006b4830450221009bf690117c8624bc97849c49ebc37e02664545761d1d975a4a6636e462a75ecf02206b67bfd773a84a7a6f04905d7b358ee96f0f436674c49f6968b19654390f5763012103ec0f6a189b51640672d67f3b41a06c9b6b01e5c65c4ef56dda7581d1ca6ad2ebffffffff70361357af324b196f1ad09297d1a33a7b42f6a0cf0c54526a712be12364cb9c010000006a473044022007170c7944cee3facff0949fadfb85739dcca9e9dc9cde9cdf86b166169b6d75022072d329810a84d3a997630e742f638bade7734d0f6e2c45cc4c2c6a0797c3bb1f0121024783018d0b2d63a455bb9d71997b458c9dc6138dc38d32ce1cb5dce69699e3d9ffffffff0eb97971739b2d8a359a48bf2099c25976d876ed0b727f00315aa04a6faf500d000000006a4730440220201f8bccbae07bc065faa5c2602b80ed3b54b5d62c59f769bd70ccca34ba343e022013b70d43fcdb673d089265348548c107a1e25acdafeb7dc54ea3d367a7b8ab16012103286074c7bde46a2d5caa130669fe45146b8a3100bcd6f4cd1c80ae7d68e67babffffffff026cbae898020000001976a91413ef0a6e4c098b740aa38d13f5e12a8f9790769888ac04815402000000001976a9140f1800503d3cfe374df1200b3387bcbb43a874e688aca9870e00", - "txid": "65a7e80d2f9b21d0003bac50dc529ceab842fb89208e7bf2b7fc20313ee6c999", - "blocktime": 1529932850, - "time": 1529932850, - "locktime": 952233, - "version": 1, - "vin": [ - { - "txid": "5765db7dd62cddada154b2c95505e298818a308cc1ce1e28d6e8565301cb8d74", - "vout": 0, - "sequence": 4294967295, - "scriptSig": { - "hex": "4830450221009bf690117c8624bc97849c49ebc37e02664545761d1d975a4a6636e462a75ecf02206b67bfd773a84a7a6f04905d7b358ee96f0f436674c49f6968b19654390f5763012103ec0f6a189b51640672d67f3b41a06c9b6b01e5c65c4ef56dda7581d1ca6ad2eb" - } - }, - { - "txid": "9ccb6423e12b716a52540ccfa0f6427b3aa3d19792d01a6f194b32af57133670", - "vout": 1, - "sequence": 4294967295, - "scriptSig": { - "hex": "473044022007170c7944cee3facff0949fadfb85739dcca9e9dc9cde9cdf86b166169b6d75022072d329810a84d3a997630e742f638bade7734d0f6e2c45cc4c2c6a0797c3bb1f0121024783018d0b2d63a455bb9d71997b458c9dc6138dc38d32ce1cb5dce69699e3d9" - } - }, - { - "txid": "0d50af6f4aa05a31007f720bed76d87659c29920bf489a358a2d9b737179b90e", - "vout": 0, - "sequence": 4294967295, - "scriptSig": { - "hex": "4730440220201f8bccbae07bc065faa5c2602b80ed3b54b5d62c59f769bd70ccca34ba343e022013b70d43fcdb673d089265348548c107a1e25acdafeb7dc54ea3d367a7b8ab16012103286074c7bde46a2d5caa130669fe45146b8a3100bcd6f4cd1c80ae7d68e67bab" - } - } - ], - "vout": [ - { - "value": 111.55323500, - "n": 0, - "scriptPubKey": { - "hex": "76a91413ef0a6e4c098b740aa38d13f5e12a8f9790769888ac" - } - }, - { - "value": 0.39092484, - "n": 1, - "scriptPubKey": { - "hex": "76a9140f1800503d3cfe374df1200b3387bcbb43a874e688ac" - } - } - ] + { + "value": 0.39092484, + "n": 1, + "scriptPubKey": { + "hex": "76a9140f1800503d3cfe374df1200b3387bcbb43a874e688ac" + } } + ] } + } } From 84b931f42b442422a1972f9c225b1c13c459d70e Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 28 Aug 2022 22:30:04 +0200 Subject: [PATCH 072/530] Display fee per size in explorer transaction detail --- api/worker.go | 7 ++----- server/public.go | 14 ++++++++++++++ static/templates/tx.html | 13 ++++++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/api/worker.go b/api/worker.go index 3011f5de53..f95ca93ee4 100644 --- a/api/worker.go +++ b/api/worker.go @@ -415,6 +415,8 @@ func (w *Worker) getTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe ValueInSat: (*Amount)(pValInSat), ValueOutSat: (*Amount)(&valOutSat), Version: bchainTx.Version, + Size: len(bchainTx.Hex) >> 1, + VSize: int(bchainTx.VSize), Hex: bchainTx.Hex, Rbf: rbf, Vin: vins, @@ -423,11 +425,6 @@ func (w *Worker) getTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe TokenTransfers: tokens, EthereumSpecific: ethSpecific, } - if w.chainParser.SupportsVSize() { - r.Size = len(bchainTx.Hex) >> 1 - r.VSize = int(bchainTx.VSize) - - } return r, nil } diff --git a/server/public.go b/server/public.go index b92e098353..4147d241b2 100644 --- a/server/public.go +++ b/server/public.go @@ -454,6 +454,7 @@ func (s *PublicServer) parseTemplates() []*template.Template { "formatAmount": s.formatAmount, "formatAmountWithDecimals": formatAmountWithDecimals, "setTxToTemplateData": setTxToTemplateData, + "feePerByte": feePerByte, "isOwnAddress": isOwnAddress, "toJSON": toJSON, "tokenTransfersCount": tokenTransfersCount, @@ -556,6 +557,19 @@ func setTxToTemplateData(td *TemplateData, tx *api.Tx) *TemplateData { return td } +// feePerByte returns fee per vByte or Byte if vsize is unknown +func feePerByte(tx *api.Tx) string { + if tx.FeesSat != nil { + if tx.VSize > 0 { + return fmt.Sprintf("%.2f sat/vByte", float64(tx.FeesSat.AsInt64())/float64(tx.VSize)) + } + if tx.Size > 0 { + return fmt.Sprintf("%.2f sat/Byte", float64(tx.FeesSat.AsInt64())/float64(tx.Size)) + } + } + return "" +} + // isOwnAddress returns true if the address is the one that is being shown in the explorer func isOwnAddress(td *TemplateData, a string) bool { return a == td.AddrStr diff --git a/static/templates/tx.html b/static/templates/tx.html index f139cba1c8..b78df2f982 100644 --- a/static/templates/tx.html +++ b/static/templates/tx.html @@ -59,17 +59,24 @@

Summary

Total Output {{formatAmount $tx.ValueOutSat}} {{$cs}} + {{- if $tx.VSize -}} + + Size / vSize + {{$tx.Size}} / {{$tx.VSize}} + + {{- else -}} {{- if $tx.Size -}} - Size/VSize - {{$tx.Size}}/{{$tx.VSize}} + Size + {{$tx.Size}} {{- end -}} {{- end -}} + {{- end -}} {{- if $tx.FeesSat -}} Fees - {{formatAmount $tx.FeesSat}} {{$cs}} + {{formatAmount $tx.FeesSat}} {{$cs}}{{if $tx.Size}} ({{feePerByte $tx}}){{end}} {{end -}} {{- if not $tx.Confirmations}} From 1a476e58f07cb32ffecc41392b339428f7075feb Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 30 Aug 2022 02:05:03 +0200 Subject: [PATCH 073/530] Upgrade to go 1.19 and rocksdb 7.5.3 --- README.md | 10 ----- bchain/coins/blockchain.go | 1 + build/docker/bin/Dockerfile | 8 ++-- build/docker/bin/Makefile | 10 ++--- db/bulkconnect.go | 24 +++++------ db/dboptions.go | 23 +++++----- db/fiat.go | 6 +-- db/fiat_test.go | 4 +- db/rocksdb.go | 68 +++++++++++++++--------------- db/rocksdb_ethereumtype.go | 20 ++++----- db/rocksdb_ethereumtype_test.go | 4 +- docs/build.md | 18 ++++---- fiat/coingecko.go | 4 +- fourbyte/fourbyte.go | 4 +- go.mod | 9 ++-- go.sum | 18 ++++---- server/public_ethereumtype_test.go | 4 +- server/public_test.go | 4 +- 18 files changed, 113 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index 541dec0378..d49fc1d449 100644 --- a/README.md +++ b/README.md @@ -66,16 +66,6 @@ Check [this](https://github.com/trezor/blockbook/issues/89) or [this](https://gi Your coin's block/transaction data may not be compatible with `BitcoinParser` `ParseBlock`/`ParseTx`, which is used by default. In that case, implement your coin in a similar way we used in case of [zcash](https://github.com/trezor/blockbook/tree/master/bchain/coins/zec) and some other coins. The principle is not to parse the block/transaction data in Blockbook but instead to get parsed transactions as json from the backend. -#### Cannot build Blockbook using `go build` command - -When building Blockbook I get error `not enough arguments in call to _Cfunc_rocksdb_approximate_sizes`. - -RocksDB version 6.16.0 changed the API in a backwards incompatible way. It is necessary to build Blockbook with the `rocksdb_6_16` tag to fix the compatibility problem. The correct way to build Blockbook is: - -``` -go build -tags rocksdb_6_16 -``` - ## Data storage in RocksDB Blockbook stores data the key-value store RocksDB. Database format is described [here](/docs/rocksdb.md). diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 6172bf5508..8cdd12459e 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -73,6 +73,7 @@ func init() { BlockChainFactories["Ethereum Testnet Ropsten"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Ropsten Archive"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Goerli"] = eth.NewEthereumRPC + BlockChainFactories["Ethereum Testnet Goerli Archive"] = eth.NewEthereumRPC BlockChainFactories["Bcash"] = bch.NewBCashRPC BlockChainFactories["Bcash Testnet"] = bch.NewBCashRPC BlockChainFactories["Bgold"] = btg.NewBGoldRPC diff --git a/build/docker/bin/Dockerfile b/build/docker/bin/Dockerfile index eab7d0ab0f..b10b533a0d 100644 --- a/build/docker/bin/Dockerfile +++ b/build/docker/bin/Dockerfile @@ -8,15 +8,15 @@ RUN apt-get update && \ apt-get upgrade -y && \ apt-get install -y build-essential git wget pkg-config lxc-dev libzmq3-dev \ libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev \ - liblz4-dev graphviz && \ + libzstd-dev liblz4-dev graphviz && \ apt-get clean ARG GOLANG_VERSION -ENV GOLANG_VERSION=go1.17.1 -ENV ROCKSDB_VERSION=v6.22.1 +ENV GOLANG_VERSION=go1.19 +ENV ROCKSDB_VERSION=v7.5.3 ENV GOPATH=/go ENV PATH=$PATH:$GOPATH/bin ENV CGO_CFLAGS="-I/opt/rocksdb/include" -ENV CGO_LDFLAGS="-L/opt/rocksdb -ldl -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4" +ENV CGO_LDFLAGS="-L/opt/rocksdb -ldl -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4 -lzstd" ARG TCMALLOC RUN mkdir /build diff --git a/build/docker/bin/Makefile b/build/docker/bin/Makefile index ebb2483129..ebe713e47e 100644 --- a/build/docker/bin/Makefile +++ b/build/docker/bin/Makefile @@ -10,12 +10,12 @@ ARGS ?= all: build tools build: prepare-sources - cd $(BLOCKBOOK_SRC) && go build -tags rocksdb_6_16 -o $(CURDIR)/blockbook -ldflags="-s -w $(LDFLAGS)" $(ARGS) + cd $(BLOCKBOOK_SRC) && go build -o $(CURDIR)/blockbook -ldflags="-s -w $(LDFLAGS)" $(ARGS) cp $(CURDIR)/blockbook /out/blockbook chown $(PACKAGER) /out/blockbook build-debug: prepare-sources - cd $(BLOCKBOOK_SRC) && go build -tags rocksdb_6_16 -o $(CURDIR)/blockbook -ldflags="$(LDFLAGS)" $(ARGS) + cd $(BLOCKBOOK_SRC) && go build -o $(CURDIR)/blockbook -ldflags="$(LDFLAGS)" $(ARGS) cp $(CURDIR)/blockbook /out/blockbook chown $(PACKAGER) /out/blockbook @@ -24,13 +24,13 @@ tools: chown $(PACKAGER) /out/{ldb,sst_dump} test: prepare-sources - cd $(BLOCKBOOK_SRC) && go test -tags 'rocksdb_6_16 unittest' `go list ./... | grep -vP '^github.com/trezor/blockbook/(contrib|tests)'` $(ARGS) + cd $(BLOCKBOOK_SRC) && go test -tags 'unittest' `go list ./... | grep -vP '^github.com/trezor/blockbook/(contrib|tests)'` $(ARGS) test-integration: prepare-sources - cd $(BLOCKBOOK_SRC) && go test -tags 'rocksdb_6_16 integration' `go list github.com/trezor/blockbook/tests/...` $(ARGS) + cd $(BLOCKBOOK_SRC) && go test -tags 'integration' `go list github.com/trezor/blockbook/tests/...` $(ARGS) test-all: prepare-sources - cd $(BLOCKBOOK_SRC) && go test -tags 'rocksdb_6_16 unittest integration' `go list ./... | grep -v '^github.com/trezor/blockbook/contrib'` $(ARGS) + cd $(BLOCKBOOK_SRC) && go test -tags 'unittest integration' `go list ./... | grep -v '^github.com/trezor/blockbook/contrib'` $(ARGS) prepare-sources: @ [ -n "`ls /src 2> /dev/null`" ] || (echo "/src doesn't exist or is empty" 1>&2 && exit 1) diff --git a/db/bulkconnect.go b/db/bulkconnect.go index 0bd7ac6221..5238aa2fae 100644 --- a/db/bulkconnect.go +++ b/db/bulkconnect.go @@ -3,8 +3,8 @@ package db import ( "time" - "github.com/flier/gorocksdb" "github.com/golang/glog" + "github.com/linxGnu/grocksdb" "github.com/trezor/blockbook/bchain" ) @@ -58,7 +58,7 @@ func (d *RocksDB) InitBulkConnect() (*BulkConnect, error) { return b, nil } -func (b *BulkConnect) storeTxAddresses(wb *gorocksdb.WriteBatch, all bool) (int, int, error) { +func (b *BulkConnect) storeTxAddresses(wb *grocksdb.WriteBatch, all bool) (int, int, error) { var txm map[string]*TxAddresses var sp int if all { @@ -101,7 +101,7 @@ func (b *BulkConnect) storeTxAddresses(wb *gorocksdb.WriteBatch, all bool) (int, func (b *BulkConnect) parallelStoreTxAddresses(c chan error, all bool) { defer close(c) start := time.Now() - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() count, sp, err := b.storeTxAddresses(wb, all) if err != nil { @@ -116,7 +116,7 @@ func (b *BulkConnect) parallelStoreTxAddresses(c chan error, all bool) { c <- nil } -func (b *BulkConnect) storeBalances(wb *gorocksdb.WriteBatch, all bool) (int, error) { +func (b *BulkConnect) storeBalances(wb *grocksdb.WriteBatch, all bool) (int, error) { var bal map[string]*AddrBalance if all { bal = b.balances @@ -141,7 +141,7 @@ func (b *BulkConnect) storeBalances(wb *gorocksdb.WriteBatch, all bool) (int, er func (b *BulkConnect) parallelStoreBalances(c chan error, all bool) { defer close(c) start := time.Now() - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() count, err := b.storeBalances(wb, all) if err != nil { @@ -156,7 +156,7 @@ func (b *BulkConnect) parallelStoreBalances(c chan error, all bool) { c <- nil } -func (b *BulkConnect) storeBulkAddresses(wb *gorocksdb.WriteBatch) error { +func (b *BulkConnect) storeBulkAddresses(wb *grocksdb.WriteBatch) error { for _, ba := range b.bulkAddresses { if err := b.d.storeAddresses(wb, ba.bi.Height, ba.addresses); err != nil { return err @@ -202,7 +202,7 @@ func (b *BulkConnect) connectBlockBitcoinType(block *bchain.Block, storeBlockTxs // open WriteBatch only if going to write if sa || b.bulkAddressesCount > maxBulkAddresses || storeBlockTxs { start := time.Now() - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() bac := b.bulkAddressesCount if sa || b.bulkAddressesCount > maxBulkAddresses { @@ -235,7 +235,7 @@ func (b *BulkConnect) connectBlockBitcoinType(block *bchain.Block, storeBlockTxs return nil } -func (b *BulkConnect) storeAddressContracts(wb *gorocksdb.WriteBatch, all bool) (int, error) { +func (b *BulkConnect) storeAddressContracts(wb *grocksdb.WriteBatch, all bool) (int, error) { var ac map[string]*AddrContracts if all { ac = b.addressContracts @@ -260,7 +260,7 @@ func (b *BulkConnect) storeAddressContracts(wb *gorocksdb.WriteBatch, all bool) func (b *BulkConnect) parallelStoreAddressContracts(c chan error, all bool) { defer close(c) start := time.Now() - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() count, err := b.storeAddressContracts(wb, all) if err != nil { @@ -303,7 +303,7 @@ func (b *BulkConnect) connectBlockEthereumType(block *bchain.Block, storeBlockTx // open WriteBatch only if going to write if sa || b.bulkAddressesCount > maxBulkAddresses || storeBlockTxs { start := time.Now() - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() bac := b.bulkAddressesCount if sa || b.bulkAddressesCount > maxBulkAddresses { @@ -333,7 +333,7 @@ func (b *BulkConnect) connectBlockEthereumType(block *bchain.Block, storeBlockTx // if there are blockSpecificData, store them blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) if blockSpecificData != nil { - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() if err = b.d.storeBlockSpecificDataEthereumType(wb, block); err != nil { return err @@ -378,7 +378,7 @@ func (b *BulkConnect) Close() error { storeAddressContractsChan = make(chan error) go b.parallelStoreAddressContracts(storeAddressContractsChan, true) } - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() bac := b.bulkAddressesCount if err := b.storeBulkAddresses(wb); err != nil { diff --git a/db/dboptions.go b/db/dboptions.go index 4aa95bd8a4..90f4b02eb2 100644 --- a/db/dboptions.go +++ b/db/dboptions.go @@ -2,31 +2,28 @@ package db // #include "rocksdb/c.h" import "C" - -import ( - "github.com/flier/gorocksdb" -) +import "github.com/linxGnu/grocksdb" /* - possible additional tuning, using options not accessible by gorocksdb + possible additional tuning, using options not accessible by grocksdb // #include "rocksdb/c.h" import "C" cNativeOpts := C.rocksdb_options_create() - opts := &gorocksdb.Options{} + opts := &grocksdb.Options{} cField := reflect.Indirect(reflect.ValueOf(opts)).FieldByName("c") cPtr := (**C.rocksdb_options_t)(unsafe.Pointer(cField.UnsafeAddr())) *cPtr = cNativeOpts cNativeBlockOpts := C.rocksdb_block_based_options_create() - blockOpts := &gorocksdb.BlockBasedTableOptions{} + blockOpts := &grocksdb.BlockBasedTableOptions{} cBlockField := reflect.Indirect(reflect.ValueOf(blockOpts)).FieldByName("c") cBlockPtr := (**C.rocksdb_block_based_table_options_t)(unsafe.Pointer(cBlockField.UnsafeAddr())) *cBlockPtr = cNativeBlockOpts // https://github.com/facebook/rocksdb/wiki/Partitioned-Index-Filters - blockOpts.SetIndexType(gorocksdb.KTwoLevelIndexSearchIndexType) + blockOpts.SetIndexType(grocksdb.KTwoLevelIndexSearchIndexType) C.rocksdb_block_based_options_set_partition_filters(cNativeBlockOpts, boolToChar(true)) C.rocksdb_block_based_options_set_metadata_block_size(cNativeBlockOpts, C.uint64_t(4096)) C.rocksdb_block_based_options_set_cache_index_and_filter_blocks_with_high_priority(cNativeBlockOpts, boolToChar(true)) @@ -41,16 +38,16 @@ func boolToChar(b bool) C.uchar { } */ -func createAndSetDBOptions(bloomBits int, c *gorocksdb.Cache, maxOpenFiles int) *gorocksdb.Options { - blockOpts := gorocksdb.NewDefaultBlockBasedTableOptions() +func createAndSetDBOptions(bloomBits int, c *grocksdb.Cache, maxOpenFiles int) *grocksdb.Options { + blockOpts := grocksdb.NewDefaultBlockBasedTableOptions() blockOpts.SetBlockSize(32 << 10) // 32kB blockOpts.SetBlockCache(c) if bloomBits > 0 { - blockOpts.SetFilterPolicy(gorocksdb.NewBloomFilter(bloomBits)) + blockOpts.SetFilterPolicy(grocksdb.NewBloomFilter(float64(bloomBits))) } blockOpts.SetFormatVersion(4) - opts := gorocksdb.NewDefaultOptions() + opts := grocksdb.NewDefaultOptions() opts.SetBlockBasedTableFactory(blockOpts) opts.SetCreateIfMissing(true) opts.SetCreateIfMissingColumnFamilies(true) @@ -60,6 +57,6 @@ func createAndSetDBOptions(bloomBits int, c *gorocksdb.Cache, maxOpenFiles int) opts.SetWriteBufferSize(1 << 27) // 128MB opts.SetMaxBytesForLevelBase(1 << 27) // 128MB opts.SetMaxOpenFiles(maxOpenFiles) - opts.SetCompression(gorocksdb.LZ4HCCompression) + opts.SetCompression(grocksdb.LZ4HCCompression) return opts } diff --git a/db/fiat.go b/db/fiat.go index 9347aba00b..5f9a52840d 100644 --- a/db/fiat.go +++ b/db/fiat.go @@ -7,9 +7,9 @@ import ( "time" vlq "github.com/bsm/go-vlq" - "github.com/flier/gorocksdb" "github.com/golang/glog" "github.com/juju/errors" + "github.com/linxGnu/grocksdb" ) // FiatRatesTimeFormat is a format string for storing FiatRates timestamps in rocksdb @@ -152,7 +152,7 @@ func FiatRatesConvertDate(date string) (*time.Time, error) { } // FiatRatesStoreTicker stores ticker data at the specified time -func (d *RocksDB) FiatRatesStoreTicker(wb *gorocksdb.WriteBatch, ticker *CurrencyRatesTicker) error { +func (d *RocksDB) FiatRatesStoreTicker(wb *grocksdb.WriteBatch, ticker *CurrencyRatesTicker) error { if len(ticker.Rates) == 0 { return errors.New("Error storing ticker: empty rates") } @@ -180,7 +180,7 @@ func isSuitableTicker(ticker *CurrencyRatesTicker, vsCurrency string, token stri return true } -func getTickerFromIterator(it *gorocksdb.Iterator, vsCurrency string, token string) (*CurrencyRatesTicker, error) { +func getTickerFromIterator(it *grocksdb.Iterator, vsCurrency string, token string) (*CurrencyRatesTicker, error) { timeObj, err := time.Parse(FiatRatesTimeFormat, string(it.Key().Data())) if err != nil { return nil, err diff --git a/db/fiat_test.go b/db/fiat_test.go index 1c90c60696..adb98563b4 100644 --- a/db/fiat_test.go +++ b/db/fiat_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/flier/gorocksdb" + "github.com/linxGnu/grocksdb" ) func TestRocksTickers(t *testing.T) { @@ -60,7 +60,7 @@ func TestRocksTickers(t *testing.T) { }, } - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() err := d.FiatRatesStoreTicker(wb, ticker1) if err != nil { diff --git a/db/rocksdb.go b/db/rocksdb.go index ad02a0de9b..6337994925 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -15,9 +15,9 @@ import ( "unsafe" vlq "github.com/bsm/go-vlq" - "github.com/flier/gorocksdb" "github.com/golang/glog" "github.com/juju/errors" + "github.com/linxGnu/grocksdb" "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/common" ) @@ -34,8 +34,8 @@ const refreshIterator = 5000000 // RepairRocksDB calls RocksDb db repair function func RepairRocksDB(name string) error { glog.Infof("rocksdb: repair") - opts := gorocksdb.NewDefaultOptions() - return gorocksdb.RepairDb(name, opts) + opts := grocksdb.NewDefaultOptions() + return grocksdb.RepairDb(name, opts) } type connectBlockStats struct { @@ -60,14 +60,14 @@ const ( // RocksDB handle type RocksDB struct { path string - db *gorocksdb.DB - wo *gorocksdb.WriteOptions - ro *gorocksdb.ReadOptions - cfh []*gorocksdb.ColumnFamilyHandle + db *grocksdb.DB + wo *grocksdb.WriteOptions + ro *grocksdb.ReadOptions + cfh []*grocksdb.ColumnFamilyHandle chainParser bchain.BlockChainParser is *common.InternalState metrics *common.Metrics - cache *gorocksdb.Cache + cache *grocksdb.Cache maxOpenFiles int cbs connectBlockStats } @@ -104,20 +104,20 @@ var cfBaseNames = []string{"default", "height", "addresses", "blockTxs", "transa var cfNamesBitcoinType = []string{"addressBalance", "txAddresses"} var cfNamesEthereumType = []string{"addressContracts", "internalData", "contracts", "functionSignatures", "blockInternalDataErrors", "addressAliases"} -func openDB(path string, c *gorocksdb.Cache, openFiles int) (*gorocksdb.DB, []*gorocksdb.ColumnFamilyHandle, error) { +func openDB(path string, c *grocksdb.Cache, openFiles int) (*grocksdb.DB, []*grocksdb.ColumnFamilyHandle, error) { // opts with bloom filter opts := createAndSetDBOptions(10, c, openFiles) // opts for addresses without bloom filter // 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, opts} + cfOptions := []*grocksdb.Options{opts, opts, optsAddresses, opts, opts, opts} // append type specific options count := len(cfNames) - len(cfOptions) for i := 0; i < count; i++ { cfOptions = append(cfOptions, opts) } - db, cfh, err := gorocksdb.OpenDbColumnFamilies(opts, path, cfNames, cfOptions) + db, cfh, err := grocksdb.OpenDbColumnFamilies(opts, path, cfNames, cfOptions) if err != nil { return nil, nil, err } @@ -139,13 +139,13 @@ func NewRocksDB(path string, cacheSize, maxOpenFiles int, parser bchain.BlockCha return nil, errors.New("Unknown chain type") } - c := gorocksdb.NewLRUCache(uint64(cacheSize)) + c := grocksdb.NewLRUCache(uint64(cacheSize)) db, cfh, err := openDB(path, c, maxOpenFiles) if err != nil { return nil, err } - wo := gorocksdb.NewDefaultWriteOptions() - ro := gorocksdb.NewDefaultReadOptions() + wo := grocksdb.NewDefaultWriteOptions() + ro := grocksdb.NewDefaultReadOptions() return &RocksDB{path, db, wo, ro, cfh, parser, nil, metrics, c, maxOpenFiles, connectBlockStats{}}, nil } @@ -200,7 +200,7 @@ func atoUint64(s string) uint64 { return uint64(i) } -func (d *RocksDB) WriteBatch(wb *gorocksdb.WriteBatch) error { +func (d *RocksDB) WriteBatch(wb *grocksdb.WriteBatch) error { return d.db.Write(d.wo, wb) } @@ -325,7 +325,7 @@ const ( // ConnectBlock indexes addresses in the block and stores them in db func (d *RocksDB) ConnectBlock(block *bchain.Block) error { - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() if glog.V(2) { @@ -740,7 +740,7 @@ func addToAddressesMap(addresses addressesMap, strAddrDesc string, btxID []byte, return false } -func (d *RocksDB) storeAddresses(wb *gorocksdb.WriteBatch, height uint32, addresses addressesMap) error { +func (d *RocksDB) storeAddresses(wb *grocksdb.WriteBatch, height uint32, addresses addressesMap) error { for addrDesc, txi := range addresses { ba := bchain.AddressDescriptor(addrDesc) key := packAddressKey(ba, height) @@ -750,7 +750,7 @@ func (d *RocksDB) storeAddresses(wb *gorocksdb.WriteBatch, height uint32, addres return nil } -func (d *RocksDB) storeTxAddresses(wb *gorocksdb.WriteBatch, am map[string]*TxAddresses) error { +func (d *RocksDB) storeTxAddresses(wb *grocksdb.WriteBatch, am map[string]*TxAddresses) error { varBuf := make([]byte, maxPackedBigintBytes) buf := make([]byte, 1024) for txID, ta := range am { @@ -760,7 +760,7 @@ func (d *RocksDB) storeTxAddresses(wb *gorocksdb.WriteBatch, am map[string]*TxAd return nil } -func (d *RocksDB) storeBalances(wb *gorocksdb.WriteBatch, abm map[string]*AddrBalance) error { +func (d *RocksDB) storeBalances(wb *grocksdb.WriteBatch, abm map[string]*AddrBalance) error { // allocate buffer initial buffer buf := make([]byte, 1024) varBuf := make([]byte, maxPackedBigintBytes) @@ -776,7 +776,7 @@ func (d *RocksDB) storeBalances(wb *gorocksdb.WriteBatch, abm map[string]*AddrBa return nil } -func (d *RocksDB) cleanupBlockTxs(wb *gorocksdb.WriteBatch, block *bchain.Block) error { +func (d *RocksDB) cleanupBlockTxs(wb *grocksdb.WriteBatch, block *bchain.Block) error { keep := d.chainParser.KeepBlockAddresses() // cleanup old block address if block.Height > uint32(keep) { @@ -797,7 +797,7 @@ func (d *RocksDB) cleanupBlockTxs(wb *gorocksdb.WriteBatch, block *bchain.Block) return nil } -func (d *RocksDB) storeAndCleanupBlockTxs(wb *gorocksdb.WriteBatch, block *bchain.Block) error { +func (d *RocksDB) storeAndCleanupBlockTxs(wb *grocksdb.WriteBatch, block *bchain.Block) error { pl := d.chainParser.PackedTxidLen() buf := make([]byte, 0, pl*len(block.Txs)) varBuf := make([]byte, vlq.MaxLen64) @@ -1225,7 +1225,7 @@ func (d *RocksDB) GetBlockInfo(height uint32) (*BlockInfo, error) { return bi, err } -func (d *RocksDB) writeHeightFromBlock(wb *gorocksdb.WriteBatch, block *bchain.Block, op int) error { +func (d *RocksDB) writeHeightFromBlock(wb *grocksdb.WriteBatch, block *bchain.Block, op int) error { return d.writeHeight(wb, block.Height, &BlockInfo{ Hash: block.Hash, Time: block.Time, @@ -1235,7 +1235,7 @@ func (d *RocksDB) writeHeightFromBlock(wb *gorocksdb.WriteBatch, block *bchain.B }, op) } -func (d *RocksDB) writeHeight(wb *gorocksdb.WriteBatch, height uint32, bi *BlockInfo, op int) error { +func (d *RocksDB) writeHeight(wb *grocksdb.WriteBatch, height uint32, bi *BlockInfo, op int) error { key := packUint(height) switch op { case opInsert: @@ -1281,7 +1281,7 @@ func (d *RocksDB) GetAddressAlias(address string) string { return name } -func (d *RocksDB) storeAddressAliasRecords(wb *gorocksdb.WriteBatch, records []bchain.AddressAliasRecord) error { +func (d *RocksDB) storeAddressAliasRecords(wb *grocksdb.WriteBatch, records []bchain.AddressAliasRecord) error { if d.chainParser.UseAddressAliases() { for i := range records { r := &records[i] @@ -1298,7 +1298,7 @@ func (d *RocksDB) storeAddressAliasRecords(wb *gorocksdb.WriteBatch, records []b // Disconnect blocks -func (d *RocksDB) disconnectTxAddressesInputs(wb *gorocksdb.WriteBatch, btxID []byte, inputs []outpoint, txa *TxAddresses, txAddressesToUpdate map[string]*TxAddresses, +func (d *RocksDB) disconnectTxAddressesInputs(wb *grocksdb.WriteBatch, btxID []byte, inputs []outpoint, txa *TxAddresses, txAddressesToUpdate map[string]*TxAddresses, getAddressBalance func(addrDesc bchain.AddressDescriptor) (*AddrBalance, error), addressFoundInTx func(addrDesc bchain.AddressDescriptor, btxID []byte) bool) error { var err error @@ -1354,7 +1354,7 @@ func (d *RocksDB) disconnectTxAddressesInputs(wb *gorocksdb.WriteBatch, btxID [] return nil } -func (d *RocksDB) disconnectTxAddressesOutputs(wb *gorocksdb.WriteBatch, btxID []byte, txa *TxAddresses, +func (d *RocksDB) disconnectTxAddressesOutputs(wb *grocksdb.WriteBatch, btxID []byte, txa *TxAddresses, getAddressBalance func(addrDesc bchain.AddressDescriptor) (*AddrBalance, error), addressFoundInTx func(addrDesc bchain.AddressDescriptor, btxID []byte) bool) error { for i, t := range txa.Outputs { @@ -1386,7 +1386,7 @@ func (d *RocksDB) disconnectTxAddressesOutputs(wb *gorocksdb.WriteBatch, btxID [ } func (d *RocksDB) disconnectBlock(height uint32, blockTxs []blockTxs) error { - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() txAddressesToUpdate := make(map[string]*TxAddresses) txAddresses := make([]*TxAddresses, len(blockTxs)) @@ -1498,7 +1498,7 @@ func (d *RocksDB) DisconnectBlockRangeBitcoinType(lower uint32, higher uint32) e return nil } -func (d *RocksDB) storeBalancesDisconnect(wb *gorocksdb.WriteBatch, balances map[string]*AddrBalance) { +func (d *RocksDB) storeBalancesDisconnect(wb *grocksdb.WriteBatch, balances map[string]*AddrBalance) { for _, b := range balances { if b != nil { // remove spent utxos @@ -1584,14 +1584,14 @@ func (d *RocksDB) DeleteTx(txid string) error { return nil } // use write batch so that this delete matches other deletes - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() d.internalDeleteTx(wb, key) return d.WriteBatch(wb) } // internalDeleteTx checks if tx is cached and updates internal state accordingly -func (d *RocksDB) internalDeleteTx(wb *gorocksdb.WriteBatch, key []byte) { +func (d *RocksDB) internalDeleteTx(wb *grocksdb.WriteBatch, key []byte) { val, err := d.db.GetCF(d.ro, d.cfh[cfTransactions], key) // ignore error, it is only for statistics if err == nil { @@ -1745,7 +1745,7 @@ func (d *RocksDB) computeColumnSize(col int, stopCompute chan os.Signal) (int64, var rows, keysSum, valuesSum int64 var seekKey []byte // do not use cache - ro := gorocksdb.NewDefaultReadOptions() + ro := grocksdb.NewDefaultReadOptions() ro.SetFillCache(false) for { var key []byte @@ -1890,7 +1890,7 @@ func (d *RocksDB) fixUtxo(addrDesc bchain.AddressDescriptor, ba *AddrBalance) (b utxos[i], utxos[opp] = utxos[opp], utxos[i] } ba.Utxos = utxos - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() err = d.storeBalances(wb, map[string]*AddrBalance{string(addrDesc): ba}) if err == nil { err = d.WriteBatch(wb) @@ -1903,7 +1903,7 @@ func (d *RocksDB) fixUtxo(addrDesc bchain.AddressDescriptor, ba *AddrBalance) (b } return fixed, false, errors.Errorf("balance %s, checksum %s, from txa %s, txs %d", ba.BalanceSat.String(), checksum.String(), checksumFromTxs.String(), ba.Txs) } else if reorder { - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() err := d.storeBalances(wb, map[string]*AddrBalance{string(addrDesc): ba}) if err == nil { err = d.WriteBatch(wb) @@ -1926,7 +1926,7 @@ func (d *RocksDB) FixUtxos(stop chan os.Signal) error { var row, errorsCount, fixedCount int64 var seekKey []byte // do not use cache - ro := gorocksdb.NewDefaultReadOptions() + ro := grocksdb.NewDefaultReadOptions() ro.SetFillCache(false) for { var addrDesc bchain.AddressDescriptor diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index a75b528444..e48761ab04 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -7,9 +7,9 @@ import ( "sync" vlq "github.com/bsm/go-vlq" - "github.com/flier/gorocksdb" "github.com/golang/glog" "github.com/juju/errors" + "github.com/linxGnu/grocksdb" "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain/coins/eth" ) @@ -131,7 +131,7 @@ func unpackAddrContracts(buf []byte, addrDesc bchain.AddressDescriptor) (*AddrCo }, nil } -func (d *RocksDB) storeAddressContracts(wb *gorocksdb.WriteBatch, acm map[string]*AddrContracts) error { +func (d *RocksDB) storeAddressContracts(wb *grocksdb.WriteBatch, acm map[string]*AddrContracts) error { for addrDesc, acs := range acm { // address with 0 contracts is removed from db - happens on disconnect if acs == nil || (acs.NonContractTxs == 0 && acs.InternalTxs == 0 && len(acs.Contracts) == 0) { @@ -659,7 +659,7 @@ func (d *RocksDB) GetFourByteSignatures(fourBytes uint32) (*[]bchain.FourByteSig } // StoreFourByteSignature stores 4byte signature in DB -func (d *RocksDB) StoreFourByteSignature(wb *gorocksdb.WriteBatch, fourBytes uint32, id uint32, signature *bchain.FourByteSignature) error { +func (d *RocksDB) StoreFourByteSignature(wb *grocksdb.WriteBatch, fourBytes uint32, id uint32, signature *bchain.FourByteSignature) error { key := packFourByteKey(fourBytes, id) wb.PutCF(d.cfh[cfFunctionSignatures], key, packFourByteSignature(signature)) cachedByteSignaturesMux.Lock() @@ -690,7 +690,7 @@ func (d *RocksDB) getEthereumInternalData(btxID []byte) (*bchain.EthereumInterna return d.unpackEthInternalData(buf) } -func (d *RocksDB) storeInternalDataEthereumType(wb *gorocksdb.WriteBatch, blockTxs []ethBlockTx) error { +func (d *RocksDB) storeInternalDataEthereumType(wb *grocksdb.WriteBatch, blockTxs []ethBlockTx) error { for i := range blockTxs { blockTx := &blockTxs[i] if blockTx.internalData != nil { @@ -785,7 +785,7 @@ func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, typeFromCon // StoreContractInfo stores contractInfo in DB // if CreatedInBlock==0 and DestructedInBlock!=0, it is evaluated as a desctruction of a contract, the contract info is updated // in all other cases the contractInfo overwrites previously stored data in DB (however it should not really happen as contract is created only once) -func (d *RocksDB) StoreContractInfo(wb *gorocksdb.WriteBatch, contractInfo *bchain.ContractInfo) error { +func (d *RocksDB) StoreContractInfo(wb *grocksdb.WriteBatch, contractInfo *bchain.ContractInfo) error { if contractInfo.Contract != "" { key, err := d.chainParser.GetAddrDescFromAddress(contractInfo.Contract) if err != nil { @@ -840,7 +840,7 @@ func packBlockTx(buf []byte, blockTx *ethBlockTx) []byte { return buf } -func (d *RocksDB) storeAndCleanupBlockTxsEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block, blockTxs []ethBlockTx) error { +func (d *RocksDB) storeAndCleanupBlockTxsEthereumType(wb *grocksdb.WriteBatch, block *bchain.Block, blockTxs []ethBlockTx) error { pl := d.chainParser.PackedTxidLen() buf := make([]byte, 0, (pl+2*eth.EthereumTypeAddressDescriptorLen)*len(blockTxs)) for i := range blockTxs { @@ -851,7 +851,7 @@ func (d *RocksDB) storeAndCleanupBlockTxsEthereumType(wb *gorocksdb.WriteBatch, return d.cleanupBlockTxs(wb, block) } -func (d *RocksDB) storeBlockInternalDataErrorEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block, message string) error { +func (d *RocksDB) storeBlockInternalDataErrorEthereumType(wb *grocksdb.WriteBatch, block *bchain.Block, message string) error { key := packUint(block.Height) txid, err := d.chainParser.PackTxid(block.Hash) if err != nil { @@ -867,7 +867,7 @@ func (d *RocksDB) storeBlockInternalDataErrorEthereumType(wb *gorocksdb.WriteBat return nil } -func (d *RocksDB) storeBlockSpecificDataEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block) error { +func (d *RocksDB) storeBlockSpecificDataEthereumType(wb *grocksdb.WriteBatch, block *bchain.Block) error { blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) if blockSpecificData != nil { if blockSpecificData.InternalDataError != "" { @@ -1112,7 +1112,7 @@ func (d *RocksDB) disconnectInternalData(btxID []byte, addresses map[string]map[ return nil } -func (d *RocksDB) disconnectBlockTxsEthereumType(wb *gorocksdb.WriteBatch, height uint32, blockTxs []ethBlockTx, contracts map[string]*AddrContracts) error { +func (d *RocksDB) disconnectBlockTxsEthereumType(wb *grocksdb.WriteBatch, height uint32, blockTxs []ethBlockTx, contracts map[string]*AddrContracts) error { glog.Info("Disconnecting block ", height, " containing ", len(blockTxs), " transactions") addresses := make(map[string]map[string]struct{}) for i := range blockTxs { @@ -1169,7 +1169,7 @@ func (d *RocksDB) DisconnectBlockRangeEthereumType(lower uint32, higher uint32) } blocks[height-lower] = blockTxs } - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() contracts := make(map[string]*AddrContracts) for height := higher; height >= lower; height-- { diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index bf651335e9..8083f7eba7 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -8,8 +8,8 @@ import ( "reflect" "testing" - "github.com/flier/gorocksdb" "github.com/juju/errors" + "github.com/linxGnu/grocksdb" "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain/coins/eth" "github.com/trezor/blockbook/common" @@ -359,7 +359,7 @@ func testFourByteSignature(t *testing.T, d *RocksDB) { Name: "xyz", Parameters: []string{"address", "(bytes,uint256[],uint256)", "uint16"}, } - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() if err := d.StoreFourByteSignature(wb, fourBytes, id, &signature); err != nil { t.Fatal(err) diff --git a/docs/build.md b/docs/build.md index a464ccb5a8..3623d50f26 100644 --- a/docs/build.md +++ b/docs/build.md @@ -78,7 +78,7 @@ There are few variables that can be passed to `make` in order to modify build pr `BASE_IMAGE`: Specifies the base image of the Docker build image. By default, it chooses the same Linux distro as the host machine but you can override it this way `make BASE_IMAGE=debian:10 all-bitcoin` to make a build for Debian 10. -*Please be aware that we are running our Blockbooks on Debian 9 and Debian 10 and do not offer support with running it on other distros.* +*Please be aware that we are currently running our Blockbooks on Debian 11 and do not offer support with running it on other distros.* `NO_CACHE`: Common behaviour of Docker image build is that build steps are cached and next time they are executed much faster. Although this is a good idea, when something went wrong you will need to override this behaviour somehow. Execute this @@ -185,13 +185,13 @@ Configuration is described in [config.md](/docs/config.md). ## Manual build -Instructions below are focused on Debian 9 (Stretch) and 10 (Buster). If you want to use another Linux distribution or operating system -like macOS or Windows, please read instructions specific for each project. +Instructions below are focused on Debian 11 on amd64. If you want to use another Linux distribution or operating system +like macOS or Windows, please adapt the instructions to your target system. Setup go environment (use newer version of go as available) ``` -wget https://golang.org/dl/go1.17.1.linux-amd64.tar.gz && tar xf go1.17.1.linux-amd64.tar.gz +wget https://golang.org/dl/go1.19.linux-amd64.tar.gz && tar xf go1.19.linux-amd64.tar.gz sudo mv go /opt/go sudo ln -s /opt/go/bin/go /usr/bin/go # see `go help gopath` for details @@ -206,18 +206,18 @@ make command to create a portable binary. ``` sudo apt-get update && sudo apt-get install -y \ - build-essential git wget pkg-config libzmq3-dev libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev + build-essential git wget pkg-config libzmq3-dev libgflags-dev libsnappy-dev zlib1g-dev libzstd-dev libbz2-dev liblz4-dev git clone https://github.com/facebook/rocksdb.git cd rocksdb -git checkout v6.22.1 +git checkout v7.5.3 CFLAGS=-fPIC CXXFLAGS=-fPIC make release ``` -Setup variables for gorocksdb +Setup variables for grocksdb ``` export CGO_CFLAGS="-I/path/to/rocksdb/include" -export CGO_LDFLAGS="-L/path/to/rocksdb -lrocksdb -lstdc++ -lm -lz -ldl -lbz2 -lsnappy -llz4" +export CGO_LDFLAGS="-L/path/to/rocksdb -lrocksdb -lstdc++ -lm -lz -ldl -lbz2 -lsnappy -llz4 -lzstd" ``` Install ZeroMQ: https://github.com/zeromq/libzmq @@ -237,7 +237,7 @@ Get blockbook sources, install dependencies, build: cd $GOPATH/src git clone https://github.com/trezor/blockbook.git cd blockbook -go build -tags rocksdb_6_16 +go build ``` ### Example command diff --git a/fiat/coingecko.go b/fiat/coingecko.go index ece706d8e5..2b0592e382 100644 --- a/fiat/coingecko.go +++ b/fiat/coingecko.go @@ -10,8 +10,8 @@ import ( "strings" "time" - "github.com/flier/gorocksdb" "github.com/golang/glog" + "github.com/linxGnu/grocksdb" "github.com/trezor/blockbook/db" ) @@ -343,7 +343,7 @@ func (cg *Coingecko) getHistoricalTicker(tickersToUpdate map[uint]*db.CurrencyRa func (cg *Coingecko) storeTickers(tickersToUpdate map[uint]*db.CurrencyRatesTicker) error { if len(tickersToUpdate) > 0 { - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() for _, v := range tickersToUpdate { if err := cg.db.FiatRatesStoreTicker(wb, v); err != nil { diff --git a/fourbyte/fourbyte.go b/fourbyte/fourbyte.go index 21d6fe12a1..2be1a158e1 100644 --- a/fourbyte/fourbyte.go +++ b/fourbyte/fourbyte.go @@ -9,8 +9,8 @@ import ( "strings" "time" - "github.com/flier/gorocksdb" "github.com/golang/glog" + "github.com/linxGnu/grocksdb" "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/db" ) @@ -173,7 +173,7 @@ func (fd *FourByteSignaturesDownloader) downloadSignatures() { } if len(results) > 0 { glog.Infof("FourByteSignaturesDownloader storing %d new signatures", len(results)) - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() for i := range results { diff --git a/go.mod b/go.mod index 7634166d9a..15adcc9d8f 100644 --- a/go.mod +++ b/go.mod @@ -15,16 +15,13 @@ require ( github.com/decred/dcrd/hdkeychain/v3 v3.0.0 github.com/decred/dcrd/txscript/v3 v3.0.0 github.com/ethereum/go-ethereum v1.10.15 - github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect - github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect - github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect - github.com/flier/gorocksdb v0.0.0-20210322035443-567cc51a1652 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/protobuf v1.5.0 github.com/gorilla/websocket v1.4.2 github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect + github.com/linxGnu/grocksdb v1.7.7 github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe github.com/martinboehm/btcd v0.0.0-20221101112928-408689e15809 github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 @@ -48,6 +45,7 @@ require ( github.com/btcsuite/btcd v0.20.1-beta // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dchest/siphash v1.2.1 // indirect github.com/decred/base58 v1.0.3 // indirect github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect @@ -59,14 +57,17 @@ require ( github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.14.0 // indirect github.com/prometheus/procfs v0.2.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/stretchr/testify v1.8.0 // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) // replace github.com/martinboehm/btcutil => ../btcutil diff --git a/go.sum b/go.sum index eaadd1a8a1..a27d52f4b3 100644 --- a/go.sum +++ b/go.sum @@ -189,17 +189,9 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/go-ethereum v1.10.15 h1:E9o0kMbD8HXhp7g6UwIwntY05WTDheCGziMhegcBsQw= github.com/ethereum/go-ethereum v1.10.15/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/flier/gorocksdb v0.0.0-20210322035443-567cc51a1652 h1:8GVjZ8n6qgX3b/0aklxpNar3RLkvS6G7FZcHkiHDUHs= -github.com/flier/gorocksdb v0.0.0-20210322035443-567cc51a1652/go.mod h1:CzkODoa0BVoE4x+tw0Pd0MOyGN/u4ip7M06gXTI7htQ= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= @@ -402,6 +394,8 @@ github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/linxGnu/grocksdb v1.7.7 h1:b6o8gagb4FL+P55qUzPchBR/C0u1lWjJOWQSWbhvTWg= +github.com/linxGnu/grocksdb v1.7.7/go.mod h1:0hTf+iA+GOr0jDX4CgIYyJZxqOH9XlBh6KVj8+zmF34= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -592,13 +586,16 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= @@ -909,8 +906,9 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 9f1ac6f01a..1b95d8cc53 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -8,8 +8,8 @@ import ( "net/http/httptest" "testing" - "github.com/flier/gorocksdb" "github.com/golang/glog" + "github.com/linxGnu/grocksdb" "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain/coins/eth" "github.com/trezor/blockbook/db" @@ -128,7 +128,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { func initEthereumTypeDB(d *db.RocksDB) error { // add 0xa9059cbb transfer(address,uint256) signature - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() if err := d.StoreFourByteSignature(wb, 2835717307, 145, &bchain.FourByteSignature{ Name: "transfer", diff --git a/server/public_test.go b/server/public_test.go index 82f66a0c3d..b5ba334c97 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -14,9 +14,9 @@ import ( "testing" "time" - "github.com/flier/gorocksdb" "github.com/golang/glog" "github.com/gorilla/websocket" + "github.com/linxGnu/grocksdb" "github.com/martinboehm/btcutil/chaincfg" gosocketio "github.com/martinboehm/golang-socketio" "github.com/martinboehm/golang-socketio/transport" @@ -176,7 +176,7 @@ func insertFiatRate(date string, rates map[string]float32, tokenRates map[string Rates: rates, TokenRates: tokenRates, } - wb := gorocksdb.NewWriteBatch() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() if err := d.FiatRatesStoreTicker(wb, ticker); err != nil { return err From 0bb8f69e604d7f0e14666c918492019826d5cf43 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 31 Aug 2022 00:29:41 +0200 Subject: [PATCH 074/530] Migration from DB v5 to v6 for BitcoinType coins --- db/rocksdb.go | 57 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/db/rocksdb.go b/db/rocksdb.go index 6337994925..5f72e6b7be 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -1635,6 +1635,41 @@ func (d *RocksDB) loadBlockTimes() ([]uint32, error) { return times, nil } +func (d *RocksDB) checkColumns(is *common.InternalState) ([]common.InternalStateColumn, error) { + // make sure that column stats match the columns + sc := is.DbColumns + nc := make([]common.InternalStateColumn, len(cfNames)) + for i := 0; i < len(nc); i++ { + nc[i].Name = cfNames[i] + nc[i].Version = dbVersion + for j := 0; j < len(sc); j++ { + if sc[j].Name == nc[i].Name { + // check the version of the column, if it does not match, the db is not compatible + if sc[j].Version != dbVersion { + // upgrade of DB 5 to 6 for BitecoinType coins is possible + // columns transactions and fiatRates must be cleared as they are not compatible + if sc[j].Version == 5 && dbVersion == 6 && d.chainParser.GetChainType() == bchain.ChainBitcoinType { + if nc[i].Name == "transactions" { + d.db.DeleteRangeCF(d.wo, d.cfh[cfTransactions], []byte{0}, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) + } else if nc[i].Name == "fiatRates" { + d.db.DeleteRangeCF(d.wo, d.cfh[cfFiatRates], []byte{0}, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) + } + glog.Infof("Column %s upgraded from v%d to v%d", nc[i].Name, sc[j].Version, dbVersion) + } else { + return nil, errors.Errorf("DB version %v of column '%v' does not match the required version %v. DB is not compatible.", sc[j].Version, sc[j].Name, dbVersion) + } + } + nc[i].Rows = sc[j].Rows + nc[i].KeyBytes = sc[j].KeyBytes + nc[i].ValueBytes = sc[j].ValueBytes + nc[i].Updated = sc[j].Updated + break + } + } + } + return nc, nil +} + // LoadInternalState loads from db internal state or initializes a new one if not yet stored func (d *RocksDB) LoadInternalState(rpcCoin string) (*common.InternalState, error) { val, err := d.db.GetCF(d.ro, d.cfh[cfDefault], []byte(internalStateKey)) @@ -1659,25 +1694,9 @@ func (d *RocksDB) LoadInternalState(rpcCoin string) (*common.InternalState, erro return nil, errors.Errorf("Coins do not match. DB coin %v, RPC coin %v", is.Coin, rpcCoin) } } - // make sure that column stats match the columns - sc := is.DbColumns - nc := make([]common.InternalStateColumn, len(cfNames)) - for i := 0; i < len(nc); i++ { - nc[i].Name = cfNames[i] - nc[i].Version = dbVersion - for j := 0; j < len(sc); j++ { - if sc[j].Name == nc[i].Name { - // check the version of the column, if it does not match, the db is not compatible - if sc[j].Version != dbVersion { - return nil, errors.Errorf("DB version %v of column '%v' does not match the required version %v. DB is not compatible.", sc[j].Version, sc[j].Name, dbVersion) - } - nc[i].Rows = sc[j].Rows - nc[i].KeyBytes = sc[j].KeyBytes - nc[i].ValueBytes = sc[j].ValueBytes - nc[i].Updated = sc[j].Updated - break - } - } + nc, err := d.checkColumns(is) + if err != nil { + return nil, err } is.DbColumns = nc is.BlockTimes, err = d.loadBlockTimes() From abb8b9dc163bc243fd5733e62f932b2fbb6601ba Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 1 Sep 2022 08:50:20 +0200 Subject: [PATCH 075/530] Fix: do not process inputs without txid in mempool --- bchain/mempool_bitcoin_type.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bchain/mempool_bitcoin_type.go b/bchain/mempool_bitcoin_type.go index 009073dd19..1063059dc6 100644 --- a/bchain/mempool_bitcoin_type.go +++ b/bchain/mempool_bitcoin_type.go @@ -61,6 +61,10 @@ func (m *MempoolBitcoinType) getInputAddress(payload *chanInputPayload) *addrInd var addrDesc AddressDescriptor var value *big.Int vin := &payload.tx.Vin[payload.index] + if vin.Txid == "" { + // cannot get address from empty input txid (for example in Litecoin mweb) + return nil + } if m.AddrDescForOutpoint != nil { addrDesc, value = m.AddrDescForOutpoint(Outpoint{vin.Txid, int32(vin.Vout)}) } From 72f2c6fcb74b275c241a0b8a6b282ea7a493aca8 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 1 Sep 2022 09:43:20 +0200 Subject: [PATCH 076/530] Upgrade go-ethereum dependency to v1.10.23 --- go.mod | 14 +- go.sum | 409 +++++---------------------------------------------------- 2 files changed, 43 insertions(+), 380 deletions(-) diff --git a/go.mod b/go.mod index 15adcc9d8f..3a83c4a34e 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c // indirect github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e github.com/dchest/blake256 v1.0.0 // indirect - github.com/deckarep/golang-set v1.7.1 + github.com/deckarep/golang-set v1.8.0 github.com/decred/dcrd/chaincfg/chainhash v1.0.2 github.com/decred/dcrd/chaincfg/v3 v3.0.0 github.com/decred/dcrd/dcrec v1.0.0 @@ -14,9 +14,9 @@ require ( github.com/decred/dcrd/dcrutil/v3 v3.0.0 github.com/decred/dcrd/hdkeychain/v3 v3.0.0 github.com/decred/dcrd/txscript/v3 v3.0.0 - github.com/ethereum/go-ethereum v1.10.15 + github.com/ethereum/go-ethereum v1.10.23 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b - github.com/golang/protobuf v1.5.0 + github.com/golang/protobuf v1.5.2 github.com/gorilla/websocket v1.4.2 github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect @@ -32,8 +32,8 @@ require ( github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e github.com/prometheus/client_golang v1.8.0 github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a - golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 - google.golang.org/protobuf v1.26.0-rc.1 + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 + google.golang.org/protobuf v1.26.0 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect ) @@ -43,6 +43,7 @@ require ( github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcd v0.20.1-beta // indirect + github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -52,6 +53,7 @@ require ( github.com/decred/dcrd/crypto/ripemd160 v1.0.1 // indirect github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/decred/dcrd/wire v1.4.0 // indirect github.com/decred/slog v1.1.0 // indirect github.com/go-ole/go-ole v1.2.1 // indirect @@ -65,7 +67,7 @@ require ( github.com/stretchr/testify v1.8.0 // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect - golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index a27d52f4b3..fc6b56937c 100644 --- a/go.sum +++ b/go.sum @@ -1,43 +1,9 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= -github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= -github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c h1:8bYNmjELeCj7DEh/dN7zFzkJ0upK3GkbOC/0u1HMQ5s= github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c/go.mod h1:DwgC62sAn4RgH4L+O8REgcE7f0XplHPNeRYFy+ffy1M= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29 h1:B11BryeZQ1LrAzzM0lCpblwleB7SyxPfvN2AsNbyvQc= github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29/go.mod h1:+39XiGr9m9TPY49sG4XIH5CVaRxHGFWT0U4MOY6dy3o= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= @@ -45,21 +11,16 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= -github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -69,26 +30,17 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= -github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= -github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= -github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= -github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= -github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= -github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e h1:D64GF/Xr5zSUnM3q1Jylzo4sK7szhP/ON+nb2DB5XJA= github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k= github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= @@ -101,33 +53,21 @@ github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= -github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= -github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -136,9 +76,8 @@ github.com/dchest/blake256 v1.0.0 h1:6gUgI5MHdz9g0TdrgKqXsoDX+Zjxmm1Sc6OsoGru50I github.com/dchest/blake256 v1.0.0/go.mod h1:xXNWCE1jsAP8DAjP+rKw2MbeqLczjI3TRx2VK+9OEYY= github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= -github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= -github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= -github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/decred/base58 v1.0.3 h1:KGZuh8d1WEMIrK0leQRM47W85KqCAdl2N+uagbctdDI= github.com/decred/base58 v1.0.3/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbiSM78E= github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU= @@ -155,6 +94,8 @@ github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1 h1:V6eqU1crZzuoFT4KG2LhaU5xDSdkHu github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1/go.mod h1:d0H8xGMWbiIQP7gN3v2rByWUcuZPm9YsgmnfoxgbINc= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 h1:sgNeV1VRMDzs6rzyPpxyM0jp317hnwiq58Filgag2xw= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrjson/v3 v3.0.1 h1:b9cpplNJG+nutE2jS8K/BtSGIJihEQHhFjFAsvJF/iI= github.com/decred/dcrd/dcrjson/v3 v3.0.1/go.mod h1:fnTHev/ABGp8IxFudDhjGi9ghLiXRff1qZz/wvq12Mg= github.com/decred/dcrd/dcrutil/v3 v3.0.0 h1:n6uQaTQynIhCY89XsoDk2WQqcUcnbD+zUM9rnZcIOZo= @@ -168,46 +109,25 @@ github.com/decred/dcrd/wire v1.4.0 h1:KmSo6eTQIvhXS0fLBQ/l7hG7QLcSJQKSwSyzSqJYDk github.com/decred/dcrd/wire v1.4.0/go.mod h1:WxC/0K+cCAnBh+SKsRjIX9YPgvrjhmE+6pZlel1G7Ro= github.com/decred/slog v1.1.0 h1:uz5ZFfmaexj1rEDgZvzQ7wjGkoSPjw2LCh8K+K1VrW4= github.com/decred/slog v1.1.0/go.mod h1:kVXlGnt6DHy2fV5OjSeuvCJ0OmlmTF6LFpEPMu/fOY0= -github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= -github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.10.15 h1:E9o0kMbD8HXhp7g6UwIwntY05WTDheCGziMhegcBsQw= -github.com/ethereum/go-ethereum v1.10.15/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw= +github.com/ethereum/go-ethereum v1.10.23 h1:Xk8XAT4/UuqcjMLIMF+7imjkg32kfVFKoeyQDaO2yWM= +github.com/ethereum/go-ethereum v1.10.23/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= -github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -216,29 +136,19 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= +github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -249,45 +159,30 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I= -github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -295,7 +190,6 @@ github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoP github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= -github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= @@ -310,42 +204,22 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= -github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= -github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/huin/goupnp v1.0.2 h1:RfGLP+h3mvisuWEyybxNq5Eft3NWhHLPeUN72kpKZoI= -github.com/huin/goupnp v1.0.2/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= -github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= -github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= -github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= -github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= -github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= -github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= -github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA= -github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -353,9 +227,6 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 h1:d2hBkTvi7B89+OXY8+bBBshPlc+7JYacGrG/dFak8SQ= github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= @@ -365,40 +236,23 @@ github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b h1:Rrp0ByJXEjhREMPGTt github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= -github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= -github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/linxGnu/grocksdb v1.7.7 h1:b6o8gagb4FL+P55qUzPchBR/C0u1lWjJOWQSWbhvTWg= github.com/linxGnu/grocksdb v1.7.7/go.mod h1:0hTf+iA+GOr0jDX4CgIYyJZxqOH9XlBh6KVj8+zmF34= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe h1:khZWpHuxJNh2EGzBbaS6EQ2d6KxgK31WeG0TnlTMUD4= github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe/go.mod h1:0hw4tpGU+9slqN/DrevhjTMb0iR9esxzpCdx8I6/UzU= github.com/martinboehm/btcd v0.0.0-20190104121910-8e7c0427fee5/go.mod h1:rKQj/jGwFruYjpM6vN+syReFoR0DsLQaajhyH/5mwUE= @@ -411,26 +265,13 @@ github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 h1:ra2UymMEDhR github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819/go.mod h1:/Z9FhVDXTih0kZExhK2hRvM+z68XkmbqZhFDU3bU1jY= github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde h1:Tz7WkXgQjeQVymqSQkEapbe/ZuzKCvb6GANFHnl0uAE= github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde/go.mod h1:p35TWcm7GkAwvPcUCEq4H+yTm0gA8Aq7UvGnbK6olQk= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -442,20 +283,15 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= -github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= @@ -463,29 +299,20 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= @@ -493,14 +320,10 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pebbe/zmq4 v1.2.1 h1:jrXQW3mD8Si2mcSY/8VBs2nNkK/sKCOEM0rHAfxyc8c= github.com/pebbe/zmq4 v1.2.1/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29 h1:awILOeL107zIYvPB1zhkz6ZTp0AaMpLGMoV16DMairA= @@ -512,7 +335,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -530,10 +352,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4= @@ -546,24 +366,18 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a h1:q2+wHBv8gDQRRPfxvRez8etJUp9VNnBDQhiUW4W5AKg= github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a/go.mod h1:FdhEqBlgflrdbBs+Wh94EXSNJT+s6DTVvsHGMo0+u80= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -574,61 +388,44 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4= -github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -636,44 +433,20 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -687,39 +460,21 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -729,140 +484,57 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 h1:uCLL3g5wH2xjxVREVuAbP9JM5PPKjRbXKRa6IBjkzmU= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -873,51 +545,40 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= -gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= From 60df986fb80396d24141cac666b439d59caf4b0c Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 1 Sep 2022 20:55:54 +0200 Subject: [PATCH 077/530] Alter memory stats logging levels --- bchain/coins/eth/ethrpc.go | 4 ++-- blockbook.go | 4 +++- db/sync.go | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 001a199cc6..986ec9990a 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -544,7 +544,7 @@ func (b *EthereumRPC) processEventsForBlock(blockNumber string) (map[string][]*b "toBlock": blockNumber, }) if err != nil { - return nil, nil, errors.Annotatef(err, "blockNumber %v", blockNumber) + return nil, nil, errors.Annotatef(err, "eth_getLogs blockNumber %v", blockNumber) } r := make(map[string][]*bchain.RpcLog) for i := range logs { @@ -708,7 +708,7 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error blockSpecificData = &bchain.EthereumBlockSpecificData{} if err != nil { blockSpecificData.InternalDataError = err.Error() - glog.Info("InternalDataError ", bbh.Height, ": ", err.Error()) + // glog.Info("InternalDataError ", bbh.Height, ": ", err.Error()) } if len(ens) > 0 { blockSpecificData.AddressAliasRecords = ens diff --git a/blockbook.go b/blockbook.go index 37db252744..7782c1a029 100644 --- a/blockbook.go +++ b/blockbook.go @@ -583,7 +583,9 @@ func storeInternalStateLoop() { glog.Error("storeInternalStateLoop ", errors.ErrorStack(err)) } if lastAppInfo.Add(logAppInfoPeriod).Before(time.Now()) { - glog.Info(index.GetMemoryStats()) + if glog.V(1) { + glog.Info(index.GetMemoryStats()) + } if err := blockbookAppInfoMetric(index, chain, txCache, internalState, metrics); err != nil { glog.Error("blockbookAppInfoMetric ", err) } diff --git a/db/sync.go b/db/sync.go index 823ec9aa09..f04512ec10 100644 --- a/db/sync.go +++ b/db/sync.go @@ -386,7 +386,9 @@ ConnectLoop: start = time.Now() } if msTime.Before(time.Now()) { - glog.Info(w.db.GetMemoryStats()) + if glog.V(1) { + glog.Info(w.db.GetMemoryStats()) + } w.metrics.IndexDBSize.Set(float64(w.db.DatabaseSizeOnDisk())) msTime = time.Now().Add(10 * time.Minute) } From 9ce6955c2aad4591921f3df4f1ea97c1cb665760 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 1 Sep 2022 22:23:33 +0200 Subject: [PATCH 078/530] Fix ETH Goerli Archive: websocket: read limit exceeded Geth sets wsMessageSizeLimit to 15M. However, Goerli contains blocks (e.g. 6109494) which require larger limit to fetch the debug_traceBlockByHash response. Fixed by a hacky way of modifying the geth source before the build of the project. Will submit PR to go-ethereum with a final fix. --- build/docker/bin/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/build/docker/bin/Makefile b/build/docker/bin/Makefile index ebe713e47e..c73045ce69 100644 --- a/build/docker/bin/Makefile +++ b/build/docker/bin/Makefile @@ -38,3 +38,4 @@ prepare-sources: mkdir -p $(BLOCKBOOK_BASE) cp -r /src $(BLOCKBOOK_SRC) cd $(BLOCKBOOK_SRC) && go mod download + sed -i 's/wsMessageSizeLimit\ =\ 15\ \*\ 1024\ \*\ 1024/wsMessageSizeLimit = 50 * 1024 * 1024/g' $(GOPATH)/pkg/mod/github.com/ethereum/go-ethereum*/rpc/websocket.go From c8f5ee9845043d5cad2168961851f448c55a4ed1 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 5 Sep 2022 23:37:35 +0200 Subject: [PATCH 079/530] Bump ethereum consensus layer Prysm to v3.1.0 --- configs/coins/ethereum_archive_consensus.json | 6 +++--- configs/coins/ethereum_consensus.json | 6 +++--- .../coins/ethereum_testnet_goerli_archive_consensus.json | 6 +++--- configs/coins/ethereum_testnet_goerli_consensus.json | 6 +++--- .../coins/ethereum_testnet_ropsten_archive_consensus.json | 6 +++--- configs/coins/ethereum_testnet_ropsten_consensus.json | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 93dd1d578b..164a1fa60b 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", + "version": "3.1.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", + "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 6d969619b2..0bb21357a3 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", + "version": "3.1.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", + "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli_archive_consensus.json b/configs/coins/ethereum_testnet_goerli_archive_consensus.json index 52db9f004d..608546566a 100644 --- a/configs/coins/ethereum_testnet_goerli_archive_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-goerli-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", + "version": "3.1.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", + "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17506 --rpc-port=17507 --monitoring-port=17508 --p2p-tcp-port=13506 --p2p-udp-port=12506 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli_consensus.json b/configs/coins/ethereum_testnet_goerli_consensus.json index 0491d85cf2..831a47aaf5 100644 --- a/configs/coins/ethereum_testnet_goerli_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-goerli-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", + "version": "3.1.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", + "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17526 --rpc-port=17527 --monitoring-port=17528 --p2p-tcp-port=13526 --p2p-udp-port=12526 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json index 43171dbe90..a0b8100f7a 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-ropsten-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", + "version": "3.1.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", + "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_ropsten_consensus.json b/configs/coins/ethereum_testnet_ropsten_consensus.json index 8220aff5c4..f457063a63 100644 --- a/configs/coins/ethereum_testnet_ropsten_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-ropsten-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.0.0/beacon-chain-v3.0.0-linux-amd64", + "version": "3.1.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "8653f204f1c60363eba85cb9ef49e12293e4932c0b848e4958b19330a06359f6", + "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13536 --p2p-udp-port=12536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From 3f8bcd1de60c86be43ec96f2e9e7ed9f9e471d98 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 6 Sep 2022 22:23:45 +0200 Subject: [PATCH 080/530] Update fiat rates download parameters in coin configs --- configs/coins/bcash.json | 6 +- configs/coins/bgold.json | 6 +- configs/coins/bgold_testnet.json | 154 +++++++++++++--------------- configs/coins/bitcoin.json | 6 +- configs/coins/bitcore.json | 10 +- configs/coins/dash.json | 6 +- configs/coins/digibyte.json | 6 +- configs/coins/dogecoin.json | 6 +- configs/coins/ecash.json | 6 +- configs/coins/ethereum-classic.json | 8 +- configs/coins/ethereum.json | 2 +- configs/coins/ethereum_archive.json | 2 +- configs/coins/groestlcoin.json | 6 +- configs/coins/litecoin.json | 6 +- configs/coins/monacoin.json | 6 +- configs/coins/namecoin.json | 10 +- configs/coins/omotenashicoin.json | 134 ++++++++++++------------ configs/coins/trezarcoin.json | 6 +- configs/coins/vertcoin.json | 6 +- configs/coins/zcash.json | 6 +- 20 files changed, 182 insertions(+), 216 deletions(-) diff --git a/configs/coins/bcash.json b/configs/coins/bcash.json index fc24971724..ac048c7073 100644 --- a/configs/coins/bcash.json +++ b/configs/coins/bcash.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "e32e05fd63161f6f1fe717fca789448d2ee48e2017d3d4c6686b4222fe69497e", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/bitcoin-qt" - ], + "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -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": "", @@ -58,7 +56,7 @@ "slip44": 145, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin-cash\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin-cash\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/bgold.json b/configs/coins/bgold.json index 13a445c19f..cdd0384278 100644 --- a/configs/coins/bgold.json +++ b/configs/coins/bgold.json @@ -27,9 +27,7 @@ "verification_type": "gpg-sha256", "verification_source": "https://github.com/BTCGPU/BTCGPU/releases/download/v0.17.3/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/bitcoin-qt" - ], + "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bgoldd -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": "", @@ -254,7 +252,7 @@ "slip44": 156, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin-gold\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin-gold\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/bgold_testnet.json b/configs/coins/bgold_testnet.json index d31a6ac130..0038f87a4f 100644 --- a/configs/coins/bgold_testnet.json +++ b/configs/coins/bgold_testnet.json @@ -1,84 +1,78 @@ { - "coin": { - "name": "Bgold Testnet", - "shortcut": "TBTG", - "label": "Bitcoin Gold Testnet", - "alias": "bgold_testnet" - }, - "ports": { - "backend_rpc": 18035, - "backend_message_queue": 48335, - "blockbook_internal": 19035, - "blockbook_public": 19135 - }, - "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-bgold-testnet", - "package_revision": "satoshilabs-1", - "system_user": "bgold", - "version": "0.17.3", - "binary_url": "https://github.com/BTCGPU/BTCGPU/releases/download/v0.17.3/bitcoin-gold-0.17.3-x86_64-linux-gnu.tar.gz", - "verification_type": "gpg-sha256", - "verification_source": "https://github.com/BTCGPU/BTCGPU/releases/download/v0.17.3/SHA256SUMS.asc", - "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/bitcoin-qt" + "coin": { + "name": "Bgold Testnet", + "shortcut": "TBTG", + "label": "Bitcoin Gold Testnet", + "alias": "bgold_testnet" + }, + "ports": { + "backend_rpc": 18035, + "backend_message_queue": 48335, + "blockbook_internal": 19035, + "blockbook_public": 19135 + }, + "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-bgold-testnet", + "package_revision": "satoshilabs-1", + "system_user": "bgold", + "version": "0.17.3", + "binary_url": "https://github.com/BTCGPU/BTCGPU/releases/download/v0.17.3/bitcoin-gold-0.17.3-x86_64-linux-gnu.tar.gz", + "verification_type": "gpg-sha256", + "verification_source": "https://github.com/BTCGPU/BTCGPU/releases/download/v0.17.3/SHA256SUMS.asc", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": ["bin/bitcoin-qt"], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bgoldd -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/testnet/*.log", + "postinst_script_template": "", + "service_type": "forking", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "bitcoin_like.conf", + "client_config_file": "bitcoin_like_client.conf", + "additional_params": { + "addnode": [ + "136.243.230.235:18338", + "167.179.114.118:18338", + "51.15.140.154:18338", + "62.141.35.88:18338", + "71.172.96.60:18338", + "8.39.234.187:18338" ], - "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bgoldd -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/testnet/*.log", - "postinst_script_template": "", - "service_type": "forking", - "service_additional_params_template": "", - "protect_memory": true, - "mainnet": false, - "server_config_file": "bitcoin_like.conf", - "client_config_file": "bitcoin_like_client.conf", - "additional_params": { - "addnode": [ - "136.243.230.235:18338", - "167.179.114.118:18338", - "51.15.140.154:18338", - "62.141.35.88:18338", - "71.172.96.60:18338", - "8.39.234.187:18338" - ], - "maxconnections": 250, - "mempoolexpiry": 72, - "timeout": 768 - } - }, - "blockbook": { - "package_name": "blockbook-bgold-testnet", - "system_user": "blockbook-bgold", - "internal_binding_template": ":{{.Ports.BlockbookInternal}}", - "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "", - "additional_params": "", - "block_chain": { - "parse": true, - "subversion": "/Bitcoin Gold:0.17.3/", - "mempool_workers": 8, - "mempool_sub_workers": 2, - "block_addresses_to_keep": 300, - "xpub_magic": 70617039, - "xpub_magic_segwit_p2sh": 71979618, - "xpub_magic_segwit_native": 73342198, - "slip44": 156, - "additional_params": { - "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin-gold\", \"periodSeconds\": 60}" - } - } - }, - "meta": { - "package_maintainer": "Martin Kuvandzhiev", - "package_maintainer_email": "martin@bitcoingold.org" + "maxconnections": 250, + "mempoolexpiry": 72, + "timeout": 768 } + }, + "blockbook": { + "package_name": "blockbook-bgold-testnet", + "system_user": "blockbook-bgold", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "subversion": "/Bitcoin Gold:0.17.3/", + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "xpub_magic": 70617039, + "xpub_magic_segwit_p2sh": 71979618, + "xpub_magic_segwit_native": 73342198, + "slip44": 156, + "additional_params": {} + } + }, + "meta": { + "package_maintainer": "Martin Kuvandzhiev", + "package_maintainer_email": "martin@bitcoingold.org" } - +} diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index 569211f16f..2c75a9e54c 100644 --- a/configs/coins/bitcoin.json +++ b/configs/coins/bitcoin.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "49df6e444515d457ea0b885d66f521f2a26ca92ccf73d5296082e633544253bf", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/bitcoin-qt" - ], + "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -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": "", @@ -62,7 +60,7 @@ "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}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/bitcore.json b/configs/coins/bitcore.json index 8cf72d54a1..a835b65e27 100644 --- a/configs/coins/bitcore.json +++ b/configs/coins/bitcore.json @@ -1,9 +1,9 @@ { "coin": { - "name": "Bitcore", - "shortcut": "BTX", - "label": "Bitcore", - "alias": "bitcore" + "name": "Bitcore", + "shortcut": "BTX", + "label": "Bitcore", + "alias": "bitcore" }, "ports": { "backend_rpc": 8054, @@ -60,7 +60,7 @@ "block_addresses_to_keep": 300, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcore\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcore\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/dash.json b/configs/coins/dash.json index 414211897e..2e955f0269 100644 --- a/configs/coins/dash.json +++ b/configs/coins/dash.json @@ -27,9 +27,7 @@ "verification_type": "gpg-sha256", "verification_source": "https://github.com/dashpay/dash/releases/download/v18.2.1/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/dash-qt" - ], + "exclude_files": ["bin/dash-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/dashd -deprecatedrpc=estimatefee -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": "", @@ -60,7 +58,7 @@ "slip44": 5, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"dash\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"dash\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/digibyte.json b/configs/coins/digibyte.json index 4c4fa237a7..f82fc3e52e 100644 --- a/configs/coins/digibyte.json +++ b/configs/coins/digibyte.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "b5cd8f590d359e4846dd5cbe60751221e54d773a6227ea9686d17c4890057f46", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/digibyte-qt" - ], + "exclude_files": ["bin/digibyte-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/digibyted -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": "", @@ -61,7 +59,7 @@ "slip44": 20, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"digibyte\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"digibyte\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/dogecoin.json b/configs/coins/dogecoin.json index 8a5fb3cb41..cc9464aa4a 100644 --- a/configs/coins/dogecoin.json +++ b/configs/coins/dogecoin.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "fe9c9cdab946155866a5bd5a5127d2971a9eed3e0b65fb553fe393ad1daaebb0", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/dogecoin-qt" - ], + "exclude_files": ["bin/dogecoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/dogecoind -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": "", @@ -62,7 +60,7 @@ "slip44": 3, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"dogecoin\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"dogecoin\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/ecash.json b/configs/coins/ecash.json index ec51d13d47..e9f967b548 100644 --- a/configs/coins/ecash.json +++ b/configs/coins/ecash.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "64c799b339b2aa03f50ac605f7df0586341ff5a2d74321b424f4fe35d37da0be", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/bitcoin-qt" - ], + "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -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": "", @@ -62,7 +60,7 @@ "slip44": 899, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ecash\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ecash\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/ethereum-classic.json b/configs/coins/ethereum-classic.json index 4236df8a47..6dd3c561fd 100644 --- a/configs/coins/ethereum-classic.json +++ b/configs/coins/ethereum-classic.json @@ -8,6 +8,8 @@ "ports": { "backend_rpc": 8037, "backend_message_queue": 0, + "backend_p2p": 38337, + "backend_http": 8137, "blockbook_internal": 9037, "blockbook_public": 9137 }, @@ -25,7 +27,7 @@ "verification_source": "91e8834b01e89aaea7b89a70cb005b527ab7815f17ce123229733aa49ff95ec3", "extract_command": "unzip -d backend", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --classic --ipcdisable --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38337 --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --http --http.port 8137 --http.addr 127.0.0.1 --http.corsdomain \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --classic --ipcdisable --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -48,10 +50,12 @@ "mempool_sub_workers": 2, "block_addresses_to_keep": 10000, "additional_params": { + "address_aliases": true, "mempoolTxTimeoutHours": 48, "queryBackendOnMempoolResync": true, "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum-classic\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum-classic\", \"periodSeconds\": 900}", + "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } }, diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 7a11de2be9..2fa9165277 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -28,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index c03bbbc160..5bf407f860 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -28,7 +28,7 @@ "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", diff --git a/configs/coins/groestlcoin.json b/configs/coins/groestlcoin.json index ffb7fe1d3d..a11f913a64 100644 --- a/configs/coins/groestlcoin.json +++ b/configs/coins/groestlcoin.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "4b69743190e2697d7b7772bf6f63cde595d590ff6664abf15a7201dab2a6098b", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/groestlcoin-qt" - ], + "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -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": "", @@ -62,7 +60,7 @@ "slip44": 17, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"groestlcoin\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"groestlcoin\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/litecoin.json b/configs/coins/litecoin.json index 87ed3cf4e7..6684508a34 100644 --- a/configs/coins/litecoin.json +++ b/configs/coins/litecoin.json @@ -27,9 +27,7 @@ "verification_type": "gpg", "verification_source": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-x86_64-linux-gnu.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/litecoin-qt" - ], + "exclude_files": ["bin/litecoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/litecoind -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": "", @@ -61,7 +59,7 @@ "slip44": 2, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"litecoin\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"litecoin\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/monacoin.json b/configs/coins/monacoin.json index 9b4fa96a61..6b274e9a09 100644 --- a/configs/coins/monacoin.json +++ b/configs/coins/monacoin.json @@ -27,9 +27,7 @@ "verification_type": "gpg-sha256", "verification_source": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.3/monacoin-0.20.3-signatures.asc", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/monacoin-qt" - ], + "exclude_files": ["bin/monacoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/monacoind -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": "", @@ -61,7 +59,7 @@ "slip44": 22, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"monacoin\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"monacoin\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/namecoin.json b/configs/coins/namecoin.json index 58e66f6b15..aff9fc08b4 100644 --- a/configs/coins/namecoin.json +++ b/configs/coins/namecoin.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "1e7f06030881fac5b8a6d33f497f1cab9a120189741ec81bc21e58d5cd93fa6f", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/namecoin-qt" - ], + "exclude_files": ["bin/namecoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/namecoind -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": "", @@ -40,9 +38,7 @@ "server_config_file": "bitcoin_like.conf", "client_config_file": "bitcoin_like_client.conf", "additional_params": { - "addnode": [ - "45.24.110.177:8334" - ], + "addnode": ["45.24.110.177:8334"], "discover": 0, "listenonion": 0, "upnp": 0, @@ -66,7 +62,7 @@ "slip44": 7, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"namecoin\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"namecoin\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/omotenashicoin.json b/configs/coins/omotenashicoin.json index 4d3c9b3cd8..52a9a7f408 100644 --- a/configs/coins/omotenashicoin.json +++ b/configs/coins/omotenashicoin.json @@ -1,70 +1,68 @@ { - "coin": { - "name": "Omotenashicoin", - "shortcut": "MTNS", - "label": "Omotenashicoin", - "alias": "omotenashicoin" - }, - "ports": { - "blockbook_internal": 9094, - "blockbook_public": 9194, - "backend_rpc": 8094, - "backend_message_queue": 38394 - }, - "ipc": { - "rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}", - "rpc_user": "rpc", - "rpc_pass": "mtnsrpc", - "rpc_timeout": 25, - "message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}" - }, - "backend": { - "package_name": "backend-mtns", - "package_revision": "satoshilabs-1", - "system_user": "mtns", - "version": "1.7.3", - "binary_url": "https://github.com/omotenashicoin-project/OmotenashiCoin-HDwalletbinaries/raw/master/stable/omotenashicoin-x86_64-linux-gnu.tar.gz", - "verification_type": "", - "verification_source": "", - "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/omotenashicoin-qt" - ], - "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/omotenashicoind -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": false, - "mainnet": true, - "server_config_file": "bitcoin_like.conf", - "client_config_file": "bitcoin_like_client.conf", - "additional_params": { - "whitelist": "127.0.0.1" - } - }, - "blockbook": { - "package_name": "blockbook-mtns", - "system_user": "blockbook-mtns", - "internal_binding_template": ":{{.Ports.BlockbookInternal}}", - "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "", - "additional_params": "", - "block_chain": { - "parse": true, - "mempool_workers": 8, - "mempool_sub_workers": 2, - "block_addresses_to_keep": 300, - "xpub_magic": 61052245, - "slip44": 341, - "additional_params": { - "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"omotenashicoin\", \"periodSeconds\": 60}" - } - } - }, - "meta": { - "package_maintainer": "omotenashicoin dev", - "package_maintainer_email": "git@omotenashicoin.site" - } + "coin": { + "name": "Omotenashicoin", + "shortcut": "MTNS", + "label": "Omotenashicoin", + "alias": "omotenashicoin" + }, + "ports": { + "blockbook_internal": 9094, + "blockbook_public": 9194, + "backend_rpc": 8094, + "backend_message_queue": 38394 + }, + "ipc": { + "rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_user": "rpc", + "rpc_pass": "mtnsrpc", + "rpc_timeout": 25, + "message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}" + }, + "backend": { + "package_name": "backend-mtns", + "package_revision": "satoshilabs-1", + "system_user": "mtns", + "version": "1.7.3", + "binary_url": "https://github.com/omotenashicoin-project/OmotenashiCoin-HDwalletbinaries/raw/master/stable/omotenashicoin-x86_64-linux-gnu.tar.gz", + "verification_type": "", + "verification_source": "", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": ["bin/omotenashicoin-qt"], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/omotenashicoind -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": false, + "mainnet": true, + "server_config_file": "bitcoin_like.conf", + "client_config_file": "bitcoin_like_client.conf", + "additional_params": { + "whitelist": "127.0.0.1" + } + }, + "blockbook": { + "package_name": "blockbook-mtns", + "system_user": "blockbook-mtns", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "xpub_magic": 61052245, + "slip44": 341, + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"omotenashicoin\", \"periodSeconds\": 900}" + } + } + }, + "meta": { + "package_maintainer": "omotenashicoin dev", + "package_maintainer_email": "git@omotenashicoin.site" + } } diff --git a/configs/coins/trezarcoin.json b/configs/coins/trezarcoin.json index c0b7ec3bb1..e20cccf7e1 100644 --- a/configs/coins/trezarcoin.json +++ b/configs/coins/trezarcoin.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "4b41c4fecf36a870d6bb7298d85b211f61d9f2bcc6c1bef3167f3ef772bc6fdf", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/trezarcoin-qt" - ], + "exclude_files": ["bin/trezarcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/trezarcoind -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": "", @@ -59,7 +57,7 @@ "slip44": 232, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"trezarcoin\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"trezarcoin\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/vertcoin.json b/configs/coins/vertcoin.json index eb81187d4d..74726839dc 100644 --- a/configs/coins/vertcoin.json +++ b/configs/coins/vertcoin.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "aab3068e02d55128326801cdbcbfcb175be96291e024edf5ab12f3af6f4433c0", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/vertcoin-qt" - ], + "exclude_files": ["bin/vertcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/vertcoind -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": "", @@ -61,7 +59,7 @@ "slip44": 28, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"vertcoin\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"vertcoin\", \"periodSeconds\": 900}" } } }, diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index 368018424e..eae0ecd92a 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -38,9 +38,7 @@ "server_config_file": "bitcoin_like.conf", "client_config_file": "bitcoin_like_client.conf", "additional_params": { - "addnode": [ - "mainnet.z.cash" - ] + "addnode": ["mainnet.z.cash"] } }, "blockbook": { @@ -59,7 +57,7 @@ "slip44": 133, "additional_params": { "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"zcash\", \"periodSeconds\": 60}" + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"zcash\", \"periodSeconds\": 900}" } } }, From f485de5e3565a873d4179bfc379ebc32d8b1f4dc Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 9 Sep 2022 17:04:02 +0200 Subject: [PATCH 081/530] Set consensus version as subversion in metrics --- blockbook.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/blockbook.go b/blockbook.go index 7782c1a029..663fa5092f 100644 --- a/blockbook.go +++ b/blockbook.go @@ -458,13 +458,19 @@ func blockbookAppInfoMetric(db *db.RocksDB, chain bchain.BlockChain, txCache *db if err != nil { return err } + subversion := si.Backend.Subversion + if subversion == "" { + // for coins without subversion (ETH) use ConsensusVersion as subversion in metrics + subversion = si.Backend.ConsensusVersion + } + metrics.BlockbookAppInfo.Reset() metrics.BlockbookAppInfo.With(common.Labels{ "blockbook_version": si.Blockbook.Version, "blockbook_commit": si.Blockbook.GitCommit, "blockbook_buildtime": si.Blockbook.BuildTime, "backend_version": si.Backend.Version, - "backend_subversion": si.Backend.Subversion, + "backend_subversion": subversion, "backend_protocol_version": si.Backend.ProtocolVersion}).Set(float64(0)) metrics.BackendBestHeight.Set(float64(si.Backend.Blocks)) metrics.BlockbookBestHeight.Set(float64(si.Blockbook.BestHeight)) From 91c4675a533e46633793603d7f02329d4c7e1366 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 10 Sep 2022 12:16:25 +0200 Subject: [PATCH 082/530] Give info about fiat rates in status page and API --- api/types.go | 41 +++++++++++++++++------------- api/worker.go | 50 ++++++++++++++++++++++++------------- common/internalstate.go | 7 ++++++ db/rocksdb.go | 5 ++++ fiat/fiat_rates.go | 38 +++++++++++++++++++++------- server/public.go | 8 ++++-- static/templates/index.html | 10 ++++++++ 7 files changed, 112 insertions(+), 47 deletions(-) diff --git a/api/types.go b/api/types.go index c44bab2061..99203f1662 100644 --- a/api/types.go +++ b/api/types.go @@ -431,24 +431,29 @@ type BlockRaw struct { // BlockbookInfo contains information about the running blockbook instance type BlockbookInfo struct { - Coin string `json:"coin"` - Host string `json:"host"` - Version string `json:"version"` - GitCommit string `json:"gitCommit"` - BuildTime string `json:"buildTime"` - SyncMode bool `json:"syncMode"` - InitialSync bool `json:"initialSync"` - InSync bool `json:"inSync"` - BestHeight uint32 `json:"bestHeight"` - LastBlockTime time.Time `json:"lastBlockTime"` - InSyncMempool bool `json:"inSyncMempool"` - LastMempoolTime time.Time `json:"lastMempoolTime"` - MempoolSize int `json:"mempoolSize"` - Decimals int `json:"decimals"` - DbSize int64 `json:"dbSize"` - DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty"` - DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty"` - About string `json:"about"` + Coin string `json:"coin"` + Host string `json:"host"` + Version string `json:"version"` + GitCommit string `json:"gitCommit"` + BuildTime string `json:"buildTime"` + SyncMode bool `json:"syncMode"` + InitialSync bool `json:"initialSync"` + InSync bool `json:"inSync"` + BestHeight uint32 `json:"bestHeight"` + LastBlockTime time.Time `json:"lastBlockTime"` + InSyncMempool bool `json:"inSyncMempool"` + LastMempoolTime time.Time `json:"lastMempoolTime"` + MempoolSize int `json:"mempoolSize"` + Decimals int `json:"decimals"` + DbSize int64 `json:"dbSize"` + HasFiatRates bool `json:"hasFiatRates,omitempty"` + HasTokenFiatRates bool `json:"hasTokenFiatRates,omitempty"` + CurrentFiatRatesTime *time.Time `json:"currentFiatRatesTime,omitempty"` + HistoricalFiatRatesTime *time.Time `json:"historicalFiatRatesTime,omitempty"` + HistoricalTokenFiatRatesTime *time.Time `json:"historicalTokenFiatRatesTime,omitempty"` + DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty"` + DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty"` + About string `json:"about"` } // SystemInfo contains information about the running blockbook and backend instance diff --git a/api/worker.go b/api/worker.go index f95ca93ee4..03809bb58d 100644 --- a/api/worker.go +++ b/api/worker.go @@ -134,9 +134,11 @@ func aggregateAddresses(m map[string]struct{}, addresses []string, isAddress boo } func (w *Worker) newAddressesMapForAliases() map[string]struct{} { + // return non nil map only if the chain supports address aliases if w.useAddressAliases { return make(map[string]struct{}) } + // returning nil disables the processing of the address aliases return nil } @@ -2034,6 +2036,13 @@ func (w *Worker) ComputeFeeStats(blockFrom, blockTo int, stopCompute chan os.Sig return nil } +func nonZeroTime(t time.Time) *time.Time { + if t.IsZero() { + return nil + } + return &t +} + // GetSystemInfo returns information about system func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) { start := time.Now() @@ -2057,24 +2066,29 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) { internalDBSize = w.is.DBSizeTotal() } blockbookInfo := &BlockbookInfo{ - Coin: w.is.Coin, - Host: w.is.Host, - Version: vi.Version, - GitCommit: vi.GitCommit, - BuildTime: vi.BuildTime, - SyncMode: w.is.SyncMode, - InitialSync: w.is.InitialSync, - InSync: inSync, - BestHeight: bestHeight, - LastBlockTime: lastBlockTime, - InSyncMempool: inSyncMempool, - LastMempoolTime: lastMempoolTime, - MempoolSize: mempoolSize, - Decimals: w.chainParser.AmountDecimals(), - DbSize: w.db.DatabaseSizeOnDisk(), - DbSizeFromColumns: internalDBSize, - DbColumns: columnStats, - About: Text.BlockbookAbout, + Coin: w.is.Coin, + Host: w.is.Host, + Version: vi.Version, + GitCommit: vi.GitCommit, + BuildTime: vi.BuildTime, + SyncMode: w.is.SyncMode, + InitialSync: w.is.InitialSync, + InSync: inSync, + BestHeight: bestHeight, + LastBlockTime: lastBlockTime, + InSyncMempool: inSyncMempool, + LastMempoolTime: lastMempoolTime, + MempoolSize: mempoolSize, + Decimals: w.chainParser.AmountDecimals(), + HasFiatRates: w.is.HasFiatRates, + HasTokenFiatRates: w.is.HasTokenFiatRates, + CurrentFiatRatesTime: nonZeroTime(w.is.CurrentFiatRatesTime), + HistoricalFiatRatesTime: nonZeroTime(w.is.HistoricalFiatRatesTime), + HistoricalTokenFiatRatesTime: nonZeroTime(w.is.HistoricalTokenFiatRatesTime), + DbSize: w.db.DatabaseSizeOnDisk(), + DbSizeFromColumns: internalDBSize, + DbColumns: columnStats, + About: Text.BlockbookAbout, } backendInfo := &common.BackendInfo{ BackendError: backendError, diff --git a/common/internalstate.go b/common/internalstate.go index a7090c80bd..6be529ece5 100644 --- a/common/internalstate.go +++ b/common/internalstate.go @@ -77,6 +77,13 @@ type InternalState struct { UtxoChecked bool `json:"utxoChecked"` + // store only the historical state, not the current state of the fiat rates in DB + HasFiatRates bool `json:"-"` + HasTokenFiatRates bool `json:"-"` + CurrentFiatRatesTime time.Time `json:"-"` + HistoricalFiatRatesTime time.Time `json:"historicalFiatRatesTime"` + HistoricalTokenFiatRatesTime time.Time `json:"historicalTokenFiatRatesTime"` + BackendInfo BackendInfo `json:"-"` } diff --git a/db/rocksdb.go b/db/rocksdb.go index 5f72e6b7be..84bdaa6b7f 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -1740,6 +1740,11 @@ func (d *RocksDB) SetInternalState(is *common.InternalState) { d.is = is } +// GetInternalState gets the InternalState +func (d *RocksDB) GetInternalState() *common.InternalState { + return d.is +} + // StoreInternalState stores the internal state to db func (d *RocksDB) StoreInternalState(is *common.InternalState) error { if d.metrics != nil { diff --git a/fiat/fiat_rates.go b/fiat/fiat_rates.go index 37e0d8350d..f7a4ac3611 100644 --- a/fiat/fiat_rates.go +++ b/fiat/fiat_rates.go @@ -28,6 +28,7 @@ type RatesDownloader struct { timeFormat string callbackOnNewTicker OnNewFiatRatesTicker downloader RatesDownloaderInterface + downloadTokens bool } // NewFiatRatesDownloader initializes the downloader for FiatRates API. @@ -55,6 +56,8 @@ func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, callb } rd.db = db rd.callbackOnNewTicker = callback + rd.downloadTokens = rdParams.PlatformIdentifier != "" && rdParams.PlatformVsCurrency != "" + is := rd.db.GetInternalState() if apiType == "coingecko" { throttle := true if callback == nil { @@ -62,6 +65,11 @@ func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, callb throttle = false } rd.downloader = NewCoinGeckoDownloader(db, rdParams.URL, rdParams.Coin, rdParams.PlatformIdentifier, rdParams.PlatformVsCurrency, rd.timeFormat, throttle) + if is != nil { + is.HasFiatRates = true + is.HasTokenFiatRates = rd.downloadTokens + } + } else { return nil, fmt.Errorf("NewFiatRatesDownloader: incorrect API type %q", apiType) } @@ -71,6 +79,7 @@ func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, callb // Run periodically downloads current (every 15 minutes) and historical (once a day) tickers func (rd *RatesDownloader) Run() error { var lastHistoricalTickers time.Time + is := rd.db.GetInternalState() for { tickers, err := rd.downloader.CurrentTickers() @@ -79,6 +88,9 @@ func (rd *RatesDownloader) Run() error { } else { rd.db.FiatRatesSetCurrentTicker(tickers) glog.Info("FiatRatesDownloader: CurrentTickers updated") + if is != nil { + is.CurrentFiatRatesTime = time.Now() + } if rd.callbackOnNewTicker != nil { rd.callbackOnNewTicker(tickers) } @@ -94,16 +106,24 @@ func (rd *RatesDownloader) Run() error { glog.Error("FiatRatesDownloader: FiatRatesFindLastTicker error ", err) } else { glog.Infof("FiatRatesDownloader: UpdateHistoricalTickers finished, last ticker from %v", ticker.Timestamp) - } - // UpdateHistoricalTokenTickers in a goroutine, it can take quite some time as there may be many tokens - go func() { - err := rd.downloader.UpdateHistoricalTokenTickers() - if err != nil { - glog.Error("FiatRatesDownloader: UpdateHistoricalTokenTickers error ", err) - } else { - glog.Info("FiatRatesDownloader: UpdateHistoricalTokenTickers finished") + if is != nil { + is.HistoricalFiatRatesTime = ticker.Timestamp } - }() + } + if rd.downloadTokens { + // UpdateHistoricalTokenTickers in a goroutine, it can take quite some time as there are many tokens + go func() { + err := rd.downloader.UpdateHistoricalTokenTickers() + if err != nil { + glog.Error("FiatRatesDownloader: UpdateHistoricalTokenTickers error ", err) + } else { + glog.Info("FiatRatesDownloader: UpdateHistoricalTokenTickers finished") + if is != nil { + is.HistoricalTokenFiatRatesTime = time.Now() + } + } + }() + } } } // wait for the next run with a slight random value to avoid too many request at the same time diff --git a/server/public.go b/server/public.go index 4147d241b2..d2b168ac07 100644 --- a/server/public.go +++ b/server/public.go @@ -520,10 +520,14 @@ func (s *PublicServer) parseTemplates() []*template.Template { } func formatUnixTime(ut int64) string { - return formatTime(time.Unix(ut, 0)) + t := time.Unix(ut, 0) + return formatTime(&t) } -func formatTime(t time.Time) string { +func formatTime(t *time.Time) string { + if t == nil { + return "" + } return t.Format(time.RFC1123) } diff --git a/static/templates/index.html b/static/templates/index.html index 7708062251..77ef6fbd5a 100644 --- a/static/templates/index.html +++ b/static/templates/index.html @@ -47,6 +47,16 @@

Blockbook

Transactions in Mempool {{if .InternalExplorer}}{{$bb.MempoolSize}}{{else}}{{$bb.MempoolSize}}{{end}} + {{- if $bb.HasFiatRates -}} + + Current Fiat rates + {{formatTime $bb.CurrentFiatRatesTime}} + + + Historical Fiat rates + {{formatTime $bb.HistoricalFiatRatesTime}}{{if $bb.HasTokenFiatRates}}
tokens {{formatTime $bb.HistoricalTokenFiatRatesTime}}{{end}} + + {{- end -}} Size On Disk {{$bb.DbSize}} From 845d7e231ab51f8f85dad6bb9c1ff4c894cd238b Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 10 Sep 2022 12:30:56 +0200 Subject: [PATCH 083/530] Store unknown contracts in DB --- api/worker.go | 10 ++++++++-- db/rocksdb_ethereumtype.go | 17 +++++++++++++---- server/public_ethereumtype_test.go | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/api/worker.go b/api/worker.go index 03809bb58d..8e03c8e06f 100644 --- a/api/worker.go +++ b/api/worker.go @@ -548,13 +548,19 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add glog.Errorf("GetContractInfo error %v, contract %v", err, t.Contract) } if contractInfo == nil { - glog.Warningf("Contract %v %v not found in DB", t.Contract, typeName) + // log warning only if the contract should have been known from processing of the internal data + if eth.ProcessInternalTransactions { + glog.Warningf("Contract %v %v not found in DB", t.Contract, typeName) + } contractInfo, err = w.chain.GetContractInfo(cd) if err != nil { glog.Errorf("GetContractInfo from chain error %v, contract %v", err, t.Contract) } if contractInfo == nil { - contractInfo = &bchain.ContractInfo{Name: t.Contract, Type: bchain.UnknownTokenType} + contractInfo = &bchain.ContractInfo{Name: t.Contract, Type: bchain.UnknownTokenType, Decimals: w.chainParser.AmountDecimals()} + } + if err = w.db.StoreContractInfo(contractInfo); err != nil { + glog.Errorf("StoreContractInfo error %v, contract %v", err, t.Contract) } } var value *Amount diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index e48761ab04..b29a8fe9e4 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -749,7 +749,7 @@ func (d *RocksDB) GetContractInfoForAddress(address string) (*bchain.ContractInf } // GetContractInfo gets contract from cache or DB and possibly updates the type from typeFromContext -// this is because it is hard to guess the type of the contract using API, it is easier to set it the first time its usage is detected in tx +// it is hard to guess the type of the contract using API, it is easier to set it the first time the contract is processed in a tx func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, error) { cacheKey := string(contract) cachedContractsMux.Lock() @@ -783,9 +783,18 @@ func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, typeFromCon } // StoreContractInfo stores contractInfo in DB -// if CreatedInBlock==0 and DestructedInBlock!=0, it is evaluated as a desctruction of a contract, the contract info is updated +// if CreatedInBlock==0 and DestructedInBlock!=0, it is evaluated as a destruction of a contract, the contract info is updated // in all other cases the contractInfo overwrites previously stored data in DB (however it should not really happen as contract is created only once) -func (d *RocksDB) StoreContractInfo(wb *grocksdb.WriteBatch, contractInfo *bchain.ContractInfo) error { +func (d *RocksDB) StoreContractInfo(contractInfo *bchain.ContractInfo) error { + wb := grocksdb.NewWriteBatch() + defer wb.Destroy() + if err := d.storeContractInfo(wb, contractInfo); err != nil { + return err + } + return d.WriteBatch(wb) +} + +func (d *RocksDB) storeContractInfo(wb *grocksdb.WriteBatch, contractInfo *bchain.ContractInfo) error { if contractInfo.Contract != "" { key, err := d.chainParser.GetAddrDescFromAddress(contractInfo.Contract) if err != nil { @@ -882,7 +891,7 @@ func (d *RocksDB) storeBlockSpecificDataEthereumType(wb *grocksdb.WriteBatch, bl } } for i := range blockSpecificData.Contracts { - if err := d.StoreContractInfo(wb, &blockSpecificData.Contracts[i]); err != nil { + if err := d.storeContractInfo(wb, &blockSpecificData.Contracts[i]); err != nil { return err } } diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 1b95d8cc53..6449683697 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -73,7 +73,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"}}}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"},"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9":{"Type":"Contract","Alias":"Contract 205"}}}`, }, }, { From f24c83b33a50c6d0da049806008c69c1cc2bad0f Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 11 Sep 2022 00:41:17 +0200 Subject: [PATCH 084/530] Update ETH consensus layer to prysm 3.1.1 --- configs/coins/ethereum_archive_consensus.json | 6 +++--- configs/coins/ethereum_consensus.json | 6 +++--- .../coins/ethereum_testnet_goerli_archive_consensus.json | 6 +++--- configs/coins/ethereum_testnet_goerli_consensus.json | 6 +++--- .../coins/ethereum_testnet_ropsten_archive_consensus.json | 6 +++--- configs/coins/ethereum_testnet_ropsten_consensus.json | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 164a1fa60b..81573c31c4 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", + "version": "3.1.1", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", "verification_type": "sha256", - "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", + "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 0bb21357a3..7fd44da536 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", + "version": "3.1.1", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", "verification_type": "sha256", - "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", + "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli_archive_consensus.json b/configs/coins/ethereum_testnet_goerli_archive_consensus.json index 608546566a..d50c6fa28e 100644 --- a/configs/coins/ethereum_testnet_goerli_archive_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-goerli-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", + "version": "3.1.1", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", "verification_type": "sha256", - "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", + "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17506 --rpc-port=17507 --monitoring-port=17508 --p2p-tcp-port=13506 --p2p-udp-port=12506 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli_consensus.json b/configs/coins/ethereum_testnet_goerli_consensus.json index 831a47aaf5..8d23b22332 100644 --- a/configs/coins/ethereum_testnet_goerli_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-goerli-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", + "version": "3.1.1", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", "verification_type": "sha256", - "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", + "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17526 --rpc-port=17527 --monitoring-port=17528 --p2p-tcp-port=13526 --p2p-udp-port=12526 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json index a0b8100f7a..da35e12984 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-ropsten-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", + "version": "3.1.1", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", "verification_type": "sha256", - "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", + "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_ropsten_consensus.json b/configs/coins/ethereum_testnet_ropsten_consensus.json index f457063a63..1c337449a7 100644 --- a/configs/coins/ethereum_testnet_ropsten_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-ropsten-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.0/beacon-chain-v3.1.0-linux-amd64", + "version": "3.1.1", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", "verification_type": "sha256", - "verification_source": "f76aed03c207c2e4ade1c1cde47cbc0828bb7fb9b44d5518e6f13a9b39dacc42", + "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13536 --p2p-udp-port=12536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From 3932d197078a7c109f98a8c7bfe98be0ed7893e0 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 12 Sep 2022 16:19:43 +0200 Subject: [PATCH 085/530] Delay download of historical tickers by one hour If the download is initiated too early, the provider does not yet have the historical tokens ready. --- fiat/coingecko.go | 2 +- fiat/fiat_rates.go | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fiat/coingecko.go b/fiat/coingecko.go index 2b0592e382..5b5789a46a 100644 --- a/fiat/coingecko.go +++ b/fiat/coingecko.go @@ -105,7 +105,7 @@ func (cg *Coingecko) makeReq(url string) ([]byte, error) { return nil, err } // if there is a throttling error, wait 60 seconds and retry - glog.Errorf("Coingecko makeReq %v error %v, will retry in 60 seconds", url, err) + glog.Warningf("Coingecko makeReq %v error %v, will retry in 60 seconds", url, err) time.Sleep(60 * time.Second) } } diff --git a/fiat/fiat_rates.go b/fiat/fiat_rates.go index f7a4ac3611..a920cb199d 100644 --- a/fiat/fiat_rates.go +++ b/fiat/fiat_rates.go @@ -95,7 +95,9 @@ func (rd *RatesDownloader) Run() error { rd.callbackOnNewTicker(tickers) } } - if time.Now().UTC().YearDay() != lastHistoricalTickers.YearDay() || time.Now().UTC().Year() != lastHistoricalTickers.Year() { + now := time.Now().UTC() + // once a day, 1 hour after UTC midnight (to let the provider prepare historical rates) update historical tickers + if (now.YearDay() != lastHistoricalTickers.YearDay() || now.Year() != lastHistoricalTickers.Year()) && now.Hour() > 0 { err = rd.downloader.UpdateHistoricalTickers() if err != nil { glog.Error("FiatRatesDownloader: UpdateHistoricalTickers error ", err) @@ -127,10 +129,10 @@ func (rd *RatesDownloader) Run() error { } } // wait for the next run with a slight random value to avoid too many request at the same time - now := time.Now().Unix() - next := now + rd.periodSeconds + unix := time.Now().Unix() + next := unix + rd.periodSeconds next -= next % rd.periodSeconds next += int64(rand.Intn(12)) - time.Sleep(time.Duration(next-now) * time.Second) + time.Sleep(time.Duration(next-unix) * time.Second) } } From 070df1efcc195fe2c5901adf3c6579820b80487a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 14 Sep 2022 18:44:21 +0200 Subject: [PATCH 086/530] Add NFT detail page --- api/types.go | 2 +- api/worker.go | 130 ++++++++++---------- bchain/basechain.go | 5 + bchain/coins/blockchain.go | 6 + bchain/coins/eth/contract.go | 45 ++++++- bchain/types.go | 1 + server/public.go | 35 ++++++ server/public_ethereumtype_test.go | 16 ++- static/css/main.css | 1 + static/templates/address.html | 10 +- static/templates/tokenDetail.html | 111 +++++++++++++++++ static/templates/tx.html | 2 +- static/templates/txdetail_ethereumtype.html | 14 +-- tests/dbtestdata/fakechain_ethereumtype.go | 7 +- 14 files changed, 299 insertions(+), 86 deletions(-) create mode 100644 static/templates/tokenDetail.html diff --git a/api/types.go b/api/types.go index 99203f1662..060b550f3b 100644 --- a/api/types.go +++ b/api/types.go @@ -163,7 +163,7 @@ type TokenTransfer struct { Type bchain.TokenTypeName `json:"type"` From string `json:"from"` To string `json:"to"` - Token string `json:"token"` + Contract string `json:"contract"` Name string `json:"name"` Symbol string `json:"symbol"` Decimals int `json:"decimals"` diff --git a/api/worker.go b/api/worker.go index 8e03c8e06f..bab9544449 100644 --- a/api/worker.go +++ b/api/worker.go @@ -532,37 +532,58 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, return r, nil } -func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, addresses map[string]struct{}) []TokenTransfer { - sort.Sort(transfers) - tokens := make([]TokenTransfer, len(transfers)) - for i := range transfers { - t := transfers[i] - cd, err := w.chainParser.GetAddrDescFromAddress(t.Contract) - if err != nil { - glog.Errorf("GetAddrDescFromAddress error %v, contract %v", err, t.Contract) - continue +func (w *Worker) getContractInfo(contract string, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) { + cd, err := w.chainParser.GetAddrDescFromAddress(contract) + if err != nil { + return nil, false, err + } + return w.getContractDescriptorInfo(cd, typeFromContext) +} + +func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) { + var err error + validContract := true + contractInfo, err := w.db.GetContractInfo(cd, typeFromContext) + if err != nil { + return nil, false, err + } + if contractInfo == nil { + // log warning only if the contract should have been known from processing of the internal data + if eth.ProcessInternalTransactions { + glog.Warningf("Contract %v %v not found in DB", cd, typeFromContext) } - typeName := bchain.EthereumTokenTypeMap[t.Type] - contractInfo, err := w.db.GetContractInfo(cd, typeName) + contractInfo, err = w.chain.GetContractInfo(cd) if err != nil { - glog.Errorf("GetContractInfo error %v, contract %v", err, t.Contract) + glog.Errorf("GetContractInfo from chain error %v, contract %v", err, cd) } if contractInfo == nil { - // log warning only if the contract should have been known from processing of the internal data - if eth.ProcessInternalTransactions { - glog.Warningf("Contract %v %v not found in DB", t.Contract, typeName) - } - contractInfo, err = w.chain.GetContractInfo(cd) - if err != nil { - glog.Errorf("GetContractInfo from chain error %v, contract %v", err, t.Contract) - } - if contractInfo == nil { - contractInfo = &bchain.ContractInfo{Name: t.Contract, Type: bchain.UnknownTokenType, Decimals: w.chainParser.AmountDecimals()} + contractInfo = &bchain.ContractInfo{Type: bchain.UnknownTokenType, Decimals: w.chainParser.AmountDecimals()} + addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(cd) + if len(addresses) > 0 { + contractInfo.Contract = addresses[0] } + + validContract = false + } else { if err = w.db.StoreContractInfo(contractInfo); err != nil { - glog.Errorf("StoreContractInfo error %v, contract %v", err, t.Contract) + glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) } } + } + return contractInfo, validContract, nil +} + +func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, addresses map[string]struct{}) []TokenTransfer { + sort.Sort(transfers) + tokens := make([]TokenTransfer, len(transfers)) + for i := range transfers { + t := transfers[i] + typeName := bchain.EthereumTokenTypeMap[t.Type] + contractInfo, _, err := w.getContractInfo(t.Contract, typeName) + if err != nil { + glog.Errorf("getContractInfo error %v, contract %v", err, t.Contract) + continue + } var value *Amount var values []MultiTokenValue if t.Type == bchain.MultiToken { @@ -578,7 +599,7 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add aggregateAddress(addresses, t.To) tokens[i] = TokenTransfer{ Type: typeName, - Token: t.Contract, + Contract: t.Contract, From: t.From, To: t.To, Value: value, @@ -591,6 +612,26 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add return tokens } +func (w *Worker) GetEthereumTokenURI(contract string, id string) (string, *bchain.ContractInfo, error) { + cd, err := w.chainParser.GetAddrDescFromAddress(contract) + if err != nil { + return "", nil, err + } + tokenId, ok := new(big.Int).SetString(id, 10) + if !ok { + return "", nil, errors.New("Invalid token id") + } + uri, err := w.chain.GetTokenURI(cd, tokenId) + if err != nil { + return "", nil, err + } + ci, _, err := w.getContractDescriptorInfo(cd, bchain.UnknownTokenType) + if err != nil { + return "", nil, err + } + return uri, ci, nil +} + func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool, filter *AddressFilter, maxResults int) ([]string, error) { var err error txids := make([]string, 0, 4) @@ -772,29 +813,11 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) { } func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails) (*Token, error) { - validContract := true typeName := bchain.EthereumTokenTypeMap[c.Type] - ci, err := w.db.GetContractInfo(c.Contract, typeName) + ci, validContract, err := w.getContractDescriptorInfo(c.Contract, typeName) if err != nil { - return nil, errors.Annotatef(err, "GetContractInfo %v", c.Contract) + return nil, errors.Annotatef(err, "getEthereumContractBalance %v", c.Contract) } - if ci == nil { - glog.Warningf("Contract %v %v not found in DB", c.Contract, typeName) - ci, err = w.chain.GetContractInfo(c.Contract) - if err != nil { - glog.Errorf("GetContractInfo from chain error %v, contract %v", err, c.Contract) - } - if ci == nil { - ci = &bchain.ContractInfo{Type: bchain.UnknownTokenType} - addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(c.Contract) - if len(addresses) > 0 { - ci.Contract = addresses[0] - ci.Name = addresses[0] - } - validContract = false - } - } - t := Token{ Contract: ci.Contract, Name: ci.Name, @@ -840,27 +863,10 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i // a fallback method in case internal transactions are not processed and there is no indexed info about contract balance for an address func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bchain.AddressDescriptor, details AccountDetails) (*Token, error) { var b *big.Int - validContract := true - ci, err := w.db.GetContractInfo(contract, "") + ci, validContract, err := w.getContractDescriptorInfo(contract, bchain.UnknownTokenType) if err != nil { return nil, errors.Annotatef(err, "GetContractInfo %v", contract) } - if ci == nil { - glog.Warningf("Contract %v not found in DB", contract) - ci, err = w.chain.GetContractInfo(contract) - if err != nil { - glog.Errorf("GetContractInfo from chain error %v, contract %v", err, contract) - } - if ci == nil { - ci = &bchain.ContractInfo{Type: bchain.UnknownTokenType} - addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(contract) - if len(addresses) > 0 { - ci.Contract = addresses[0] - ci.Name = addresses[0] - } - validContract = false - } - } // do not read contract balances etc in case of Basic option if details >= AccountDetailsTokenBalances && validContract { b, err = w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, contract) diff --git a/bchain/basechain.go b/bchain/basechain.go index ee5d30e06f..bcd03d195d 100644 --- a/bchain/basechain.go +++ b/bchain/basechain.go @@ -63,3 +63,8 @@ func (b *BaseChain) GetContractInfo(contractDesc AddressDescriptor) (*ContractIn func (b *BaseChain) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error) { return nil, errors.New("Not supported") } + +// GetContractInfo returns URI of non fungible or multi token defined by token id +func (p *BaseChain) GetTokenURI(contractDesc AddressDescriptor, tokenID *big.Int) (string, error) { + return "", errors.New("Not supported") +} diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 8cdd12459e..fa3195a58a 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -333,6 +333,12 @@ func (c *blockChainWithMetrics) EthereumTypeGetErc20ContractBalance(addrDesc, co return c.b.EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc) } +// GetContractInfo returns URI of non fungible or multi token defined by token id +func (c *blockChainWithMetrics) GetTokenURI(contractDesc bchain.AddressDescriptor, tokenID *big.Int) (v string, err error) { + defer func(s time.Time) { c.observeRPCLatency("GetTokenURI", s, err) }(time.Now()) + return c.b.GetTokenURI(contractDesc, tokenID) +} + type mempoolWithMetrics struct { mempool bchain.Mempool m *common.Metrics diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go index a3db57efd4..4cbb9541f5 100644 --- a/bchain/coins/eth/contract.go +++ b/bchain/coins/eth/contract.go @@ -6,6 +6,7 @@ import ( "strings" ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/juju/errors" "github.com/trezor/blockbook/bchain" ) @@ -14,6 +15,8 @@ const erc20TransferMethodSignature = "0xa9059cbb" // transfer(a const erc721TransferFromMethodSignature = "0x23b872dd" // transferFrom(address,address,uint256) const erc721SafeTransferFromMethodSignature = "0x42842e0e" // safeTransferFrom(address,address,uint256) const erc721SafeTransferFromWithDataMethodSignature = "0xb88d4fde" // safeTransferFrom(address,address,uint256,bytes) +const erc721TokenURIMethodSignature = "0xc87b56dd" // tokenURI(uint256) +const erc1155URIMethodSignature = "0x0e89341c" // uri(uint256) const tokenTransferEventSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" const tokenERC1155TransferSingleEventSignature = "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62" @@ -24,7 +27,7 @@ const nameRegisteredEventSignature = "0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621 const contractNameSignature = "0x06fdde03" const contractSymbolSignature = "0x95d89b41" const contractDecimalsSignature = "0x313ce567" -const contractBalanceOf = "0x70a08231" +const contractBalanceOfSignature = "0x70a08231" func addressFromPaddedHex(s string) (string, error) { var t big.Int @@ -315,9 +318,9 @@ func (b *EthereumRPC) GetContractInfo(contractDesc bchain.AddressDescriptor) (*b // EthereumTypeGetErc20ContractBalance returns balance of ERC20 contract for given address func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) { - addr := EIP55Address(addrDesc) - contract := EIP55Address(contractDesc) - req := contractBalanceOf + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr)-2:] + addr[2:] + addr := hexutil.Encode(addrDesc) + contract := hexutil.Encode(contractDesc) + req := contractBalanceOfSignature + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr)-2:] + addr[2:] data, err := b.ethCall(req, contract) if err != nil { return nil, err @@ -328,3 +331,37 @@ func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc } return r, nil } + +// GetContractInfo returns URI of non fungible or multi token defined by token id +func (b *EthereumRPC) GetTokenURI(contractDesc bchain.AddressDescriptor, tokenID *big.Int) (string, error) { + address := hexutil.Encode(contractDesc) + // CryptoKitties do not fully support ERC721 standard, do not have tokenURI method + if address == "0x06012c8cf97bead5deae237070f9587f8e7a266d" { + return "https://api.cryptokitties.co/kitties/" + tokenID.Text(10), nil + } + id := tokenID.Text(16) + if len(id) < 64 { + id = "0000000000000000000000000000000000000000000000000000000000000000"[len(id):] + id + } + // try ERC721 tokenURI method and ERC1155 uri method + for _, method := range []string{erc721TokenURIMethodSignature, erc1155URIMethodSignature} { + data, err := b.ethCall(method+id, address) + if err == nil && data != "" { + uri := parseSimpleStringProperty(data) + // try to sanitize the URI returned from the contract + i := strings.LastIndex(uri, "ipfs://") + if i >= 0 { + uri = strings.Replace(uri[i:], "ipfs://", "https://ipfs.io/ipfs/", 1) + // some contracts return uri ipfs://ifps/abcdef instead of ipfs://abcdef + uri = strings.Replace(uri, "https://ipfs.io/ipfs/ipfs/", "https://ipfs.io/ipfs/", 1) + } + i = strings.LastIndex(uri, "https://") + // allow only https:// URIs + if i >= 0 { + uri = strings.ReplaceAll(uri[i:], "{id}", id) + return uri, nil + } + } + } + return "", nil +} diff --git a/bchain/types.go b/bchain/types.go index 2faba03212..8a69fbee5c 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -317,6 +317,7 @@ type BlockChain interface { EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error) + GetTokenURI(contractDesc AddressDescriptor, tokenID *big.Int) (string, error) } // BlockChainParser defines common interface to parsing and conversions of block chain data diff --git a/server/public.go b/server/public.go index d2b168ac07..ce860869f2 100644 --- a/server/public.go +++ b/server/public.go @@ -142,6 +142,9 @@ func (s *PublicServer) ConnectFullPublicInterface() { serveMux.HandleFunc(path+"spending/", s.htmlTemplateHandler(s.explorerSpendingTx)) serveMux.HandleFunc(path+"sendtx", s.htmlTemplateHandler(s.explorerSendTx)) serveMux.HandleFunc(path+"mempool", s.htmlTemplateHandler(s.explorerMempool)) + if s.chainParser.GetChainType() == bchain.ChainEthereumType { + serveMux.HandleFunc(path+"nft/", s.htmlTemplateHandler(s.explorerNftDetail)) + } } else { // redirect to wallet requests for tx and address, possibly to external site serveMux.HandleFunc(path+"tx/", s.txRedirect) @@ -417,6 +420,7 @@ const ( blockTpl sendTransactionTpl mempoolTpl + nftDetailTpl tplCount ) @@ -445,6 +449,9 @@ type TemplateData struct { SendTxHex string Status string NonZeroBalanceTokens bool + TokenId string + URI string + ContractInfo *bchain.ContractInfo } func (s *PublicServer) parseTemplates() []*template.Template { @@ -460,6 +467,7 @@ func (s *PublicServer) parseTemplates() []*template.Template { "tokenTransfersCount": tokenTransfersCount, "tokenCount": tokenCount, "hasPrefix": strings.HasPrefix, + "jsStr": jsStr, } var createTemplate func(filenames ...string) *template.Template if s.debug { @@ -509,6 +517,7 @@ func (s *PublicServer) parseTemplates() []*template.Template { t[txTpl] = createTemplate("./static/templates/tx.html", "./static/templates/txdetail_ethereumtype.html", "./static/templates/base.html") t[addressTpl] = createTemplate("./static/templates/address.html", "./static/templates/txdetail_ethereumtype.html", "./static/templates/paging.html", "./static/templates/base.html") t[blockTpl] = createTemplate("./static/templates/block.html", "./static/templates/txdetail_ethereumtype.html", "./static/templates/paging.html", "./static/templates/base.html") + t[nftDetailTpl] = createTemplate("./static/templates/tokenDetail.html", "./static/templates/base.html") } else { t[txTpl] = createTemplate("./static/templates/tx.html", "./static/templates/txdetail.html", "./static/templates/base.html") t[addressTpl] = createTemplate("./static/templates/address.html", "./static/templates/txdetail.html", "./static/templates/paging.html", "./static/templates/base.html") @@ -601,6 +610,10 @@ func tokenCount(tokens []api.Token, t bchain.TokenTypeName) int { return count } +func jsStr(s string) template.JSStr { + return template.JSStr(s) +} + func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) { var tx *api.Tx var err error @@ -737,6 +750,28 @@ func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) ( return addressTpl, data, nil } +func (s *PublicServer) explorerNftDetail(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) { + parts := strings.Split(r.URL.Path, "/") + if len(parts) < 3 { + return errorTpl, nil, api.NewAPIError("Missing parameters", true) + } + tokenId := parts[len(parts)-1] + contract := parts[len(parts)-2] + uri, ci, err := s.api.GetEthereumTokenURI(contract, tokenId) + s.metrics.ExplorerViews.With(common.Labels{"action": "nftDetail"}).Inc() + if err != nil { + return errorTpl, nil, api.NewAPIError(err.Error(), true) + } + if ci == nil { + return errorTpl, nil, api.NewAPIError(fmt.Sprintf("Unknown contract %s", contract), true) + } + data := s.newTemplateData() + data.TokenId = tokenId + data.ContractInfo = ci + data.URI = uri + return nftDetailTpl, data, nil +} + func (s *PublicServer) explorerXpub(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) { var xpub string i := strings.LastIndex(r.URL.Path, "xpub/") diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 6449683697..221ef57f8b 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -24,7 +24,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address 0.000000000123450123 FAKE

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

Confirmed

Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ERC20 Tokens
ContractTokensTransfers
Contract 740.001000123074 S741
Contract 130.000000001000123013 S131
ERC721 Tokens
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
ID 1 S205
Fee: 0.00008794500041041 FAKE
Unconfirmed Transaction!0 FAKE
ERC20 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
871.180000950184 S74
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
7.674999999999991915 S13
Fee: 0.000216368 FAKE
Unconfirmed Transaction!0 FAKE
`, + `Trezor Fake Coin Explorer

Address 0.000000000123450123 FAKE

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

Confirmed

Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ERC20 Tokens
ContractTokensTransfers
Contract 740.001000123074 S741
Contract 130.000000001000123013 S131
ERC721 Tokens
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
ID 1 S205
Fee: 0.00008794500041041 FAKE
Unconfirmed Transaction!0 FAKE
ERC20 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
871.180000950184 S74
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
7.674999999999991915 S13
Fee: 0.000216368 FAKE
Unconfirmed Transaction!0 FAKE
`, }, }, { @@ -33,7 +33,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address 0.000000000123450093 FAKE

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

Confirmed

Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ERC1155 Tokens
ContractTokensTransfers
Contract 1111776:1 S111, 1898:10 S1111

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0 FAKE
ERC1155 Token Transfers
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1776:1 S1111898:10 S111
Fee: 0.000081891755740665 FAKE
Unconfirmed Transaction!0 FAKE
`, + `Trezor Fake Coin Explorer

Address 0.000000000123450093 FAKE

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

Confirmed

Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ERC1155 Tokens
ContractTokensTransfers
Contract 1111 of ID 1776, 10 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0 FAKE
ERC1155 Token Transfers
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
Fee: 0.000081891755740665 FAKE
Unconfirmed Transaction!0 FAKE
`, }, }, { @@ -42,8 +42,14 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101

Summary

In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE
Fees0.002081 FAKE
RBFON

Details

Input Data
Transfer
Method ID: 0xa9059cbb
Function: transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101

Summary

In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE
Fees0.002081 FAKE
RBFON

Details

Input Data
Transfer
Method ID: 0xa9059cbb
Function: transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, }, + }, { + name: "explorerTokenDetail " + dbtestdata.EthAddr7b, + r: newGetRequest(ts.URL + "/nft/" + dbtestdata.EthAddrContractCd + "/" + "1"), + status: http.StatusOK, + contentType: "text/html; charset=utf-8", + body: []string{`

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9 Contract 205
Contract typeERC20
`, `Loading metadata from https://ipfs.io/ipfs/cda9fc258358ecaa88845f19af595e908bb7efe9.json`}, }, { name: "apiIndex", @@ -73,7 +79,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"},"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9":{"Type":"Contract","Alias":"Contract 205"}}}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"},"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9":{"Type":"Contract","Alias":"Contract 205"}}}`, }, }, { @@ -82,7 +88,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}},"addressAliases":{"0x20cD153de35D469BA46127A0C8F18626b59a256A":{"Type":"ENS","Alias":"address20.eth"},"0x4af4114F73d1c1C903aC9E0361b379D1291808A2":{"Type":"Contract","Alias":"Contract 74"}}}`, + `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}},"addressAliases":{"0x20cD153de35D469BA46127A0C8F18626b59a256A":{"Type":"ENS","Alias":"address20.eth"},"0x4af4114F73d1c1C903aC9E0361b379D1291808A2":{"Type":"Contract","Alias":"Contract 74"}}}`, }, }, { diff --git a/static/css/main.css b/static/css/main.css index d4642b3c77..82d4ae64ae 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -226,6 +226,7 @@ h3 { table-layout: fixed; border-radius: .25rem; background: white; + overflow-wrap: break-word; } .data-table td, .data-table th { diff --git a/static/templates/address.html b/static/templates/address.html index 987ea89e74..590da4a75e 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -44,7 +44,7 @@

Confirmed

{{range $t := $addr.Tokens}} {{if eq $t.Type "ERC20"}} - {{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} + {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} {{formatAmountWithDecimals $t.BalanceSat $t.Decimals}} {{$t.Symbol}} {{$t.Transfers}} @@ -69,9 +69,9 @@

Confirmed

{{range $t := $addr.Tokens}} {{if eq $t.Type "ERC721"}} - {{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} + {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} - {{range $i, $iv := $t.Ids}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv 0}}{{end}} + {{range $i, $iv := $t.Ids}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv 0}}{{end}} {{$t.Transfers}} @@ -96,9 +96,9 @@

Confirmed

{{range $t := $addr.Tokens}} {{if eq $t.Type "ERC1155"}} - {{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} + {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} - {{range $i, $iv := $t.MultiTokenValues}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$t.Symbol}}{{end}} + {{range $i, $iv := $t.MultiTokenValues}}{{if $i}}, {{end}}{{$iv.Value}} of ID {{$iv.Id}}{{end}} {{$t.Transfers}} diff --git a/static/templates/tokenDetail.html b/static/templates/tokenDetail.html new file mode 100644 index 0000000000..e7d6f6f3e6 --- /dev/null +++ b/static/templates/tokenDetail.html @@ -0,0 +1,111 @@ +{{define "specific"}}{{$data := .}} +

NFT Token Detail

+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
Token ID{{$data.TokenId}}
Contract{{$data.ContractInfo.Contract}} {{$data.ContractInfo.Name}}
Contract type{{$data.ContractInfo.Type}}
+
+
+
+
+
Metadata
+
+
Loading metadata from {{$data.URI}}...
+
+
+ +{{end}} \ No newline at end of file diff --git a/static/templates/tx.html b/static/templates/tx.html index b78df2f982..af19283619 100644 --- a/static/templates/tx.html +++ b/static/templates/tx.html @@ -111,7 +111,7 @@
Input Data
# - Type + Type Data diff --git a/static/templates/txdetail_ethereumtype.html b/static/templates/txdetail_ethereumtype.html index 28bcfe780b..6df45e736a 100644 --- a/static/templates/txdetail_ethereumtype.html +++ b/static/templates/txdetail_ethereumtype.html @@ -67,7 +67,7 @@
-
+
{{formatAmount $tx.ValueOutSat}} {{$cs}}
@@ -132,7 +132,7 @@
-
{{formatAmount $tt.Value}} {{$cs}}
+
{{formatAmount $tt.Value}} {{$cs}}
{{- end -}}
@@ -176,7 +176,7 @@
-
{{formatAmountWithDecimals $tt.Value $tt.Decimals}} {{$tt.Symbol}}
+
{{formatAmountWithDecimals $tt.Value $tt.Decimals}} {{$tt.Symbol}}
{{- end -}} {{- end -}} @@ -221,7 +221,7 @@ -
ID {{formatAmountWithDecimals $tt.Value 0}} {{$tt.Symbol}}
+
ID {{$tt.Value}} {{$tt.Symbol}}
{{- end -}} {{- end -}} @@ -266,9 +266,9 @@ -
- {{- range $iv := $tt.MultiTokenValues -}} - {{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$tt.Symbol}} +
+ {{- range $i, $iv := $tt.MultiTokenValues -}} + {{if $i}}, {{end}}{{$iv.Value}} {{$tt.Symbol}} of ID {{$iv.Id}} {{- end -}}
diff --git a/tests/dbtestdata/fakechain_ethereumtype.go b/tests/dbtestdata/fakechain_ethereumtype.go index 4ee5047281..73e30b1209 100644 --- a/tests/dbtestdata/fakechain_ethereumtype.go +++ b/tests/dbtestdata/fakechain_ethereumtype.go @@ -129,7 +129,12 @@ func (c *fakeBlockChainEthereumType) GetContractInfo(contractDesc bchain.Address }, nil } -// EthereumTypeGetErc20ContractBalance is not supported +// EthereumTypeGetErc20ContractBalance returns simulated balance func (c *fakeBlockChainEthereumType) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) { return big.NewInt(1000000000 + int64(addrDesc[0])*1000 + int64(contractDesc[0])), nil } + +// GetTokenURI returns URI derived from the input contractDesc +func (c *fakeBlockChainEthereumType) GetTokenURI(contractDesc bchain.AddressDescriptor, tokenID *big.Int) (string, error) { + return "https://ipfs.io/ipfs/" + contractDesc.String()[3:] + ".json", nil +} From a14145b0a0d78b3873fad5054d60c972bb63d19f Mon Sep 17 00:00:00 2001 From: vdovhanych Date: Mon, 26 Sep 2022 12:55:57 +0200 Subject: [PATCH 087/530] fix docker build for arm when targetplatform is aarch64 --- build/docker/bin/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/docker/bin/Dockerfile b/build/docker/bin/Dockerfile index b10b533a0d..326ed7e791 100644 --- a/build/docker/bin/Dockerfile +++ b/build/docker/bin/Dockerfile @@ -29,7 +29,7 @@ fi # install and configure go ARG TARGETPLATFORM -RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then ARCHITECTURE=amd64; elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCHITECTURE=arm64; else ARCHITECTURE=amd64; fi \ +RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then ARCHITECTURE=amd64; elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCHITECTURE=arm64; elif [ "$TARGETPLATFORM" = "linux/aarch64" ]; then ARCHITECTURE=arm64; else ARCHITECTURE=amd64; fi \ && cd /opt && wget https://dl.google.com/go/$GOLANG_VERSION.linux-$ARCHITECTURE.tar.gz && \ tar xf $GOLANG_VERSION.linux-$ARCHITECTURE.tar.gz RUN ln -s /opt/go/bin/go /usr/bin/go From 750a27d9b78f96fb5fb73cf8986b66d0c261e548 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Sun, 25 Sep 2022 22:23:06 +0200 Subject: [PATCH 088/530] chore(ver): add arm64 for btc, ltc, eth, doge - arm64 binary paths added for backends and consensus: btc, btc testnet, btc signet, eth, eth ropsten, eth goerli, ltc, ltc testnet, doge, doge testnet --- configs/coins/bitcoin.json | 6 ++++++ configs/coins/bitcoin_signet.json | 6 ++++++ configs/coins/bitcoin_testnet.json | 6 ++++++ configs/coins/dogecoin.json | 7 +++++++ configs/coins/dogecoin_testnet.json | 7 +++++++ configs/coins/ethereum.json | 8 +++++++- configs/coins/ethereum_archive.json | 8 +++++++- configs/coins/ethereum_archive_consensus.json | 8 +++++++- configs/coins/ethereum_consensus.json | 8 +++++++- configs/coins/ethereum_testnet_goerli.json | 8 +++++++- configs/coins/ethereum_testnet_goerli_archive.json | 8 +++++++- .../coins/ethereum_testnet_goerli_archive_consensus.json | 8 +++++++- configs/coins/ethereum_testnet_goerli_consensus.json | 8 +++++++- configs/coins/ethereum_testnet_ropsten.json | 8 +++++++- configs/coins/ethereum_testnet_ropsten_archive.json | 8 +++++++- .../coins/ethereum_testnet_ropsten_archive_consensus.json | 8 +++++++- configs/coins/ethereum_testnet_ropsten_consensus.json | 8 +++++++- configs/coins/litecoin.json | 6 ++++++ configs/coins/litecoin_testnet.json | 6 ++++++ 19 files changed, 128 insertions(+), 12 deletions(-) diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index 2c75a9e54c..d1ad69ac29 100644 --- a/configs/coins/bitcoin.json +++ b/configs/coins/bitcoin.json @@ -39,6 +39,12 @@ "client_config_file": "bitcoin_client.conf", "additional_params": { "deprecatedrpc": "estimatefee" + }, + "platforms": { + "arm64": { + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-23.0/bitcoin-23.0-aarch64-linux-gnu.tar.gz", + "verification_source": "06f4c78271a77752ba5990d60d81b1751507f77efda1e5981b4e92fd4d9969fb" + } } }, "blockbook": { diff --git a/configs/coins/bitcoin_signet.json b/configs/coins/bitcoin_signet.json index 95f7c4bb4a..fc82b3e8c0 100644 --- a/configs/coins/bitcoin_signet.json +++ b/configs/coins/bitcoin_signet.json @@ -41,6 +41,12 @@ "client_config_file": "bitcoin_client.conf", "additional_params": { "deprecatedrpc": "estimatefee" + }, + "platforms": { + "arm64": { + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-23.0/bitcoin-23.0-aarch64-linux-gnu.tar.gz", + "verification_source": "06f4c78271a77752ba5990d60d81b1751507f77efda1e5981b4e92fd4d9969fb" + } } }, "blockbook": { diff --git a/configs/coins/bitcoin_testnet.json b/configs/coins/bitcoin_testnet.json index 4bf0586874..13546f6b2f 100644 --- a/configs/coins/bitcoin_testnet.json +++ b/configs/coins/bitcoin_testnet.json @@ -41,6 +41,12 @@ "client_config_file": "bitcoin_client.conf", "additional_params": { "deprecatedrpc": "estimatefee" + }, + "platforms": { + "arm64": { + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-23.0/bitcoin-23.0-aarch64-linux-gnu.tar.gz", + "verification_source": "06f4c78271a77752ba5990d60d81b1751507f77efda1e5981b4e92fd4d9969fb" + } } }, "blockbook": { diff --git a/configs/coins/dogecoin.json b/configs/coins/dogecoin.json index cc9464aa4a..0ad5a87ae3 100644 --- a/configs/coins/dogecoin.json +++ b/configs/coins/dogecoin.json @@ -42,6 +42,13 @@ "rpcthreads": 16, "upnp": 0, "whitelist": "127.0.0.1" + }, + "platforms": { + "arm64": { + "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.6/dogecoin-1.14.6-aarch64-linux-gnu.tar.gz", + "verification_source": "87419c29607b2612746fccebd694037e4be7600fc32198c4989f919be20952db", + "exclude_files": [] + } } }, "blockbook": { diff --git a/configs/coins/dogecoin_testnet.json b/configs/coins/dogecoin_testnet.json index 7dece87b32..115ac63c79 100644 --- a/configs/coins/dogecoin_testnet.json +++ b/configs/coins/dogecoin_testnet.json @@ -44,6 +44,13 @@ "rpcthreads": 16, "upnp": 0, "whitelist": "127.0.0.1" + }, + "platforms": { + "arm64": { + "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.6/dogecoin-1.14.6-aarch64-linux-gnu.tar.gz", + "verification_source": "87419c29607b2612746fccebd694037e4be7600fc32198c4989f919be20952db", + "exclude_files": [] + } } }, "blockbook": { diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 2fa9165277..1de8943cfb 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -36,7 +36,13 @@ "protect_memory": true, "mainnet": true, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + } + } }, "blockbook": { "package_name": "blockbook-ethereum", diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 5bf407f860..0332bc526f 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -36,7 +36,13 @@ "protect_memory": true, "mainnet": true, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + } + } }, "blockbook": { "package_name": "blockbook-ethereum-archive", diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 81573c31c4..554838d90a 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -33,7 +33,13 @@ "protect_memory": true, "mainnet": false, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", + "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + } + } }, "meta": { "package_maintainer": "IT", diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 7fd44da536..40d7fed53d 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -33,7 +33,13 @@ "protect_memory": true, "mainnet": false, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", + "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + } + } }, "meta": { "package_maintainer": "IT", diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index e9d784212f..c26df8211c 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -36,7 +36,13 @@ "protect_memory": true, "mainnet": false, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + } + } }, "blockbook": { "package_name": "blockbook-ethereum-testnet-goerli", diff --git a/configs/coins/ethereum_testnet_goerli_archive.json b/configs/coins/ethereum_testnet_goerli_archive.json index 9bdf5590e9..6c3ee919b4 100644 --- a/configs/coins/ethereum_testnet_goerli_archive.json +++ b/configs/coins/ethereum_testnet_goerli_archive.json @@ -36,7 +36,13 @@ "protect_memory": true, "mainnet": false, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + } + } }, "blockbook": { "package_name": "blockbook-ethereum-testnet-goerli-archive", diff --git a/configs/coins/ethereum_testnet_goerli_archive_consensus.json b/configs/coins/ethereum_testnet_goerli_archive_consensus.json index d50c6fa28e..6f678f02e8 100644 --- a/configs/coins/ethereum_testnet_goerli_archive_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_archive_consensus.json @@ -33,7 +33,13 @@ "protect_memory": true, "mainnet": false, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", + "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + } + } }, "meta": { "package_maintainer": "IT", diff --git a/configs/coins/ethereum_testnet_goerli_consensus.json b/configs/coins/ethereum_testnet_goerli_consensus.json index 8d23b22332..ab95e52119 100644 --- a/configs/coins/ethereum_testnet_goerli_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_consensus.json @@ -33,7 +33,13 @@ "protect_memory": true, "mainnet": false, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", + "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + } + } }, "meta": { "package_maintainer": "IT", diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index c607f10151..394fcf81fe 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -36,7 +36,13 @@ "protect_memory": true, "mainnet": false, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + } + } }, "blockbook": { "package_name": "blockbook-ethereum-testnet-ropsten", diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index a24e036c8b..bd924ab43b 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -36,7 +36,13 @@ "protect_memory": true, "mainnet": false, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + } + } }, "blockbook": { "package_name": "blockbook-ethereum-testnet-ropsten-archive", diff --git a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json index da35e12984..fd8de2418d 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json @@ -33,7 +33,13 @@ "protect_memory": true, "mainnet": false, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", + "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + } + } }, "meta": { "package_maintainer": "IT", diff --git a/configs/coins/ethereum_testnet_ropsten_consensus.json b/configs/coins/ethereum_testnet_ropsten_consensus.json index 1c337449a7..4bdd60370f 100644 --- a/configs/coins/ethereum_testnet_ropsten_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_consensus.json @@ -33,7 +33,13 @@ "protect_memory": true, "mainnet": false, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", + "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + } + } }, "meta": { "package_maintainer": "IT", diff --git a/configs/coins/litecoin.json b/configs/coins/litecoin.json index 6684508a34..7483d26473 100644 --- a/configs/coins/litecoin.json +++ b/configs/coins/litecoin.json @@ -39,6 +39,12 @@ "client_config_file": "bitcoin_like_client.conf", "additional_params": { "whitelist": "127.0.0.1" + }, + "platforms": { + "arm64": { + "binary_url": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-aarch64-linux-gnu.tar.gz", + "verification_source": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-aarch64-linux-gnu.tar.gz.asc" + } } }, "blockbook": { diff --git a/configs/coins/litecoin_testnet.json b/configs/coins/litecoin_testnet.json index a5956c96e9..fb23dbde04 100644 --- a/configs/coins/litecoin_testnet.json +++ b/configs/coins/litecoin_testnet.json @@ -41,6 +41,12 @@ "client_config_file": "bitcoin_like_client.conf", "additional_params": { "whitelist": "127.0.0.1" + }, + "platforms": { + "arm64": { + "binary_url": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-aarch64-linux-gnu.tar.gz", + "verification_source": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-aarch64-linux-gnu.tar.gz.asc" + } } }, "blockbook": { From 4f6b62ba2d35bf25fc483511d005e7d3edbd2454 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 10 Oct 2022 08:39:04 +0200 Subject: [PATCH 089/530] Blockbook version to 0.4.0 --- configs/environ.json | 10 +- docs/api.md | 235 +++++++++++++++++++++++-------------------- 2 files changed, 131 insertions(+), 114 deletions(-) diff --git a/configs/environ.json b/configs/environ.json index 4554561a8d..529ac6404f 100644 --- a/configs/environ.json +++ b/configs/environ.json @@ -1,7 +1,7 @@ { - "version": "0.3.6", - "backend_install_path": "/opt/coins/nodes", - "backend_data_path": "/opt/coins/data", - "blockbook_install_path": "/opt/coins/blockbook", - "blockbook_data_path": "/opt/coins/data" + "version": "0.4.0", + "backend_install_path": "/opt/coins/nodes", + "backend_data_path": "/opt/coins/data", + "blockbook_install_path": "/opt/coins/blockbook", + "blockbook_data_path": "/opt/coins/data" } diff --git a/docs/api.md b/docs/api.md index 9798429f89..648b08c05d 100644 --- a/docs/api.md +++ b/docs/api.md @@ -9,6 +9,7 @@ There are two versions of provided API. The legacy API is a compatible subset of API provided by **Bitcore Insight**. It supports only Bitcoin-type coins. The details of the REST/socket.io requests can be found in the Insight's documentation. ### REST API + ``` GET /api/v1/block-index/ GET /api/v1/tx/ @@ -17,15 +18,16 @@ GET /api/v1/utxo/
GET /api/v1/block/ GET /api/v1/estimatefee/ GET /api/v1/sendtx/ -POST /api/v1/sendtx/ (hex tx data in request body) +POST /api/v1/sendtx/ (hex tx data in request body) ``` ### Socket.io API + Socket.io interface is provided at `/socket.io/`. The interface also can be explored using Blockbook Socket.io Test Page found at `/test-socketio.html`. The legacy API is provided as is and will not be further developed. -The legacy API is currently (Blockbook v0.3.5) also accessible without the */v1/* prefix, however in the future versions the version less access will be removed. +The legacy API is currently (as of Blockbook v0.4.0) also accessible without the _/v1/_ prefix, however in the future versions the version less access will be removed. ## API V2 @@ -34,8 +36,7 @@ API V2 is the current version of API. It can be used with all coin types that Bl Common principles used in API V2: - all amounts are transferred as strings, in the lowest denomination (satoshis, wei, ...), without decimal point -- empty fields are omitted. Empty field is a string of value *null* or *""*, a number of value *0*, an object of value *null* or an array without elements. The reason for this is that the interface serves many different coins which use only subset of the fields. Sometimes this principle can lead to slightly confusing results, for example when transaction version is 0, the field *version* is omitted. - +- empty fields are omitted. Empty field is a string of value _null_ or _""_, a number of value _0_, an object of value _null_ or an array without elements. The reason for this is that the interface serves many different coins which use only subset of the fields. Sometimes this principle can lead to slightly confusing results, for example when transaction version is 0, the field _version_ is omitted. ### REST API @@ -55,7 +56,9 @@ The following methods are supported: - [Balance history](#balance-history) #### Status page + Status page returns current status of Blockbook and connected backend. + ``` GET /api ``` @@ -67,7 +70,7 @@ Response: "blockbook": { "coin": "Bitcoin", "host": "blockbook", - "version": "0.3.6", + "version": "0.4.0", "gitCommit": "3d9ad91", "buildTime": "2019-05-17T14:34:00+00:00", "syncMode": true, @@ -99,6 +102,7 @@ Response: ``` #### Get block hash + ``` GET /api/v2/block-index/ ``` @@ -111,10 +115,12 @@ Response: } ``` -_Note: Blockbook always follows the main chain of the backend it is attached to. See notes on **Get Block** below_ +_Note: Blockbook always follows the main chain of the backend it is attached to. See notes on **Get Block** below_ #### Get transaction + Get transaction returns "normalized" data about transaction, which has the same general structure for all supported coins. It does not return coin specific fields (for example information about Zcash shielded addresses). + ``` GET /api/v2/tx/ ``` @@ -170,7 +176,7 @@ Response for Bitcoin-type coins: } ``` -Response for Ethereum-type coins. There is always only one *vin*, only one *vout*, possibly an array of *tokenTransfers* and *ethereumSpecific* part. Missing is *hex* field: +Response for Ethereum-type coins. There is always only one _vin_, only one _vout_, possibly an array of _tokenTransfers_ and _ethereumSpecific_ part. Missing is _hex_ field: ```javascript { @@ -224,6 +230,7 @@ Response for Ethereum-type coins. There is always only one *vin*, only one *vout ``` A note about the `blockTime` field: + - for already mined transaction (`confirmations > 0`), the field `blockTime` contains time of the block - for transactions in mempool (`confirmations == 0`), the field contains time when the running instance of Blockbook was first time notified about the transaction. This time may be different in different instances of Blockbook. @@ -295,17 +302,18 @@ GET /api/v2/address/
[?page=&pageSize=&from=&t ``` The optional query parameters: -- *page*: specifies page of returned transactions, starting from 1. If out of range, Blockbook returns the closest possible page. -- *pageSize*: number of transactions returned by call (default and maximum 1000) -- *from*, *to*: filter of the returned transactions *from* block height *to* block height (default no filter) -- *details*: specifies level of details returned by request (default *txids*) - - *basic*: return only address balances, without any transactions - - *tokens*: *basic* + tokens belonging to the address (applicable only to some coins) - - *tokenBalances*: *basic* + tokens with balances + belonging to the address (applicable only to some coins) - - *txids*: *tokenBalances* + list of txids, subject to *from*, *to* filter and paging - - *txslight*: *tokenBalances* + list of transaction with limited details (only data from index), subject to *from*, *to* filter and paging - - *txs*: *tokenBalances* + list of transaction with details, subject to *from*, *to* filter and paging -- *contract*: return only transactions which affect specified contract (applicable only to coins which support contracts) + +- _page_: specifies page of returned transactions, starting from 1. If out of range, Blockbook returns the closest possible page. +- _pageSize_: number of transactions returned by call (default and maximum 1000) +- _from_, _to_: filter of the returned transactions _from_ block height _to_ block height (default no filter) +- _details_: specifies level of details returned by request (default _txids_) + - _basic_: return only address balances, without any transactions + - _tokens_: _basic_ + tokens belonging to the address (applicable only to some coins) + - _tokenBalances_: _basic_ + tokens with balances + belonging to the address (applicable only to some coins) + - _txids_: _tokenBalances_ + list of txids, subject to _from_, _to_ filter and paging + - _txslight_: _tokenBalances_ + list of transaction with limited details (only data from index), subject to _from_, _to_ filter and paging + - _txs_: _tokenBalances_ + list of transaction with details, subject to _from_, _to_ filter and paging +- _contract_: return only transactions which affect specified contract (applicable only to coins which support contracts) Response: @@ -331,30 +339,29 @@ Response: #### Get xpub -Returns balances and transactions of an xpub or output descriptor, applicable only for Bitcoin-type coins. +Returns balances and transactions of an xpub or output descriptor, applicable only for Bitcoin-type coins. Blockbook supports BIP44, BIP49, BIP84 and BIP86 (Taproot) derivation schemes, using either xpubs or output descriptors (see https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md) -* Xpubs +- Xpubs - Blockbook expects xpub at level 3 derivation path, i.e. *m/purpose'/coin_type'/account'/*. Blockbook completes the *change/address_index* part of the path when deriving addresses. + Blockbook expects xpub at level 3 derivation path, i.e. _m/purpose'/coin_type'/account'/_. Blockbook completes the _change/address_index_ part of the path when deriving addresses. The BIP version is determined by the prefix of the xpub. The prefixes for each coin are defined by fields `xpub_magic`, `xpub_magic_segwit_p2sh`, `xpub_magic_segwit_native` in the [trezor-common](https://github.com/trezor/trezor-common/tree/master/defs/bitcoin) library. If the prefix is not recognized, Blockbook defaults to BIP44 derivation scheme. -* Output descriptors - +- Output descriptors + Output descriptors are in the form `([][//*])[#checkum]`, for example `pkh([5c9e228d/44'/0'/0']xpub6BgBgses...Mj92pReUsQ/<0;1>/*)#abcd` - + Parameters `type` and `xpub` are mandatory, the rest is optional - + Blockbook supports a limited set of `type`s: + - BIP44: `pkh(xpub)` - BIP49: `sh(wpkh(xpub))` - BIP84: `wpkh(xpub)` - BIP86 (Taproot single key): `tr(xpub)` - - Parameter `change` can be a single number or a list of change indexes, specified either in the format `` or `{index1,index2,...}`. If the parameter `change` is not specified, Blockbook defaults to `<0;1>`. - + Parameter `change` can be a single number or a list of change indexes, specified either in the format `` or `{index1,index2,...}`. If the parameter `change` is not specified, Blockbook defaults to `<0;1>`. The returned transactions are sorted by block height, newest blocks first. @@ -363,19 +370,20 @@ GET /api/v2/xpub/[?page=&pageSize=&from=[?confirmed=true] @@ -443,35 +451,35 @@ Response: ```javascript [ { - "txid": "13d26cd939bf5d155b1c60054e02d9c9b832a85e6ec4f2411be44b6b5a2842e9", - "vout": 0, - "value": "1422303206539", - "confirmations": 0, - "lockTime": 2648100 + txid: "13d26cd939bf5d155b1c60054e02d9c9b832a85e6ec4f2411be44b6b5a2842e9", + vout: 0, + value: "1422303206539", + confirmations: 0, + lockTime: 2648100, }, { - "txid": "a79e396a32e10856c97b95f43da7e9d2b9a11d446f7638dbd75e5e7603128cac", - "vout": 1, - "value": "39748685", - "height": 2648043, - "confirmations": 47, - "coinbase": true + txid: "a79e396a32e10856c97b95f43da7e9d2b9a11d446f7638dbd75e5e7603128cac", + vout: 1, + value: "39748685", + height: 2648043, + confirmations: 47, + coinbase: true, }, { - "txid": "de4f379fdc3ea9be063e60340461a014f372a018d70c3db35701654e7066b3ef", - "vout": 0, - "value": "122492339065", - "height": 2646043, - "confirmations": 2047 + txid: "de4f379fdc3ea9be063e60340461a014f372a018d70c3db35701654e7066b3ef", + vout: 0, + value: "122492339065", + height: 2646043, + confirmations: 2047, }, { - "txid": "9e8eb9b3d2e8e4b5d6af4c43a9196dfc55a05945c8675904d8c61f404ea7b1e9", - "vout": 0, - "value": "142771322208", - "height": 2644885, - "confirmations": 3205 - } -] + txid: "9e8eb9b3d2e8e4b5d6af4c43a9196dfc55a05945c8675904d8c61f404ea7b1e9", + vout: 0, + value: "142771322208", + height: 2644885, + confirmations: 3205, + }, +]; ``` #### Get block @@ -571,6 +579,7 @@ Response: ] } ``` + _Note: Blockbook always follows the main chain of the backend it is attached to. If there is a rollback-reorg in the backend, Blockbook will also do rollback. When you ask for block by height, you will always get the main chain block. If you ask for block by hash, you may get the block from another fork but it is not guaranteed (backend may not keep it)_ #### Send transaction @@ -609,7 +618,8 @@ GET /api/v2/tickers-list/?timestamp= ``` The query parameters: -- *timestamp*: specifies a Unix timestamp to return available tickers for. + +- _timestamp_: specifies a Unix timestamp to return available tickers for. Example response: @@ -633,8 +643,9 @@ GET /api/v2/tickers/[?currency=×tamp=] ``` The optional query parameters: -- *currency*: specifies a currency of returned rate ("usd", "eur", "eth"...). If not specified, all available currencies will be returned. -- *timestamp*: a Unix timestamp that specifies a date to return currency rates for. If not specified, the last available rate will be returned. + +- _currency_: specifies a currency of returned rate ("usd", "eur", "eth"...). If not specified, all available currencies will be returned. +- _timestamp_: a Unix timestamp that specifies a date to return currency rates for. If not specified, the last available rate will be returned. Example response (no parameters): @@ -660,6 +671,7 @@ Example response (currency=usd): ``` Example error response (e.g. rate unavailable, incorrect currency...): + ```javascript { "ts":7980386400, @@ -678,14 +690,17 @@ GET /api/v2/balancehistory/?from=&to=[&fiatcur ``` Query parameters: -- *from*: specifies a start date as a Unix timestamp -- *to*: specifies an end date as a Unix timestamp + +- _from_: specifies a start date as a Unix timestamp +- _to_: specifies an end date as a Unix timestamp The optional query parameters: -- *fiatcurrency*: if specified, the response will contain fiat rate at the time of transaction. If not, all available currencies will be returned. -- *groupBy*: an interval in seconds, to group results by. Default is 3600 seconds. + +- _fiatcurrency_: if specified, the response will contain fiat rate at the time of transaction. If not, all available currencies will be returned. +- _groupBy_: an interval in seconds, to group results by. Default is 3600 seconds. Example response (fiatcurrency not specified): + ```javascript [ { @@ -720,26 +735,26 @@ Example response (fiatcurrency=usd): ```javascript [ { - "time": 1578391200, - "txs": 5, - "received": "5000000", - "sent": "0", - "sentToSelf":"0", - "rates": { - "usd": 7855.9 - } + time: 1578391200, + txs: 5, + received: "5000000", + sent: "0", + sentToSelf: "0", + rates: { + usd: 7855.9, + }, }, { - "time": 1578488400, - "txs": 1, - "received": "0", - "sent": "5000000", - "sentToSelf":"0", - "rates": { - "usd": 8283.11 - } - } -] + time: 1578488400, + txs: 1, + received: "0", + sent: "5000000", + sentToSelf: "0", + rates: { + usd: 8283.11, + }, + }, +]; ``` Example response (fiatcurrency=usd&groupBy=172800): @@ -747,16 +762,16 @@ Example response (fiatcurrency=usd&groupBy=172800): ```javascript [ { - "time": 1578355200, - "txs": 6, - "received": "5000000", - "sent": "5000000", - "sentToSelf":"0", - "rates": { - "usd": 7734.45 - } - } -] + time: 1578355200, + txs: 6, + received: "5000000", + sent: "5000000", + sentToSelf: "0", + rates: { + usd: 7734.45, + }, + }, +]; ``` The value of `sentToSelf` is the amount sent from the same address to the same address or within addresses of xpub. @@ -783,10 +798,10 @@ The websocket interface provides the following requests: The client can subscribe to the following events: -- `subscribeNewBlock` - new block added to blockchain +- `subscribeNewBlock` - new block added to blockchain - `subscribeNewTransaction` - new transaction added to blockchain (all addresses) -- `subscribeAddresses` - new transaction for given address (list of addresses) -- `subscribeFiatRates` - new currency rate ticker +- `subscribeAddresses` - new transaction for given address (list of addresses) +- `subscribeFiatRates` - new currency rate ticker There can be always only one subscription of given event per connection, i.e. new list of addresses replaces previous list of addresses. @@ -795,19 +810,21 @@ The subscribeNewTransaction event is not enabled by default. To enable support, _Note: If there is reorg on the backend (blockchain), you will get a new block hash with the same or even smaller height if the reorg is deeper_ Websocket communication format + ``` { "id":"1", //an id to help to identify the response - "method":"", + "method":"", "params": } ``` Example for subscribing to an address (or multiple addresses) + ``` { - "id":"1", - "method":"subscribeAddresses", + "id":"1", + "method":"subscribeAddresses", "params":{ "addresses":["mnYYiDCb2JZXnqEeXta1nkt5oCVe2RVhJj", "tb1qp0we5epypgj4acd2c4au58045ruud2pd6heuee"] } From 922bdc42e586d5e90af45704ea4bece702551d23 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 12 Oct 2022 23:58:16 +0200 Subject: [PATCH 090/530] Bump golang to 1.19.2, rocksdb to 7.7.2, additional go deps --- build/docker/bin/Dockerfile | 4 ++-- go.mod | 2 +- go.sum | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/build/docker/bin/Dockerfile b/build/docker/bin/Dockerfile index 326ed7e791..a8b0a6380e 100644 --- a/build/docker/bin/Dockerfile +++ b/build/docker/bin/Dockerfile @@ -11,8 +11,8 @@ RUN apt-get update && \ libzstd-dev liblz4-dev graphviz && \ apt-get clean ARG GOLANG_VERSION -ENV GOLANG_VERSION=go1.19 -ENV ROCKSDB_VERSION=v7.5.3 +ENV GOLANG_VERSION=go1.19.2 +ENV ROCKSDB_VERSION=v7.7.2 ENV GOPATH=/go ENV PATH=$PATH:$GOPATH/bin ENV CGO_CFLAGS="-I/opt/rocksdb/include" diff --git a/go.mod b/go.mod index 3a83c4a34e..2fe3bf725d 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/decred/dcrd/dcrutil/v3 v3.0.0 github.com/decred/dcrd/hdkeychain/v3 v3.0.0 github.com/decred/dcrd/txscript/v3 v3.0.0 - github.com/ethereum/go-ethereum v1.10.23 + github.com/ethereum/go-ethereum v1.10.25 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/protobuf v1.5.2 github.com/gorilla/websocket v1.4.2 diff --git a/go.sum b/go.sum index fc6b56937c..4c01569be6 100644 --- a/go.sum +++ b/go.sum @@ -119,8 +119,8 @@ github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaB github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.10.23 h1:Xk8XAT4/UuqcjMLIMF+7imjkg32kfVFKoeyQDaO2yWM= -github.com/ethereum/go-ethereum v1.10.23/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= +github.com/ethereum/go-ethereum v1.10.25 h1:5dFrKJDnYf8L6/5o42abCE6a9yJm9cs4EJVRyYMr55s= +github.com/ethereum/go-ethereum v1.10.25/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -256,7 +256,6 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe h1:khZWpHuxJNh2EGzBbaS6EQ2d6KxgK31WeG0TnlTMUD4= github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe/go.mod h1:0hw4tpGU+9slqN/DrevhjTMb0iR9esxzpCdx8I6/UzU= github.com/martinboehm/btcd v0.0.0-20190104121910-8e7c0427fee5/go.mod h1:rKQj/jGwFruYjpM6vN+syReFoR0DsLQaajhyH/5mwUE= -github.com/martinboehm/btcd v0.0.0-20211010165247-d1f65b0f30fa/go.mod h1:YGXD0z/xtFXFF5jFp1GaVnrKRlEADn4pD47Zu4xaLg0= github.com/martinboehm/btcd v0.0.0-20221101112928-408689e15809 h1:a3l5GCQYYyB4zDmtsB8gu+aB15earQxMG1W/S/zKcXs= github.com/martinboehm/btcd v0.0.0-20221101112928-408689e15809/go.mod h1:YGXD0z/xtFXFF5jFp1GaVnrKRlEADn4pD47Zu4xaLg0= github.com/martinboehm/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:NIviPmxe43yBgIB4HGB4w4kv9/s5kaDa/pi+wZAAxQo= From 50602bcaae3fb3df7e6e6f8c2cc9c1d95717b6ed Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 16 Oct 2022 11:00:38 +0200 Subject: [PATCH 091/530] Update display of contracts in explorer --- .gitignore | 1 + api/worker.go | 46 +++++++++++++++++++++------- db/rocksdb_ethereumtype.go | 4 +++ server/public_ethereumtype_test.go | 4 +-- static/templates/address.html | 48 +++++++++++++++++++++++++----- 5 files changed, 82 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 98fe00b344..5cc7d1f1b5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ build/*.deb .bin-image .deb-image \.idea/ +__debug* \ No newline at end of file diff --git a/api/worker.go b/api/worker.go index bab9544449..864c631409 100644 --- a/api/worker.go +++ b/api/worker.go @@ -569,6 +569,27 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) } } + } else if (len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) { + // fix contract name/symbol that was parsed as a string consisting of zeroes + blockchainContractInfo, err := w.chain.GetContractInfo(cd) + if err != nil { + glog.Errorf("GetContractInfo from chain error %v, contract %v", err, cd) + } else { + if len(blockchainContractInfo.Name) > 0 && blockchainContractInfo.Name[0] != 0 { + contractInfo.Name = blockchainContractInfo.Name + } else { + contractInfo.Name = "" + } + if len(blockchainContractInfo.Symbol) > 0 && blockchainContractInfo.Symbol[0] != 0 { + contractInfo.Symbol = blockchainContractInfo.Symbol + } else { + contractInfo.Symbol = "" + } + contractInfo.Decimals = blockchainContractInfo.Decimals + if err = w.db.StoreContractInfo(contractInfo); err != nil { + glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) + } + } } return contractInfo, validContract, nil } @@ -957,8 +978,10 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto totalResults = int(ca.TotalTxs) } else if filter.Vout == 0 { totalResults = int(ca.NonContractTxs) - } else if filter.Vout > 0 && filter.Vout-1 < len(ca.Contracts) { - totalResults = int(ca.Contracts[filter.Vout-1].Txs) + } else if filter.Vout == db.InternalTxIndexOffset { + totalResults = int(ca.InternalTxs) + } else if filter.Vout >= db.ContractIndexOffset && filter.Vout-db.ContractIndexOffset < len(ca.Contracts) { + totalResults = int(ca.Contracts[filter.Vout-db.ContractIndexOffset].Txs) } else if filter.Vout == AddressFilterVoutQueryNotNecessary { totalResults = 0 } @@ -972,16 +995,17 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto BalanceSat: *b, } } - // special handling if filtering for a contract, check the ballance of it in the blockchain - if len(filterDesc) > 0 && details >= AccountDetailsTokens { - t, err := w.getEthereumContractBalanceFromBlockchain(addrDesc, filterDesc, details) - if err != nil { - return nil, nil, nil, 0, 0, 0, 0, err - } - tokens = []Token{*t} - // switch off query for transactions, there are no transactions - filter.Vout = AddressFilterVoutQueryNotNecessary + } + // special handling if filtering for a contract, return the contract details even though the address had no transactions with it + if len(tokens) == 0 && len(filterDesc) > 0 && details >= AccountDetailsTokens { + t, err := w.getEthereumContractBalanceFromBlockchain(addrDesc, filterDesc, details) + if err != nil { + return nil, nil, nil, 0, 0, 0, 0, err } + tokens = []Token{*t} + // switch off query for transactions, there are no transactions + filter.Vout = AddressFilterVoutQueryNotNecessary + totalResults = -1 } return ba, tokens, ci, n, nonContractTxs, internalTxs, totalResults, nil } diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index b29a8fe9e4..b16429b330 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -812,6 +812,10 @@ func (d *RocksDB) storeContractInfo(wb *grocksdb.WriteBatch, contractInfo *bchai contractInfo = storedCI } wb.PutCF(d.cfh[cfContracts], key, packContractInfo(contractInfo)) + cacheKey := string(key) + cachedContractsMux.Lock() + delete(cachedContracts, cacheKey) + cachedContractsMux.Unlock() } return nil } diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 221ef57f8b..41d3164377 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -24,7 +24,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address 0.000000000123450123 FAKE

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

Confirmed

Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ERC20 Tokens
ContractTokensTransfers
Contract 740.001000123074 S741
Contract 130.000000001000123013 S131
ERC721 Tokens
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
ID 1 S205
Fee: 0.00008794500041041 FAKE
Unconfirmed Transaction!0 FAKE
ERC20 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
871.180000950184 S74
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
7.674999999999991915 S13
Fee: 0.000216368 FAKE
Unconfirmed Transaction!0 FAKE
`, + `Trezor Fake Coin Explorer

Address 0.000000000123450123 FAKE

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

Confirmed

Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ERC20 Tokens
ContractTokensTransfers
Contract 740.001000123074 S741
Contract 130.000000001000123013 S131
ERC721 Tokens
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
ID 1 S205
Fee: 0.00008794500041041 FAKE
Unconfirmed Transaction!0 FAKE
ERC20 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
871.180000950184 S74
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
7.674999999999991915 S13
Fee: 0.000216368 FAKE
Unconfirmed Transaction!0 FAKE
`, }, }, { @@ -33,7 +33,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address 0.000000000123450093 FAKE

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

Confirmed

Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ERC1155 Tokens
ContractTokensTransfers
Contract 1111 of ID 1776, 10 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0 FAKE
ERC1155 Token Transfers
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
Fee: 0.000081891755740665 FAKE
Unconfirmed Transaction!0 FAKE
`, + `Trezor Fake Coin Explorer

Address 0.000000000123450093 FAKE

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

Confirmed

Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ERC1155 Tokens
ContractTokensTransfers
Contract 1111 of ID 1776, 10 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0 FAKE
ERC1155 Token Transfers
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
Fee: 0.000081891755740665 FAKE
Unconfirmed Transaction!0 FAKE
`, }, }, { diff --git a/static/templates/address.html b/static/templates/address.html index 590da4a75e..c3bc0c9ec4 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -1,5 +1,5 @@ {{define "specific"}}{{$cs := .CoinShortcut}}{{$addr := .Address}}{{$data := .}} -

{{if $addr.ContractInfo}}Contract {{$addr.ContractInfo.Name}} ({{$addr.ContractInfo.Symbol}}){{else}}Address{{end}} {{formatAmount $addr.BalanceSat}} {{$cs}} +

{{if $addr.ContractInfo}}Contract {{$addr.ContractInfo.Name}}{{if $addr.ContractInfo.Symbol}} ({{$addr.ContractInfo.Symbol}}){{end}}{{else}}Address{{end}} {{formatAmount $addr.BalanceSat}} {{$cs}}

{{$addr.AddrStr}} @@ -10,6 +10,26 @@

Confirmed

{{- if eq .ChainType 1 -}} + {{if $addr.ContractInfo}} + {{if $addr.ContractInfo.Type}} + + + + + {{end}} + {{if $addr.ContractInfo.CreatedInBlock}} + + + + + {{end}} + {{if $addr.ContractInfo.DestructedInBlock}} + + + + + {{end}} + {{end}} @@ -158,19 +178,31 @@

Unconfirmed

{{- end}}{{if or $addr.Transactions $addr.Filter -}}

Transactions

- - - + + {{- if $addr.Tokens -}} - - + + {{- range $t := $addr.Tokens -}} - + {{if eq $t.Type "ERC20"}} + + {{- end -}} + {{- end -}} + {{- range $t := $addr.Tokens -}} + {{if eq $t.Type "ERC721"}} + + {{- end -}} + {{- end -}} + {{- range $t := $addr.Tokens -}} + {{if eq $t.Type "ERC1155"}} + + {{- end -}} {{- end -}} {{- end -}} -
+
From 3967565b30812d2b1033931175ad7e2ae935a164 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 18 Oct 2022 17:08:59 +0200 Subject: [PATCH 092/530] Rename goerli symbol to tGOR --- configs/coins/ethereum_testnet_goerli.json | 2 +- configs/coins/ethereum_testnet_goerli_archive.json | 2 +- configs/coins/ethereum_testnet_goerli_archive_consensus.json | 2 +- configs/coins/ethereum_testnet_goerli_consensus.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index c26df8211c..a612deb2a1 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli", - "shortcut": "gGOE", + "shortcut": "tGOR", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli" }, diff --git a/configs/coins/ethereum_testnet_goerli_archive.json b/configs/coins/ethereum_testnet_goerli_archive.json index 6c3ee919b4..ee0a22befd 100644 --- a/configs/coins/ethereum_testnet_goerli_archive.json +++ b/configs/coins/ethereum_testnet_goerli_archive.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli Archive", - "shortcut": "gGOE", + "shortcut": "tGOR", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli_archive" }, diff --git a/configs/coins/ethereum_testnet_goerli_archive_consensus.json b/configs/coins/ethereum_testnet_goerli_archive_consensus.json index 6f678f02e8..43b3a998d3 100644 --- a/configs/coins/ethereum_testnet_goerli_archive_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_archive_consensus.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli Archive", - "shortcut": "tROP", + "shortcut": "tGOR", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli_archive_consensus", "execution_alias": "ethereum_testnet_goerli_archive" diff --git a/configs/coins/ethereum_testnet_goerli_consensus.json b/configs/coins/ethereum_testnet_goerli_consensus.json index ab95e52119..74cc596cc5 100644 --- a/configs/coins/ethereum_testnet_goerli_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_consensus.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli", - "shortcut": "tROP", + "shortcut": "tGOR", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli_consensus", "execution_alias": "ethereum_testnet_goerli" From 0c5af954c75e50983925fa9bdb12b518652da9a6 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 3 Nov 2022 00:20:11 +0100 Subject: [PATCH 093/530] Add panic handler to ETH transfer event parsers --- bchain/coins/eth/contract.go | 37 +++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go index 4cbb9541f5..82c89908a5 100644 --- a/bchain/coins/eth/contract.go +++ b/bchain/coins/eth/contract.go @@ -44,7 +44,12 @@ func addressFromPaddedHex(s string) (string, error) { return a.String(), nil } -func processTransferEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { +func processTransferEvent(l *bchain.RpcLog) (transfer *bchain.TokenTransfer, err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("processTransferEvent recovered from panic %v", r) + } + }() tl := len(l.Topics) var ttt bchain.TokenType var value big.Int @@ -63,11 +68,12 @@ func processTransferEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { } else { return nil, nil } - from, err := addressFromPaddedHex(l.Topics[1]) + var from, to string + from, err = addressFromPaddedHex(l.Topics[1]) if err != nil { return nil, err } - to, err := addressFromPaddedHex(l.Topics[2]) + to, err = addressFromPaddedHex(l.Topics[2]) if err != nil { return nil, err } @@ -80,16 +86,22 @@ func processTransferEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { }, nil } -func processERC1155TransferSingleEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { +func processERC1155TransferSingleEvent(l *bchain.RpcLog) (transfer *bchain.TokenTransfer, err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("processERC1155TransferSingleEvent recovered from panic %v", r) + } + }() tl := len(l.Topics) if tl != 4 { return nil, nil } - from, err := addressFromPaddedHex(l.Topics[2]) + var from, to string + from, err = addressFromPaddedHex(l.Topics[2]) if err != nil { return nil, err } - to, err := addressFromPaddedHex(l.Topics[3]) + to, err = addressFromPaddedHex(l.Topics[3]) if err != nil { return nil, err } @@ -115,16 +127,22 @@ func processERC1155TransferSingleEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, }, nil } -func processERC1155TransferBatchEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) { +func processERC1155TransferBatchEvent(l *bchain.RpcLog) (transfer *bchain.TokenTransfer, err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("processERC1155TransferBatchEvent recovered from panic %v", r) + } + }() tl := len(l.Topics) if tl < 4 { return nil, nil } - from, err := addressFromPaddedHex(l.Topics[2]) + var from, to string + from, err = addressFromPaddedHex(l.Topics[2]) if err != nil { return nil, err } - to, err := addressFromPaddedHex(l.Topics[3]) + to, err = addressFromPaddedHex(l.Topics[3]) if err != nil { return nil, err } @@ -179,6 +197,7 @@ func processERC1155TransferBatchEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, MultiTokenValues: idValues, }, nil } + func contractGetTransfersFromLog(logs []*bchain.RpcLog) (bchain.TokenTransfers, error) { var r bchain.TokenTransfers var tt *bchain.TokenTransfer From e47760149a300f56eb96dbd467746a3e6cd72404 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Sun, 25 Sep 2022 23:35:55 +0200 Subject: [PATCH 094/530] feat: add ethereum_testnet_sepolia --- bchain/coins/blockchain.go | 2 + bchain/coins/eth/ethrpc.go | 5 ++ configs/coins/ethereum_testnet_sepolia.json | 70 +++++++++++++++++ .../ethereum_testnet_sepolia_archive.json | 75 +++++++++++++++++++ ...eum_testnet_sepolia_archive_consensus.json | 52 +++++++++++++ .../ethereum_testnet_sepolia_consensus.json | 52 +++++++++++++ docs/ports.md | 1 + 7 files changed, 257 insertions(+) create mode 100644 configs/coins/ethereum_testnet_sepolia.json create mode 100644 configs/coins/ethereum_testnet_sepolia_archive.json create mode 100644 configs/coins/ethereum_testnet_sepolia_archive_consensus.json create mode 100644 configs/coins/ethereum_testnet_sepolia_consensus.json diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index fa3195a58a..613b44c25b 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -74,6 +74,8 @@ func init() { BlockChainFactories["Ethereum Testnet Ropsten Archive"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Goerli"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Goerli Archive"] = eth.NewEthereumRPC + BlockChainFactories["Ethereum Testnet Sepolia"] = eth.NewEthereumRPC + BlockChainFactories["Ethereum Testnet Sepolia Archive"] = eth.NewEthereumRPC BlockChainFactories["Bcash"] = bch.NewBCashRPC BlockChainFactories["Bcash Testnet"] = bch.NewBCashRPC BlockChainFactories["Bgold"] = btg.NewBGoldRPC diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 986ec9990a..2077d10250 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -34,6 +34,8 @@ const ( TestNet EthereumNet = 3 // TestNetGoerli is Goerli test network TestNetGoerli EthereumNet = 5 + // TestNetSepolia is Sepolia test network + TestNetSepolia EthereumNet = 11155111 ) // Configuration represents json config file @@ -175,6 +177,9 @@ func (b *EthereumRPC) Initialize() error { case TestNetGoerli: b.Testnet = true b.Network = "goerli" + case TestNetSepolia: + b.Testnet = true + b.Network = "sepolia" default: return errors.Errorf("Unknown network id %v", id) } diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json new file mode 100644 index 0000000000..24d192245e --- /dev/null +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -0,0 +1,70 @@ +{ + "coin": { + "name": "Ethereum Testnet Sepolia", + "shortcut": "gSEP", + "label": "Ethereum Sepolia", + "alias": "ethereum_testnet_sepolia" + }, + "ports": { + "backend_rpc": 18076, + "backend_message_queue": 0, + "backend_p2p": 48376, + "backend_http": 18176, + "backend_authrpc": 18576, + "blockbook_internal": 19076, + "blockbook_public": 19176 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-ethereum-testnet-sepolia", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "1.10.23-d901d853", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", + "verification_type": "gpg", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --sepolia --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + } + } + }, + "blockbook": { + "package_name": "blockbook-ethereum-testnet-sepolia", + "system_user": "blockbook-ethereum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 3000, + "additional_params": { + "consensusNodeVersion": "http://localhost:17576/eth/v1/node/version", + "mempoolTxTimeoutHours": 12, + "queryBackendOnMempoolResync": false + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json new file mode 100644 index 0000000000..030066622a --- /dev/null +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -0,0 +1,75 @@ +{ + "coin": { + "name": "Ethereum Testnet Sepolia Archive", + "shortcut": "gSEP", + "label": "Ethereum Sepolia", + "alias": "ethereum_testnet_sepolia_archive" + }, + "ports": { + "backend_rpc": 18086, + "backend_message_queue": 0, + "backend_p2p": 48386, + "backend_http": 18186, + "backend_authrpc": 18586, + "blockbook_internal": 19086, + "blockbook_public": 19186 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-ethereum-testnet-sepolia-archive", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "1.10.23-d901d853", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", + "verification_type": "gpg", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --sepolia --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + } + } + }, + "blockbook": { + "package_name": "blockbook-ethereum-testnet-sepolia-archive", + "system_user": "blockbook-ethereum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "-workers=16", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 3000, + "additional_params": { + "consensusNodeVersion": "http://localhost:17586/eth/v1/node/version", + "address_aliases": true, + "mempoolTxTimeoutHours": 12, + "processInternalTransactions": true, + "queryBackendOnMempoolResync": false, + "fiat_rates-disabled": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json new file mode 100644 index 0000000000..0215fb9d95 --- /dev/null +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -0,0 +1,52 @@ +{ + "coin": { + "name": "Ethereum Testnet Sepolia Archive", + "shortcut": "gSEP", + "label": "Ethereum Sepolia", + "alias": "ethereum_testnet_sepolia_archive_consensus", + "execution_alias": "ethereum_testnet_sepolia_archive" + }, + "ports": { + "backend_rpc": 18086, + "backend_message_queue": 0, + "backend_p2p": 48386, + "backend_http": 18186, + "backend_authrpc": 18586, + "blockbook_internal": 19086, + "blockbook_public": 19186 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "3.1.1", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", + "verification_type": "sha256", + "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", + "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "wget https://github.com/eth-clients/merge-testnets/raw/302fe27afdc7a9d15b1766a0c0a9d64319140255/sepolia/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", + "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json new file mode 100644 index 0000000000..d33fd9d88e --- /dev/null +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -0,0 +1,52 @@ +{ + "coin": { + "name": "Ethereum Testnet Sepolia", + "shortcut": "gSEP", + "label": "Ethereum Sepolia", + "alias": "ethereum_testnet_sepolia_consensus", + "execution_alias": "ethereum_testnet_sepolia" + }, + "ports": { + "backend_rpc": 18076, + "backend_message_queue": 0, + "backend_p2p": 48376, + "backend_http": 18176, + "backend_authrpc": 18576, + "blockbook_internal": 19076, + "blockbook_public": 19176 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-ethereum-testnet-sepolia-consensus", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "3.1.1", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", + "verification_type": "sha256", + "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", + "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "wget https://github.com/eth-clients/merge-testnets/raw/302fe27afdc7a9d15b1766a0c0a9d64319140255/sepolia/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", + "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/docs/ports.md b/docs/ports.md index 7d7a6b11a4..295866a0d2 100644 --- a/docs/ports.md +++ b/docs/ports.md @@ -49,6 +49,7 @@ | Bitcoin Signet | 19020 | 19120 | 18020 | 48320 | | Bitcoin Regtest | 19021 | 19121 | 18021 | 48321 | | Ethereum Goerli | 19026 | 19126 | 18026 | 48326 p2p | +| Ethereum Sepolia | 19176 | 19176 | 18076 | 48376 p2p | | Bitcoin Testnet | 19030 | 19130 | 18030 | 48330 | | Bitcoin Cash Testnet | 19031 | 19131 | 18031 | 48331 | | Zcash Testnet | 19032 | 19132 | 18032 | 48332 | From 096bab30a86fed73b36ec47b6e36989c14511d66 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 4 Nov 2022 18:08:48 +0100 Subject: [PATCH 095/530] Bump backend geth to v1.10.26 and prysma to v 3.1.2 --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_archive_consensus.json | 10 +++++----- configs/coins/ethereum_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_goerli.json | 10 +++++----- configs/coins/ethereum_testnet_goerli_archive.json | 10 +++++----- .../ethereum_testnet_goerli_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_goerli_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_ropsten.json | 10 +++++----- configs/coins/ethereum_testnet_ropsten_archive.json | 10 +++++----- .../ethereum_testnet_ropsten_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_ropsten_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- .../ethereum_testnet_sepolia_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_consensus.json | 10 +++++----- 16 files changed, 80 insertions(+), 80 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 1de8943cfb..120cc9ee69 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.23-d901d853", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", + "version": "1.10.26-e5eb32ac", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz.asc" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 0332bc526f..1d93dd3680 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.23-d901d853", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", + "version": "1.10.26-e5eb32ac", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz.asc" } } }, diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 554838d90a..2983664449 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.1", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", + "version": "3.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", + "verification_source": "56abf71981d3bfd48b04e8bd09544513a0512202b46e9f239a762922c84ac18c", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", - "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-arm64", + "verification_source": "1701df47dbb6598a9215f82a313e1531c211bb912618dc3d0cd33e6e67c5ebb5" } } }, diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 40d7fed53d..088d3cdab6 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.1", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", + "version": "3.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", + "verification_source": "56abf71981d3bfd48b04e8bd09544513a0512202b46e9f239a762922c84ac18c", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/geth/jwtsecret 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", - "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-arm64", + "verification_source": "1701df47dbb6598a9215f82a313e1531c211bb912618dc3d0cd33e6e67c5ebb5" } } }, diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index a612deb2a1..8ab40d4d65 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-goerli", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.23-d901d853", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", + "version": "1.10.26-e5eb32ac", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz.asc" } } }, diff --git a/configs/coins/ethereum_testnet_goerli_archive.json b/configs/coins/ethereum_testnet_goerli_archive.json index ee0a22befd..955019585b 100644 --- a/configs/coins/ethereum_testnet_goerli_archive.json +++ b/configs/coins/ethereum_testnet_goerli_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-goerli-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.23-d901d853", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", + "version": "1.10.26-e5eb32ac", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz.asc" } } }, diff --git a/configs/coins/ethereum_testnet_goerli_archive_consensus.json b/configs/coins/ethereum_testnet_goerli_archive_consensus.json index 43b3a998d3..b1d7443349 100644 --- a/configs/coins/ethereum_testnet_goerli_archive_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-goerli-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.1", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", + "version": "3.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", + "verification_source": "56abf71981d3bfd48b04e8bd09544513a0512202b46e9f239a762922c84ac18c", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17506 --rpc-port=17507 --monitoring-port=17508 --p2p-tcp-port=13506 --p2p-udp-port=12506 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", - "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-arm64", + "verification_source": "1701df47dbb6598a9215f82a313e1531c211bb912618dc3d0cd33e6e67c5ebb5" } } }, diff --git a/configs/coins/ethereum_testnet_goerli_consensus.json b/configs/coins/ethereum_testnet_goerli_consensus.json index 74cc596cc5..8309214c2f 100644 --- a/configs/coins/ethereum_testnet_goerli_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-goerli-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.1", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", + "version": "3.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", + "verification_source": "56abf71981d3bfd48b04e8bd09544513a0512202b46e9f239a762922c84ac18c", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --prater --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17526 --rpc-port=17527 --monitoring-port=17528 --p2p-tcp-port=13526 --p2p-udp-port=12526 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_goerli/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", - "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-arm64", + "verification_source": "1701df47dbb6598a9215f82a313e1531c211bb912618dc3d0cd33e6e67c5ebb5" } } }, diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 394fcf81fe..ee91692ece 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-ropsten", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.23-d901d853", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", + "version": "1.10.26-e5eb32ac", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --override.terminaltotaldifficulty 50000000000000000 --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz.asc" } } }, diff --git a/configs/coins/ethereum_testnet_ropsten_archive.json b/configs/coins/ethereum_testnet_ropsten_archive.json index bd924ab43b..47408d1b27 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive.json +++ b/configs/coins/ethereum_testnet_ropsten_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-ropsten-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.23-d901d853", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", + "version": "1.10.26-e5eb32ac", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ropsten --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --override.terminaltotaldifficulty 50000000000000000 --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz.asc" } } }, diff --git a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json index fd8de2418d..dc3e579dd6 100644 --- a/configs/coins/ethereum_testnet_ropsten_archive_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-ropsten-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.1", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", + "version": "3.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", + "verification_source": "56abf71981d3bfd48b04e8bd09544513a0512202b46e9f239a762922c84ac18c", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", - "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-arm64", + "verification_source": "1701df47dbb6598a9215f82a313e1531c211bb912618dc3d0cd33e6e67c5ebb5" } } }, diff --git a/configs/coins/ethereum_testnet_ropsten_consensus.json b/configs/coins/ethereum_testnet_ropsten_consensus.json index 4bdd60370f..3ac7adca4d 100644 --- a/configs/coins/ethereum_testnet_ropsten_consensus.json +++ b/configs/coins/ethereum_testnet_ropsten_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-testnet-ropsten-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.1", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", + "version": "3.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", + "verification_source": "56abf71981d3bfd48b04e8bd09544513a0512202b46e9f239a762922c84ac18c", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --ropsten --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13536 --p2p-udp-port=12536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_ropsten/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", - "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-arm64", + "verification_source": "1701df47dbb6598a9215f82a313e1531c211bb912618dc3d0cd33e6e67c5ebb5" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 24d192245e..4503e73029 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.23-d901d853", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", + "version": "1.10.26-e5eb32ac", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --sepolia --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz.asc" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 030066622a..ae4f12b36f 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.23-d901d853", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz", + "version": "1.10.26-e5eb32ac", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.23-d901d853.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --sepolia --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.23-d901d853.tar.gz.asc" + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-arm64-1.10.26-e5eb32ac.tar.gz.asc" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json index 0215fb9d95..90b7a2aeec 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.1", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", + "version": "3.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", + "verification_source": "56abf71981d3bfd48b04e8bd09544513a0512202b46e9f239a762922c84ac18c", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", - "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-arm64", + "verification_source": "1701df47dbb6598a9215f82a313e1531c211bb912618dc3d0cd33e6e67c5ebb5" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json index d33fd9d88e..bd69fc30d5 100644 --- a/configs/coins/ethereum_testnet_sepolia_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.1", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64", + "version": "3.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "917c37f41506182da7061aa2e9a15bdecc5d30eaafdc2688c9b0fba7073a7d05", + "verification_source": "56abf71981d3bfd48b04e8bd09544513a0512202b46e9f239a762922c84ac18c", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/geth/jwtsecret --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-arm64", - "verification_source": "97665ac0ff54c9f8f97c99949519d13eea964a09decc17e2830b14c9d6dc1b24" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v3.1.2/beacon-chain-v3.1.2-linux-arm64", + "verification_source": "1701df47dbb6598a9215f82a313e1531c211bb912618dc3d0cd33e6e67c5ebb5" } } }, From a939b2d93f0cc85b6ed79755eb7a0bce1088b0c7 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 20 Oct 2022 19:24:53 +0200 Subject: [PATCH 096/530] Explorer redesign part 1 --- server/public.go | 85 +++++- server/public_test.go | 28 ++ static/css/TTHoves/TTHoves-Black.woff | Bin 0 -> 68144 bytes static/css/TTHoves/TTHoves-Black.woff2 | Bin 0 -> 43712 bytes static/css/TTHoves/TTHoves-Bold.woff | Bin 0 -> 69640 bytes static/css/TTHoves/TTHoves-Bold.woff2 | Bin 0 -> 44880 bytes static/css/TTHoves/TTHoves-BoldItalic.woff | Bin 0 -> 74328 bytes static/css/TTHoves/TTHoves-BoldItalic.woff2 | Bin 0 -> 47584 bytes static/css/TTHoves/TTHoves-DemiBold.woff | Bin 0 -> 70128 bytes static/css/TTHoves/TTHoves-DemiBold.woff2 | Bin 0 -> 45188 bytes static/css/TTHoves/TTHoves-ExtraBold.woff | Bin 0 -> 70156 bytes static/css/TTHoves/TTHoves-ExtraBold.woff2 | Bin 0 -> 45504 bytes static/css/TTHoves/TTHoves-ExtraLight.woff | Bin 0 -> 70636 bytes static/css/TTHoves/TTHoves-ExtraLight.woff2 | Bin 0 -> 45376 bytes static/css/TTHoves/TTHoves-Light.woff | Bin 0 -> 70596 bytes static/css/TTHoves/TTHoves-Light.woff2 | Bin 0 -> 45220 bytes static/css/TTHoves/TTHoves-Medium.woff | Bin 0 -> 70020 bytes static/css/TTHoves/TTHoves-Medium.woff2 | Bin 0 -> 45168 bytes static/css/TTHoves/TTHoves-Regular.woff | Bin 0 -> 69088 bytes static/css/TTHoves/TTHoves-Regular.woff2 | Bin 0 -> 44244 bytes static/css/TTHoves/TTHoves.css | 39 +++ static/css/main2.css | 303 ++++++++++++++++++++ static/templates/address.html | 4 +- static/templates/base.html | 94 +++--- static/templates/block.html | 16 +- static/templates/blocks.html | 29 +- static/templates/index.html | 72 ++--- static/templates/mempool.html | 18 +- static/templates/paging.html | 27 +- static/templates/xpub.html | 4 +- 30 files changed, 589 insertions(+), 130 deletions(-) create mode 100644 static/css/TTHoves/TTHoves-Black.woff create mode 100644 static/css/TTHoves/TTHoves-Black.woff2 create mode 100644 static/css/TTHoves/TTHoves-Bold.woff create mode 100644 static/css/TTHoves/TTHoves-Bold.woff2 create mode 100644 static/css/TTHoves/TTHoves-BoldItalic.woff create mode 100644 static/css/TTHoves/TTHoves-BoldItalic.woff2 create mode 100644 static/css/TTHoves/TTHoves-DemiBold.woff create mode 100644 static/css/TTHoves/TTHoves-DemiBold.woff2 create mode 100644 static/css/TTHoves/TTHoves-ExtraBold.woff create mode 100644 static/css/TTHoves/TTHoves-ExtraBold.woff2 create mode 100644 static/css/TTHoves/TTHoves-ExtraLight.woff create mode 100644 static/css/TTHoves/TTHoves-ExtraLight.woff2 create mode 100644 static/css/TTHoves/TTHoves-Light.woff create mode 100644 static/css/TTHoves/TTHoves-Light.woff2 create mode 100644 static/css/TTHoves/TTHoves-Medium.woff create mode 100644 static/css/TTHoves/TTHoves-Medium.woff2 create mode 100644 static/css/TTHoves/TTHoves-Regular.woff create mode 100644 static/css/TTHoves/TTHoves-Regular.woff2 create mode 100644 static/css/TTHoves/TTHoves.css create mode 100644 static/css/main2.css diff --git a/server/public.go b/server/public.go index ce860869f2..416ddae4bf 100644 --- a/server/public.go +++ b/server/public.go @@ -460,6 +460,9 @@ func (s *PublicServer) parseTemplates() []*template.Template { "formatUnixTime": formatUnixTime, "formatAmount": s.formatAmount, "formatAmountWithDecimals": formatAmountWithDecimals, + "formatInt64": formatInt64, + "formatInt": formatInt, + "formatUint32": formatUint32, "setTxToTemplateData": setTxToTemplateData, "feePerByte": feePerByte, "isOwnAddress": isOwnAddress, @@ -528,16 +531,70 @@ func (s *PublicServer) parseTemplates() []*template.Template { return t } -func formatUnixTime(ut int64) string { +func relativeTime(d int64) string { + var u string + if d < 60 { + if d == 1 { + u = " sec" + } else { + u = " secs" + } + } else if d < 3600 { + d /= 60 + if d == 1 { + u = " min" + } else { + u = " mins" + } + } else if d < 3600*24 { + d /= 3600 + if d == 1 { + u = " hour" + } else { + u = " hours" + } + } else { + d /= 3600 * 24 + if d == 1 { + u = " day" + } else { + u = " days" + } + } + return strconv.FormatInt(d, 10) + u +} + +func formatUnixTime(ut int64) template.HTML { t := time.Unix(ut, 0) return formatTime(&t) } -func formatTime(t *time.Time) string { +func formatTime(t *time.Time) template.HTML { if t == nil { return "" } - return t.Format(time.RFC1123) + u := t.Unix() + if u <= 0 { + return "" + } + d := time.Now().Unix() - u + f := t.UTC().Format("2006-01-02 15:04:05") + if d < 0 { + return template.HTML(f) + } + r := relativeTime(d) + if d > 3600*24 { + d = d % (3600 * 24) + if d >= 3600 { + r += " " + relativeTime(d) + } + } else if d > 3600 { + d = d % 3600 + if d >= 60 { + r += " " + relativeTime(d) + } + } + return template.HTML(`` + r + " ago") } func toJSON(data interface{}) string { @@ -564,6 +621,28 @@ func formatAmountWithDecimals(a *api.Amount, d int) string { return a.DecimalString(d) } +func formatInt(i int) template.HTML { + return formatInt64(int64(i)) +} + +func formatUint32(i uint32) template.HTML { + return formatInt64(int64(i)) +} + +func formatInt64(i int64) template.HTML { + s := strconv.FormatInt(i, 10) + t := (len(s) - 1) / 3 + if t <= 0 { + return template.HTML(s) + } + t *= 3 + rv := s[:len(s)-t] + for i := len(s) - t; i < len(s); i += 3 { + rv += `` + s[i:i+3] + "" + } + return template.HTML(rv) +} + // called from template to support txdetail.html functionality func setTxToTemplateData(td *TemplateData, tx *api.Tx) *TemplateData { td.Tx = tx diff --git a/server/public_test.go b/server/public_test.go index b5ba334c97..c21622b0a5 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -4,11 +4,13 @@ package server import ( "encoding/json" + "html/template" "io/ioutil" "net/http" "net/http/httptest" "net/url" "os" + "reflect" "strconv" "strings" "testing" @@ -1607,3 +1609,29 @@ func Test_PublicServer_BitcoinType(t *testing.T) { socketioTestsBitcoinType(t, ts) websocketTestsBitcoinType(t, ts) } + +func Test_formatInt64(t *testing.T) { + tests := []struct { + name string + n int64 + want template.HTML + }{ + {"1", 1, "1"}, + {"13", 13, "13"}, + {"123", 123, "123"}, + {"1234", 1234, `1234`}, + {"91234", 91234, `91234`}, + {"891234", 891234, `891234`}, + {"7891234", 7891234, `7891234`}, + {"67891234", 67891234, `67891234`}, + {"567891234", 567891234, `567891234`}, + {"4567891234", 4567891234, `4567891234`}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := formatInt64(tt.n); !reflect.DeepEqual(got, tt.want) { + t.Errorf("formatInt64() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/static/css/TTHoves/TTHoves-Black.woff b/static/css/TTHoves/TTHoves-Black.woff new file mode 100644 index 0000000000000000000000000000000000000000..e30243577e44073034318fd071bfddb617dc59ec GIT binary patch literal 68144 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 z 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 Z>rK(-sHpS&+h&fBw%*!ImGmd`{{ghvrMdtB literal 0 HcmV?d00001 diff --git a/static/css/TTHoves/TTHoves-Black.woff2 b/static/css/TTHoves/TTHoves-Black.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..c81505974737f82911f92e47bbd0959f0e39d66f GIT binary patch literal 43712 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 PynHj~%j$b%Dbx<5lQr)qj>+=tQ2PTt@VEq{CF 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 O+;017?a4V0ApZ}5I{cUb literal 0 HcmV?d00001 diff --git a/static/css/TTHoves/TTHoves-Bold.woff b/static/css/TTHoves/TTHoves-Bold.woff new file mode 100644 index 0000000000000000000000000000000000000000..96630ca3cf1aab9ebe4623da24c25796e9810fc7 GIT binary patch literal 69640 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

+-bO~KX2zMRcErB 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 Ok2F6087CM4JO2Z4K4kv@ literal 0 HcmV?d00001 diff --git a/static/css/TTHoves/TTHoves-Bold.woff2 b/static/css/TTHoves/TTHoves-Bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..8f1fd69015583711f53bf5955b2202bbd1a83903 GIT binary patch literal 44880 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 literal 0 HcmV?d00001 diff --git a/static/css/TTHoves/TTHoves-BoldItalic.woff b/static/css/TTHoves/TTHoves-BoldItalic.woff new file mode 100644 index 0000000000000000000000000000000000000000..3c76f859b6a6bbf2d2202089797dfc4f01de44c1 GIT binary patch literal 74328 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%0DSq{ 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 SQC2dPJ}l`fngpG?kpBfcAV~=T literal 0 HcmV?d00001 diff --git a/static/css/TTHoves/TTHoves-DemiBold.woff b/static/css/TTHoves/TTHoves-DemiBold.woff new file mode 100644 index 0000000000000000000000000000000000000000..6dc9af24c3cebbf05cb8b59c86381ae83fcb1658 GIT binary patch literal 70128 zcmZsCV{|1=wD!p)nb@{Dv5kq7iEU%zEtRb!2~~|K}ktt|I$oH}_SD{~tnr^2bStiiraN z>!Dxr?iYzbMqBo#go3;>0C05$01)p004_#P4DTTl%BnvBfDLN^0M#D=V7j{7**cL| zW@P!Q^BPiQ2r@SWe(BczzHFfX!{;Xe+}zsT z3;@940|1Jn0AN-5XB~gZEKChezW6l0Y&iY{x~R^*#h3C+7xN{Pe1RN_0EXAX#>wrg zp>$tV+W-It`tKiI+}3u+Uv`Mi003;@7a~>_#fxnW-M;P%R{vFx=nKS<@qk2ILmN{7 zplAH+`JMm(7{XZGeoK2hpc4Qv&<_A$wEzI@{&e)wKmLE8ev6KeO@q}(;=2dHfggQw z02XKKga6ByM~%SWk3YXdW`f^*iO_&=|7igLw-sXp69a?HH#guLl0Fjk9S=e|I0?+b^MU=sIuoP3*s zhu=U>V~*+x-NsT#@J~#|5v`#!FepVb(6|AC!T$U@vGK9peCwD_Ka-8PdiYYOW&HgO zc_gE*iDn6j3%a|%0h-N_UcPt-QhY;o)G>xGJ?#ohWYT(^nQ;Ds9~nlOm9@wGpr-@h5YOL2P zLSd1vzQ-_oZJQ2R22l2qlQRY1p1YXR*LlJz&#EQLic-F1Lh7%pz5|1I`ZZZ)k}VK4 zOQJ5{)GE!>>y~ow$|QI&q*c{%g$-z#_E8^;YE$$0CLpn9;gQr(l(TC9v9|Th&6!pX zKJ?Efu0__CjThD%EbZpQK3m@$aXnUDcB=amMt#!faP-*v(2@s%QkTp<6qw6eADb?E zDZ!f@$C-O8ewSu86*EoAmo`E*?HzaBJT{HD&O7Cp$>$z9RtK{v=j!jUAun8{@yWd? zr{{dXr}z-b>e>ceKGo&cJkvQ$XLts`By^xRAqte9GOPEFS?vyX=P`aE*wd-?=5O0X z$d)1S9Cm53COV9v&~A)dAKsiubxLxWwPJ0Dy3*%&C%7G|j(*4j6!mbM+=DN_Qx0cd z=rHL=KDU)H41IX(wv+NAd}0 zT*-7{SWe9)^2m9**?mCWg1dG0<+>EF8&mRFFmg97#lCkri@%%@Fm4jUn2P`O7ch63 ztW&*Om%uMS)j3GV%1+L%^-jEf9D%A8d7<~JkxY-l!^|2*GE(8r4(zIoTJzsmj8(+GpXP^ zR_U6Wk0xKLyhv2M`Pk97mXG&I`CMO&&9EA`BvT&9%w@~Bt@p#V+r60@Kl!%SSHgOT zcZ56UE7n0~T+)yNtSrOsY{ zEPSM1<2+mhO)kHDe;^!a>QpQ3w9DR@*p0RsPL7mwt^^?@E|RHz-c_c5@bg8lb~et- zI8t**dp+Cu*I4FzwGAMc*!>J?VkPW*FsIjV==5h?87{}T7kXwUawFlKv)CW0TjoFV zVZ78Z_J{~bE zFyKR(B=~`uF@q??Hb?+Ix?yn0K_EnaALR0&9PZ!vmfoT5^>0^FJyMY_^HG3yN81Te zvsK&W<^qDtlm}thwU%gZ5t%ghWs6kROEY4N>PEX}mNIj6snEJJIdW$iyy~pY1*)kc z>26_IV-}Mx307-3^daRNj)ypof2E2#y4B5NEl>h?ipF=#H+;Ho-RJs23JHow_JXlD zT1SCzuytC7TU~E>Y4F9Wa$9eE(w*Xt@AfM7u#*3GP22KSE^p{YK*6Q`LNpW7+-x(W z(?;xB4&~a=dzGiA8|&p`pr?m?bF#Bt7g1|3k5k!p9&SyZ+arH>{rpnejo@=|S<(ho z*G}1-YUr`>4yk$c`fYzoQ5n}1+`mOFLKxLkNCu^%sg!f}MWaZMR@_?5-Qc7c;eyx8X zaFPY}uwznjW9uJ7P0Q<=O=+0LmgOfp`K{&<_xUG~73SJgFZ65c+CK@#Q^I$P)#alw zUL>C9_&3Uu7HXk-hIA{|oaAa$UZ@4;)~0kT?zXEhU77JTtFFU{uZT08Fq>o#1UC$?lsL!@8gR}E&Xr}IC35q24Z#$b z&g^eWG6IrQ%TsAsC9}LatL~jhgsbjN1RXB7?L(cy#`*#&Z+#wIr}RfwWnxIa5=Wc! zS#nGKo0EjqZ9X)r}8>SiTkf>otGbrtY>=9@U2#}P%X+FIiE<^^BD>^ z8Sm^Dk>59GUNSFsy(0HuUk{h5JO*Bd!;X1(-pyVf0^o}UQwE}~x<#(c34OTPIg5y{ zTR6~KC^T5=QDa0sdcPG{C97ZQl^_2e7e7 zalST+!9%$RG!?-w`Eois{_1 zwiS}BByLnx+qjlb4pRy@`XSI?s)f`IH}-0s&W_%=2QDP5#Xjz_v$ywd(7G>FrzrZi zXC9LHRUI!njtRE0#&=Qm|pp8R&<{)9cEuT4NVaHfX%n4&4g}q5k*g) zJ{5q8bErI$s(w2OY+nnh&SzH)Mkr5Dy)-=dOKrW*V=V-)TjD%I3$pU2hNc4B&o01Z z%-z)g_*V0CRtkw?*xlEk7w*D5-(abclV>xNwKrm9{D}={l6Hc4EwP9h=d(3SG?ygh z{sx&+>!CChmSJk*q;f*!jFglh$I16(-AD{t$F!DNu&ifa@i?bB=beBLwP)jzn{2JI>6yd!en0?^!Hb zW^WarKR+zck_7B^q8qi!W;uShH4uHHw6R>oBze$T!8LT@?L{IS+7;Haa& zvv$g7atu!Ay(D;NMel91)1dgSCgbaLb=o5!UraTV$2XGq!e*DoIR@IDxSb>#oPh%l zzfGL6SEZ~UnBLYe+cL13rz##md?I`veqj2>cWvnJSKX;UjlG+G;OkdZtu)Q3AN;u5 ze>OvOLQ2Okb~0~aUB|g_axLW@A-IfkmS?ZT>B0-1Hg>{0jI0w6x&EtYrGkecUZQC7 z3;4d>dTi-(?!eya(%RV8*uv2|ssq#o`mEoNwSO?aw)s4~sm!@jvhk|~!iU@iI|Vrf z>%d3BL}f1h?y|gWcwDMl;tlB65P!GR&aRJ!|Kr;sLOeva>YL8os7%&fngavj0Ovx9 zYHeyv9iQ?Gds~z#s6-T1jI9Y(q+HeWnyYzYRD+5KhCEJ`g=TG%t&lY!b(}PDIX_nJ zd+J(GhDq!MzT>cIrE0}9I#YC8Gw7KvJ0@tYuY=BrS&Yt*W=unlnIb_+oEt+$X#iHO zHte-I3(76V(z1A;L{0kx%l+?qdoP%ldt{Sae3N^!E7yo;{jC6zeOmNWOfq3N-jFgQ{jOdue00j@|>%KPl zmI7<`Cvi*e>WOxVcC<83HCXk%>|5D~kPtF9IT2}aF^TPPmc2a}dpedLUL}%i5LB;u zHZe8IsNZM~{pT!9h7N*$MqR5>gVFudNei@5AayRoPb|Z*Ck$o+n7^VD2jaA(^Rbn~ z+rwG0a7@(jaCtrCOslwCX$kD*>^tnO>;YDVc{{n(iebe`)UIE$%en~la(`t7B4^psnWRRZ}F%>kbCE7R)ILs~#95yv2p-A_hksCHm zA(-)CGjmttpC+S5mL}=Ow8odFN%BETB~%$S_P-bXo2y%^j*Y(?TbkZ`e^zVIVf`3t ztr_!G{49O_5fk`phblGxO-g3fxG}!5u4$|huj#dMipn@%CJq$eF>=eh<7)3h?NWbv zb(u0mV(*5JM%7E>pq7rr*@e~;UpLzbWD81Hir!Y2wU+DUa2 z(EFpl?d(S7gDn^jD;+5UCKQV;*km^B}Z+BKo~N+qh-a#2kf+t-%H#JEfF*vS zB!)s+3FQf~v#tJF=O6FLSNzvMY{>LMgE=F=F;~cK3pIM?O~G&tF51?Rh{9 zIh-@+cZM(4m~=mA!jlH3N+)MbmutmwguZ9w7;xe2Mg*OOT03#jfa2*#7R-3-Lh6cu zxsF%dYxwSRGEt9!Ex@zio8PP7Ww#OoM{=TeV%e1Qe$>I_Vb86-`x)QwOp+?WsVvT9 z`EkY(yAdKTXr55sVcudv?60p-!wjP?>-kk-j$|$8O(ZddEyM#+wmQ2UU5!R&3>N=8 zk(|IdkB;|}7ni+T3vo$ywvdK`?mxY&Tj(w57wA{X36xnBB)s9mTA5|(#z_8NJvvd0 z19WLv*`SNwzrCrw6o`RneLeP}>0XFg$^+B(THO3%)ubvYm7(e?igOAkG`KWcTR)rW zY%$dPZ1!KGzC{fZ77)=3YI)BAoocx{H1y7BJ{;if>8`O)HEwHSQSD(`^6UFGQ0X*Z zKvzwBmp+7HL%ZD3L->aTb^-zu=2uYiF>*(8TyjtHzmmR58_UefEEmKn$sQ}<S# zd{ULhA1$D7-+BGk{hW1ZUcf}7H5#)ZrCk7a+zUAz^arjMZV=9xB^#c8ELn9#%cL`W zD|{~;1Irmp7t6eidA{7zp`n$>9>(V{P|AJ8`L|Rio%x8>9>??9e##hkazS!rvNyY5 zS^};|WU;uSaaC$FDlo`h%+*v9r9e_`?*}?;lhHBlw&lF);e4u=S4IAU60ZaO1@5g@ zANyrPja9gFXlnwGnXaj>t8TSz4WU|eh52Im3C8`zebkG=doGKbltOI+YJu9Z!uJH< zN|DOhsrN55P3 z?M*y^S~!C^@$6n~<=Dm9RRt?%jH>8`A=v?a9(UQV8f+-r0wR82!&kX*!GTwk)U z0oUFH2or*7SQjFfyO+tERc)8eMD2p6-8iuDumKZH)*4HQq6UxXDlM~L!v;!hC!dXH%B0Qp1A|!r7JIF>-ZDlQ-ih+vAiuDS?tbduIS=d>3 ze9zY=Bk3lSoa&&+gKG{UyxPc_LAx?b<%ZOi)>R)(QaU%dMJ21_nqLB90tEsXe5!)y zKDb^yc48XWYLyL>CyfjOef((rvwWvs95>cA{Dm1alXjd#GZle^6>BY<&7|DS$B*T1f-PL2A!^XCUk&nEBj&O6lWxF@Bn+UNc5 zF-?i|fW0lAQhX1IF5_*C6O7BmyTpgY!+ATZe}7#ZGxp^oXLs&gg?M&&u?gCvPNEJF zwL`jg)rTC-9jOZHnw6KASC(g=^MD8Jcg{Cf52vMz3yWk`SUS+^t>Kq(t=5$=i@&g% ze>8?qt4;)wFfJPT}?Ajp8lfZR6eJErJFJMUiqs)|Hp@PMGiE z?@2v?9_}4+8-seaBGy0p`Im3%MvHMcOx$m-V2@Eoy#97J|11|T^+t3 z^e6Ps^p_PU74PTGR2w3~3%bzVix%LB|;=HW0 z)(#(JpD_goymovn-`S2OUTJ({^)DZviUi37lm*5F@dS7UK5>5{#s<>{*NYtFq%vMH z)-&!fwlZEAE-@K5J1p^?5oNxHz0p2dzN~z*KRGiC!B>MffXQUP;yr24G{xDK+MRET zi!+fGkR`=_#2$am;Q5#An~|HmU7yf{5HLM6*|ZkWavxAHS>3*1>1_Q)ZYygYFb2wt ziA?3MCYp`yqj7XqwfiCThT3}MGPRL4^h~`PU=THE_Ry3D(1@ZC{kg~qVmS_ybL6(T zob*yBQX;F@`PcRPr-3wv(vmowL6rMez4fLmGcHd&IpIZUGdQ@JpTJ})etNy5n~Bd) zzsTgrN&d$*;1vIBqUrE=QxzK>%TWyGGh-vhg2}gshdEM$+ zyRIFuS5WWwA77WesKsR`RWGs9h3?~e3>Q6+T^)}zTzOO>?USL8SYpSB;l$!^*{jA4 zGsh)Rsb$QDZJ*%oZF$r{=;CkX=P=qENWOv75iTB3p&a7n4wMaLn{0^ScY5*DK3wl0 z#Y^g{Ep{&)*O|kyQaX;Lgk(0nA7PP}!W+gVrHtKPKQt;o2hFY1_8S zDpdE=bQ%iVdo0#eZTYP`67{Jrhfbvml$=*}^6rX$o zA10cqSABQCf}~S3V>7PTdV|*CFo#Xj$A83+FaRR=Yn`&G$BPI{Y%B{n_hy6Zx38XM zB+1?mQ28LH8gm|}U2e*iC}+Ft#GjfEuza>8F7^EdpKvg|co3%0$3_8}@vbvSpg1pT z_nV$Rt>0T%U!DV;NU(xo=p!SS%CY_x_47q6Z!vMiwGfNX zi}D(+dqcSzyu0I%l1;y$WQm@VZU=-zoSngs;qh4_hpb?SAEwT=YC?fbj!-P~ z4iPjB%hXWE1VS+?Pg$I%K+3Y&{C?4YT&w;Zswu=C2_3gvz2d+EwW5uNkB4;()1U=n zqT)G=Z+Vk6ioGE4c@Za>IFUJKWykuH`*J9_Gm|TLo#e0guR7} z>1FM3Sp>86m%Fv6Eo}((t(iaw?Lp;eSK-!wuHd$lIC?3sfR9eBC;L8uEt2|)r}YH~ zCyar8IkB-L!1R+fUQPj@kjgsD%rb*1Zb#Y1H=P0(6LNG!`OdzAcHqUJ#NiRpY%X*o z(4p24KYn&^4rDD|q*tPz7UllZ7i>76ZMlSe@#v;#44PsxARuT!#P+fQd#hA66C-kT zua0A(l0w10Go}B)|P*g>lS1gyu&g*G&SVI;!HfXw6izEpEM6gb7-Y1p@mr&2UsI$IP}<-Gnm_ImkA(M=Dr)gT1b z9zV~7b!udy93B0HKyqjJ&!A;m%s$b<9nb}_8@S>R9A?-S`@Z8CGg%DGomopiJ6LI* z{CD*)Dg3@U_wf<)5AfD0&>uXw%caH1DcO|6H&L~k9WT%^T z=Mm&eSufEia98+`06Gs{h4wA1oMwx*jEouHZBwO{q&2Q7Ep*HEcYCVL9EbgbJtCm!UQ|LF^gw&^%zjv(4-qhJzG z`X#Y2zp*#xC)6)QR`xpA#b=s|PoQYyt<+rIQD)_teDc@kG?{EDk*djd?6g-f=Ph{g z*%j~uV?hBNI6o3n7-*OCSmi(<{2A#RW2h;7=!9va963HHZhgoT)4>pw;S;NhJmebc z#`k$k_8eRr5s@%n`)z_u&`nfXB|$D(#BJ!%@mXgbq@~5tP>i^yHr<*n8C$xg1UHr@ z{rdLNIa65s2R-PIa*`TdX)}Gneq&P7s_l~6vUM2~f*&%4A6gDr=;yF-c>>V-42940 zX9(s`Fur>#CXB$QX>sXqQ5QeR{{f*d>9|*Qx0zN%9nvQf* zGAQIy(JWGfe*YGJkk-Zg*@f0RecFo@JHm~U0{!9zk*UoQ;S;=(eRai96^O+7l3tDA zZM$>`B99*E1+IYQetU?6@$0yNknH&j2o_sJ$|Cw6)&-NLB`P4(Bvs1NfS!SkIM40Y zS&*~9@pcvENm#?_zNNy!sHvIWls#@zm&$SW_~=|oSA52V##b?$6-NZNQC*8hn%wiv zXpu|+w0oTcAxJA?Pkg%6GlDoOCP{_nzV+_=w)OaVHR!w6VJIvk9{e-M8DzKF@%q4G zPFalXJ@5kA;QKjl{CPT0H3>J{QBl!R(V3Np(DgBp?EMjURzBCx^6Ai}RGi&dJ zZkqS4cZ$%>oV7hKK5?|P+KY8i8g;{@uUKMF7U3q_^u5zML#`yjJmF}r^W(ON*19oF zdFTh;Nm&$-%5_7}k`puE$c&!GHEF$b*6y-#|pq+-Ifi_P*Pe|GndxfrXV};Mtd&nYJ*s7j+&es2z50s@pXRfWi2KfjQ{&po-1#ZTx)`3kKy}5J0sd_Tt&l0vF&wK?P;FTCR(@;#&zGz|B z5)cowu`OiqS?!!8poL({!fTl;CU2=hC%;Eh9wF}XU+yYUTXVfr8y;|pT@-Hmn_Zvx z?}1l`fl}jUNL}(Ke$DDLrj~=oD|5K{%HcY}m^Lk+AM{jF>Z|AT)hL87$sv{5^x=*4 zxyHWHuQajKeqp_V``-x(P3k*Qouoh;hDB=VO}Xn0J2-zvCf|FD$f0;e5}-A zgpY%aU5Nw=p}XB{z>z&uw(kMl;J0MFT&gf0wDR`GZH+);x2?vpAqJ4VN&_26&Yp^B zz4O^Wp`?VS-rK=Ogm?N=a7PU+N)<&{r1Zns9jod!Z?Z2dviM_Lh#G(Xt*`W-fy((M z5?{M)?v34=%$-7aTjH5!uDZW}NvETKSu0@3=4$ZwD3NXVG0Z4YwNW5_&G*1hi7w2K z(*k5_Ym)gTrk8em^{GNx{-;kvH>9V?*KYyzR9+Na9PExdo?a%f&6mkKF!mi00#v>s z*F@e?eBsg_ z$MiuH#Vw^#=B>s_WWjdHy)Jz=xO&OY;ytE{HoyrHKw^US4v&o-Ut(cn*#pw(T!ECf zvCz;=GP^SMLIpVAzY)s7gj!8oVf{t4+fo}TY{^2!``+x!hK#$V78$`ruuFCami*lu z!ag_Cf;Bv*n4^(wJXF-<4@R&zEJk;9vS-u9+JT*p#(WIwuQoe_S$~qmpE$1M!>0@xm22+DwwNIM3 z3%@D~>Lf3B>H7OwU7PlWeL~T9RXMA%QdN4jBH9$Ki)NAgwW>_khNk3X_6Nd?232fb z0?JfLvP$VctU>TAPpUXU4A6= zy>j476evs2JO#1Em)M3avA_Iw_}Oxi5sTc@gv^m>q|fm_U<&f-Yq*mP=d#v+H!$uu zq`e%_?o~Uxh~A&w6PvS*XOf@@49;`8pG{~3eH-?C(J4B;Rn*19jE zc;K4C{Z$1!DFQ>`2%YVjTTxMb7ArjPDPtzfw`et|Dc1Q>+Qj#Al(d3jLLkY7JH~pkvCmk?h9*HDx_^KAfC z2j%2EJ*@W(fUtm6Ma@n&whR8Uy}ZM7&2A2PS0V?O{!`|mnWgzfqzgH^J^z?cA)>S~ zpog1}FJ(F7%~`5Vlu-BK#@bc%5T>*`($=auTZ0<7&C+KA zr;ez;q%qsV&}>QsIY})vSJZGq2p%BH;u0-dv7glI`fy6%_0~Ly)l;ovr4E+$1lU#T zhp^z8(tes|d%y$znT$$PiUkR#`rzyi4=)l7`={r{HpPQ;Su=i3vxpkjFSasIoDzvi zZ^GJc+uJJIk$$gqWTp!BU_fBa^G&#IRyi&ml3LByM-?5i}Ya3D>a*S~Z$}Ab3*v5wxWn z!`~<-eAxTnuf10&umk~9R~zcnmNz{)gxh#~@NZ-hWFe1#qYOxS{=}uHgk2kNlbkC& zEx9cf-RC5n8XH%04-FmTOzclH*FbfEGegv&Y;ssg%J@>Df8%4cTy3L#4+=_Gd_vXo z-K8iBDyQ=D0N8O8W{=mypJD#EWRT z7cM|-qCdePfD=zuDrSJYB=8`k&m$Dt(dD^qwftc!V!+rEdx~4heD({FRR_ybcF&B; zsze=fRc@z@k0cb1d}cG<)xqEijwG5=sClA|mMJdV4V)ub{nMdYm%Hs1?}eR;X?ee6*HRt&rh;&G3%IT33p>=E4| zJb{C&$|B5*1bnpjqPxeunDrisRDwOw3c04GFSVFCo)D;YC|8jehR-Bu8KS;x=Wv)C z=G0C6zSc~iX4LlNXs0Akq9`i&&-~+BU29_* zi~L5Bv*D?l`CM_cfV-Y)@`-2hvF9e*)JSZ?x0A*-p;#F_QmH3ylKVHp7; zv`q^J|5}YlmV+BzS1W8r-+ey+#;u@|uC}xZn`IT$5XGDf_pI|X3H?43t;(6t^(A9u zkfuD-uQU5hW8_d(VN=asv|R_DR(S4VJn_C%V)*$EpF=f2C6M_n9mQIU*}-;^#^Pn! zdG&IFeOh=OIjj7r>Njs0yy$0+#^(HyPQZ~tkzOKQx0mde%;?w(hp@rqvCf{JS~9|h zb$Twtdpb%_K^O$e06#h%hPW!F4yo3Fa^@dwvv)S3BD4Z}hM$HB#~g*jHKdVV)*d8P z&aAD4+1}mDY3tR<89N;RxPxqQ&ZxNmPMA<^X{=jBH~9g9$qXO(ZTv%2^t10p3@nkD zc@%O1AgpeF0x3O@p~}f-V)ADYtIEL7zN6oI{}`*8F2*Jmn{e4l{A^E2ztwasNn0Ur zlQmQi2bbvO$$U+4O+(mgJ-(K;IQb@B_7Glvrmmk!my#E0B`_^BK7|F_nJ(NdV8EKg z;^Y{$_(jeL%pTjq@)err_GB~<-ApchbfF_6}W#=UcF&c(#)@iHG{Xw00fa8 zOnKy2GB^ue>Aa2`=wRqhN2TxmE#IU$Up%_1Rbe4f+?W)*8Y4`HeeS84-VTAT#zT$>WzHuW9C9Rw*HRa35t{UM zyBc6P&OU4Sg4=+eeiqhfyU2pxlT=N;r_w+cw~p$``_1lMF%9i}6g#eDVRe9YEef20 z-_Jk{yad@DvL25%Y^G<_luSAvR5X@tt>14Fa>gh(=2CQ|R2;a9(bgWnMSCN>1!If* zDcWJoy@|r_QXa22GS1)I10RJchy1uOuOQ!SY?u}JGO5$aAmlXh@oT*zPEAZd)O_astD@5~nw%obx)R7X#y11b_eb_ly3p(~2^^CBtDFJX54=VA26ZUk z-|bNSPWKX&e}#XV23lmlq-Uh2W(@7kupS_xX7CmE5fJEaIsOEPa1VWw29qEZ%cBtI zo#a)#4JwXp(~bZ^&v)sb2=1F~$LC3a<%=YQn38`FG+xQYH)c5KH$YZybW;hZNI&Rd zBoBw**Iwb^I21F{x7lbCeE!2Gtul9FTcW1I`j`6UCbF9dhfWVY6%JE3?kM^St~bL3 z(Gl@>!TR!>`Cz*Y7ckiM7IkAlhsN6*G@OV6oj^qs(MQ_UFJ!nz3VDmXjR`9v>mCI9 z;Enef4~M;eDo=M%$bEI`hn)%- zFlN@G;0TheIfQ}b19RH+S^iVVh~MLYJtF+tu!ehSlL+k?ypZ)dMhgBhl66^PN4&ad zBq~WfYER0=i99V|a%pnYhe)U!9K)~)XF6Ga_d2~ZU5~gQzrg#N*AAk(L!>%}OLJVh6q-)BwNS2A{ zpwx9^! zdT7V&TMWtqDUnDCet~z{8?UZB=Mn9xdaipCPwhqRDYhnzy>@MtcEisggjek@{o&2^ zr;=5DuA&kG>$kgBtjXW>g5vUp0`0pOeMpx}ouW&55&b_h{n*i4NH}P}r(;A$!07k> z!oVWjby3n}Vm<{vJ;T1}2dW~-4YmS-zkDh7mQDNYDd+$HPGDX9oSw8?0-bl{;^Z;8(d zup@vUDLn6Z2xV-8o#ck|;U$rfiBlpO*;i^qJYTsM(i1lH;2D8m>FTqzr*q(T?`&)- z$Z=qPvG4ZXY;_C~PLBISaE@W~)=JQ6rYsv?RWfPGq}Y^;62%fVY_M&LuWyRytod=s;CrTa(ZwB13H zl)_xh0@yyB4q1m}yhk-~NvECF>TW#zuwJgQ9_G43m-5EBoIr~BDP~_B&wUUsqL+@z z`-7p4-16L-tAi--DDFOfbD&_PymnxTHwB*cpEAuKHrlWbvud-#GxT8E2TZkxu0w4IwWzSOptd8BtJ@H_bTrOrSFFs6``#hI zC(<|{^KX^`<+~Qc$zpP-2fsbBCUI-?q;l0?%f;oXwGMUML=HO3GPw$rL@VxaqArP@ zq5V4Xn3@oS2Wm&CBrYk^kuGwN!-&(~M;ByWG%aC79in!Wt3@31r}A7w(I=;u0OV~%4T6raHC_y~Ggcjf4msmXhsG)( z6P!gltM96t$&0av!yd9q2{lxnb#-g;$_p$!dT9B3g+M zW0eAbrzy3GX3gBbix!aX*h@_fw3;@*^x7QDE>M?$zlfw_No)FhGu7*BFB=>UgYhXn!uF&gD?Y0XaLzlWErLO!pAfnpv)yTV~IMG*i0c`g7y#odV0`cxv`z&DWkByaZU0W1Xkv!=Dg4bhFQlf6Ew`BSNhcCQFPSxW(?b@@HRU zRiGqUSou=8n)-khXqbI!#BI$Ym)z=pg(D<8MGhOVQC=&}RpDP|xXuV0!8v@VUbEnz#0ln^{qz}| zWZlKbnZ$m@sY{=>GV|5GQ8BOg@p@w<(C&7-+>=v|ltq!1j51U1C&}tK-wtU~ZL6!p zFg6yhO3uE}l!}W}&4D99uI&+yHoqFQtC7SzX{jnY?6kl?Hi*Mc;4k!$;tG|Q$9utOyCpMV$}>3nNL71_IV#jG>uPR6Nf3h z6WyO#;SH;+3nEiY-@YC=+Bfyd{>5F-y}! zW?xfL#x&Cp`JG!IGgzG-^G>eM&0h6Hw~wWjq^aodi!0y9E4ih*og<@t-+FXZ#Z)Hw zQ4)dEup5#e5cGQ!D0S4i_w96`KY8qefPg3AI8{n!4F^bR8`9x^9tvi~fD>`ftzXlk zPGk#N!4@o*#w3*z5bMx+Fj-RXmrc``-@LD=h>Mvsk1&wRculSqM9@)3&MB9YdRdIE zWnvdk5uzdo7L-h%`3Uu6w%yNZ2XAPv=|788mk=!XUF+ZvVT;m}bC$u>E0NT)K-fez$CbApCdnbvFz7CCr z7!T_Pi$~Wj0pal(LK1P8&}71YbLEBH%vTJRD9Xm`wAVIt^uaKJ*6_ygyt}Pbj0s##BGX}4e^t3RuT;f_2b^4 zYpy!6dc;3J;K)y3vpKNaHWSJXoUHY?Hb)DF{%|9E`@2D2^RDxIpJ&%isartuIei;> zM3|J_g`7e7A+vrks$*jGEcoa(&WLv!e()CRSQ_McIpO;Y76F!6M zKdj_Fv4fvh_hd{Mq~ms|z~6a(G(p*KDlcz zC0Jz<11;=dHwoyJ#}z^McB0#rh;WZ!I&(OPaGPOw*xB}ijJ+6H5^2>}1;nL6=Dyu0 zgbr08T&*#L+Zx_$%cF#BAfj7GP7SkPx(zh0!fw5_gZ(wAUQix!`LP^Ic#pujYY6Sm zEoN|wlNgGwYg6cjB=lXhRXgxegUhn}Xw-u`bt48N;&v7iMrxWD|{UIlUUE!f`W_(FaRzW`?9usC~rWXD!bM;^u70J5jDfcNR3o9& zk(QQ3-b4j%2TN;n?-Joe{xLO!?%5^W{e4hy8t;0p;4PR&5uHN_)p_Y3pfoB^nOo>p z)On?&IBC~3Yqi|+uSO}_dZ`D|dTHJeXqy@(5T)|)-*QaPj`0XA?q56Cxjd3|dPKfi z870BTI%=Gme#tDDRm`OQqU}e*L^4$~IM=A_-Pgq}V#0*-4WV>R;zxxt*?93gis%iw zfMkVep&g58yzY2Wgu~_iU{N!(V<+IZ5HquL7qrOP8LihxzcH&rZJX$jtcINrF}@Fc z)kmXrY98OQx3)K?%YSs_$Qx!1rQ9RkI}(aKb5qHP@R6EQBszv};{o`7FqQm_WX|1_ zOFAv?3Sz&~+oVwsy+X{0Cxm_L844&*6+R>OG@B$^6vlJN8hrApMC$$@0Bb;$zZ9=| z5J|*poM6@DF=i*H)Sc@kXr=Tg(e**t7 zG-l0v|AZH% zeL`Ns)oeD>INB}=rN#9NA>*^2Ap-$tU~CLNxpCVNu}0zG47Cne z#6cwH9G#pcmkGm)&jxxHASO))iqvjLYbXj6@r%aFqFDYos%a(^Qb^)f(HyDrnxsP}omp$bxYAk~ee!A?k^Pp=_0)+Le%o)qy^7QI8CZ|^W4_#{EyKWHTm$J&y#mmZShzUO&eVHI{hVjFV} zsgYkG$r1v5`M5bS;2)5fW2`Y8)JEN!qOB>N3UhM;50f5!Vtr9u2c8V36~s5|vqb#m zF+KYtK6BF3hveSP3^TsDk0+9?9Mj{6WiCtWjB?}Zj4-rFb3Y0tw%Ul`6wL?BPm*Ov zp~NNGxkcY(d{!X!Ps$lihe_>|ItDA2wI`y5Yc3@?(aKAX_8yGZPKn(IF~3VX5DMP{ z6;y0P81W~PK147d_O9582)`?KBN9G_7sZxD)W#9VJe)8lU@IN-oBqg%YJw{r?`*<& zxHF14-XHvt(QXyo~b{qiF;A;aMi;$A9$N}q>m#6YBgEPN0#WPTN zm$7WmP;qOes%UfJmfMORo(vRf(fYt>IA9F~goA-djrs3onQ)quYz7(I{Uj$j+H^3Q z5{W*suDQ{?FtECmN?hTcn0Ca!Y^^pmr!3!GT2oEs?Zvw48hT-CGO)67@) zuf$hI^Xg6W%dE#3%$8k`)759outz^5zIJ{(m-X14zyCowhw*d;N4#gXoURaGJ@)a? zK7aG}Jt*fgnBK-?&tWZHVLm1;%)M3YPcGUYO40bhekE#oOtI$+0bkl9a0a_W8Ebc3 zh_gEw9CijCOsM~b@x{~c5^_imKN2+-&*PKH^YiiBafh)LIpPlDN&tl8v>(Yi5<@R$ zvOOk@PHqYSd9TxQD$pKrEWI<--;Zyj5WWT4qd#yQ{sjEP{H7kizen2tHQJ-q?u?ul za+3FNq6Y@57#dNaLusC-s@C+7!r<88z&TEiWtqaqX-iZjk*O*_&)RBw?pMzZbu`&J zWvn*gBW@k(4^zRq12Wo6k{DB}*quIDJ9W8&mx8WHC}rX1#g{-g9rE?VAD{3x)$sg_ z8wUoce}l`DonUvE;Ej(LpT_q{Zzgnz`zh}A&-xJ2&)89ByfV|AJhK9A z1iLkzoEV~&qlgCaSBM4-es-N~g!EG*-P8=Ej~eN-wnBDAu8!@gB_27iN&BX(h_e~6 z4`+?+IB1%=(dayYrYiAKq7e^7RbYjJDZCh$ThYC6W&_tH>zxo^*~Hx%(|m#>w0VW} zwo+2+Vhh5+42Gy}AlxNaPcMl5??(6gyAK zN5Wl{kzk(8$77gIm7SRWd-0AM%&XU>@$EmYkIY}9j|o6 zQlB`1K0J|Hu&R0}_{c7&#j&KL$=Ibavw>#XqHn8?i@qUN@`@QLt z-;;2&eQV}(uL9Vj(|?rzLOdVvIpfqAX|D<1sKyF4DV>@B`RN%_pxw{3T*#olcv>}B4!^YX>! zMZDPmn%pA+ycM4_qlwL#anmFSNzROwq~3O671h4l{_Gc$$hk#X(Vi13^Io@H-EBv-&t%w-5Kqm4JdTyK};fZ_Q4 zUJ{$jAf27XTs|GDxEMmUp^IUh3U!+6xX~46XY5Vi*FxuhceE!7mKAhGBX^yk4~3^D zH}2iLaq>)6eSOs#5E)~8Pww^G*4h3C?t6*3|AXF_0uqEs52k1ACH$0)eI6uG2+Xgh ztfj%+pN*7?x~fz&dfyWgHngGAAD3}?$}mc=TZYcvI4%4k3}2%+g=Y(D(r@lQ7o0mN zzn$jZ;kz3&gcIc&UJlG8&$)VW>{RH~*u|@37eW`%7POw_Hb%HHa>oglO|3C^bj8we zhC8}y4}CJ!wX~~yX{+09Z(rKcx-|5}z5}1smK3u~x}H%zaM!{`POsC~P`+?cOU@$wnmFjV>b3{sQ;J%>Ry@Klh#(I^HM5drgdVEUq;q5XT1W|I>fg zT{3^IJCx>iA4}xklAJ{8nIJP`Bqhime)`w)O3-Urj6h*0Ngnt<11!LFCKwdYU=gA; zKwCz~$45i>)38F(yLIc2@uyye);Bse+TRbKe3y}9{o(cS@6Wl5g~;vGqTDu$=Om~` zLMA>MJrThT(njp-gx3z}WmJ`TjI2=AJD1P#{Da|Q<(ZBNU8*=iB1WAS{@o{rsiJldB zMPtv3v7@MTeDgyOZ5}^X-P&4x3>NyzqwhW%Y~R$*^iz30<58WY16JgHL@*x15XO#T z2uIOB1M(sa;Y&nmN_KZrJS0!(IEHe30z;XIf{4i1uZj9)ot)hx=rr16Oy1m7w6SAo zsjWaUoY&tC%BAoNR0_)F@_x;5T21!>e5sPk)Y{Wt>{vsT%hfjXnzc=^bLyH(c_h8|TwT7IumcA941-T1)^Q#`+J{0z`N=6a~!$u$yfJ;lTw?WJuicv|# zK50!g$b|})<96!gCXsbZj|~@9R1^(|oX!vu)^r-JY3T~2&=xE#Sll~MQKT;)@UeR_ zULigEiC(~~U1-Y{B+zSRh#vAdS3~ljupVL;^$;E2G)~w%O00)emdQ)eA$L%C$8svH z<#LpPsq!k&Dh^Hs0#itHQEO1h{`&XlkozRg0qQL;W(a;$!$1pTD2O<95t1;g5$qYA$lR-QvoUY-!)e}&x<5~rWIPWiOHQ$g)eU`Pwt zeFOEtYf}9&mM%I{;B4msx%0u`c_d6u3f!qvf7YEg(J>-A3Elz!jKKpkkKtYL*Sz=U zuXPU=(OJ;<#d%Sk<@gwSV+=j%_Y3(Q#k7@oDmk1T%&mtuJdJV~Dqb#c2k^!Rvvwn>Viw z3|1^$STP9eI=1PhO@j-&7K&#F_Y%45W(FNaK`@R1u?3J9fm~;GO!$2Gkikg;#T5); z2q7Pa;ETQ<(NFXd-Ui8p`59({xjW*IN1C1cvxCL!8kS^#?Q7Xf8rBsD6(#nKZNT!X zy*=LM#=*vBZ(+-lYT$I1xARMY8P!L0^h!DudnMfm1D{YkgF#2`W1D>8@GCo)FWh3xRxNJ3 zbJFj=PU;k6ix-8DZ{N)L?G>P5#J@@_fUs7gAc^0gnb3TQnPewX{{R;j?L~CfZ5!<+ zidZ(w-jAnSy`-g(aaqsa|EYVw=5zPmP51x%6Yt8Se%E}WcgFi&YY;i_BYszM?jP{4 zz4OksQ^Rohqc;bPm6gT;q68dY|NMHa1`P3Syr{Rx%Za=#jGX8qTsYS&BQoVokb*pe zIw?FP96yf1UAQnvfc;f!W+Qb`qW@LYh6n?SHHU-=nO4N=#G?9G62GgciD_wrnGWH)+kNjY;U}nK*S&7^x$%g+=k^o+#_{te2Zd`uu&c4DkgC(zoP6Qd z-VrWqq<3r3AM{Tsg427R*8~R#gPP~}d?W4DskCo^aVOSV5ucbA_u~=0VmK{{#w7u+ z#_FU7IhM~+KiR9R{TaNoZp36Y=iK`i{#D^iY@W#Gv@L{fY_A_IyvUuD!2W;hUK9OL*%TJ>G?07ji1QWSZfB49UvH`NHk4GH z_(F}>RzSV5I|EBPElZVG(3Un!cWHxdQOtdzMdT!nR#{TO%y@#$!JuP3OKL)`Z1U7s z*AX)+q}DchEQQ#birP~9AN2H?m#V2jrSOMqhBpaAPqtWYoqBoG;cU4Mj@ zExmPRm$4%|CxnAT*8Y7@e|98v>tn0(($cSSCxesxa}PfBS@G`AW+LwiyhwDLz~dwE zvi)t=f|~U7AYfx@+p_n8Nq^cyij2wfNxBSFuvg6+f%_t=2Y72yxqmV^C-{W26D@y1B}GkUOuR%pQv8WBA->u(7J zuv^~1-Be%KI$ z$DGb0rS62sJDa;`?W(}OoPmy6Jw`2SGxvzT`8&ye#WY34DXXy|SRC|YmGE(h1e#u7 zLl;JjHT|3Lk&M~NdGsz^x*lqAA<%OQM?7s^y0a0|qQ{sO53ia)?ZWcNl4GMw* z1`38J#Cr?=X6*dsvA?VE@-W~}V2@Og@K)*IpzsUP*U|ywY9-nqGwXHKv14#$-;X_X zpU89>kZ|7XJr8=;rM|vPkv=Inev}t0?O1GWU|=BRaz*27dA_zb-$+b|EuuFjz+jpl zRu_Ffb|*9ea9!g6lCgU)GDUg7Mh*n7M~CeQCdBn*Y~JE~;%GxdSD2ga8K2b8hS&Bz zF|NTAuT&)Va;r!9b_fUPJg~GC#J!G#bR+^JuI(z(-++nAGtU_;?)Tl;afaOuAi1#n z?BG-+=JKj2=kQ249?c?wCiJ@Sc1ZXk9Vd9>G>Hb%wS1S!Ix+&b)aW9fL8~tWv7tQ) zw%VyvVh+GA`R`$jq6B$IMq(0*`%%~0RZEQ>IoToMHz7`sMc(sKk%xmgCxvfPu}5Sc z@vRia%RJJ7N0fS8t0?s*g6MDK#UA##2vbz@F(1FY5@bw;SjPwAL?>SS) z(eR+Hb7d>dvYv0T3cIn6=e-&6{=6~^FNgqNV{dRyHDX6#JB1WdK5mMAIaM%7qZB*ku&?wtQ!(+~bZMq+q;w^cA{*mZrEuB9yzadAL@1~Y zhEC*6PJ(v$JlZaBlL@io*!z929ozmU8RO0E$FBO(Ml#IH{;P@p+t~Nc4|_()U?ZN; z)R!u*E%BvR3Hyf8Bh1Z9)=hNuVmYywoLvQ!ClVvmFm8<}COD0yYg~Mq;@|`v z>=9ii!D|fjf88B*90M{E-?8X-7USdvI8F{~0dHqOutwC5Sg_pO=G}9DXvkY?%}|9< zx*=zv?~wQ6ILR_^>f7UINTPJ=ul4O9d#oT!nn_xF*Bp#3-%I z-MmRrbcxwz_N;LBypqAEJ;r&N}gQ2N`H_LhlHibox#)|$xq&GEw0U4vi@q3 z9BX+K$e=Qo)o6+kc@biFB1e3jh}8q34<0_5o3(1`kftat@0$;O@{Jq6jT8e3XVH_& z`)={>RDI=0dzU%KpndG`A~iwi^Q*RPi{^GeAnEIwFmAF~ zj6usoTN|4VIr&+e4qj6)f_t_Znk>ltXaWP~= zbc~pAa{AnV~ z>bNY}@A>gM55bsNd75BHOMxXrg%$bZ$A>Qj(he_KmXoGDiq-jx#vcTSNi26S_b%0g zsfUB+MbqHEk}E0n;AArkO*|X)Vp%NgJ3(^KPHV5pSUmV~?pDA>grSUqfnL+PaX{EY zgTwt^ESN7(knr%q!i$79btEq-PwdAO)pT+E;1#Wx>AC~H3&S3Xw!7-URvDv38u8;r z`a(t2T?}B6)>uq5-gPT41PuH|qV(SQXmT#CCy$A;Or7K&Onp1D-bhN)8> zdozqn-;L3+mDndl#>Dd#)ohrpcn)nAdBJ8+JcoENz~axLYh!U~Qehgq1l{F#;yGkX zIEMgIZOl1zen1k#3`FvBAF$qA=!f=l1WHt!SV-LK4F9&awWCv zlEc}ob(1K6kVO0mCy4F>8uzFC;3hGUhzxKCX>R%dr|y+_`ljxc_{-gK{MWp@Zq>Z* zuKWK_PxHS{&GBEmHjVaBI}5yo@h)Z2h#7Df*L}7*^x$Prm~26aiY5B4>KQ*(mIIO+bVls|UC+ z>{tarjKRuj4m{vnC$r+)9B(EMJ`%N`Gap{~4@|c?lj5cK4AB@jzJs+^OoU}^RSj5UR3OO z;3?JKjg<|}je~2nrI`nVoL$NE36edq#5|Fa#U5A3LO${v}-^6wF z%EX#I!3&4(sBsbmt{#=0IQp&apIH7 z-xcG;eWdepG)#Oa?deFzmDI#Z-xlkv=;&s0LP&;Z{LRKcI~xoU3op$kh@x>TyPxzg zf5-G+{+_vLOg1a7C#TbxZvy#LA?bX1=S#bCgl#!F=yN%{UfLDGaLaAC{o`$0w#3C- zv*P-4M&OB;@h%KF2J24_%}_p6-^ost#P|o>19gK#tflII++AU#Mn2 z+P8-7ltirPtP?ORgt^9|rjDFZe}#MOB=v;J@nqH433#elId)t%A*;=n$J`C5PudNL zz>ULIEb>n52Q)Gg$+hP;16{RXot4Z`zanx}Y#obmJC>F4#Vir>l6x;@=yy@QB1x{P z2w9SID&U(ErHQAaQbBE3J@rLnzcr55*wmw>fRL2Ci2iMy1M~{X-DJ;UJ9XHNwp<|@ zrvc&jQ$!|_dKS`Gh+dZ?xsk~lrtaWQB$i^2P2;a%&QKVvb8-?L0C zY$L^l@d==Qtv=t);`#3GThI85janMl9|+#oL+cQx>xh57jQKU1>Qd{G#LKQBy;RU^ zTBn>#OUaODp`gU$e%#vMcVXC^nyJc7Q!6ywsfhPo_`PR&_gXP`yN={X7c$=mF8P)^ z;^`2zT2qU5N-`#)U z4T9qv2ljuLRE#y=iwuC-&+ir9VKrkimQRl@SuB$oJ^%xEyj31qyY?`_h*yWQ<>UD~tU?G8Py+J8I0$XJk~P@vR&gQNVm1BW)%v@BX!-rxhJxwZPP z2gICZ^fNf?qvJbg99|b0UpBpCx28m|BBeLG?-~q%W^AfkXliS#t8J{Sw+HvBZtp6o zEy$27kUUpgmA~}PZCxeR3knMP{<4CS%9?V|py*Sp6aDbiCnojG;U#@c>y>!sh-WP7 zn!}DU0)y0Pi2c&-F|Ikv7sg_VZ;txM&@0qCM?7S){pBoZ%Hco8!CZG3geMotEnB zvDk?^JH_wpTrd-`r22a-mQ-NUI1To@mSZ`LQ{BM~vXbVh)bb-1ppz#tn;mzeEyqea z+DwPG4)xm>wKoim9k*D~fA;f;o))L7=CHYM|%(jWTS8#J%+Im@9#zx zgKqbrd(@5p!}?Ae_M42KNE5AV3=5qp!zT3Dpl*((*{@OiXD+puB3zpry;L1l;e zt?Zm!U}PsfzZb)&9?Gjc(C!TSdcUxe#?fR3^mWuJuZ->2BvS3S53XJN;M#Gk*$i6I z*EhemdGl)}i|$*r=swnG6tp6fyj-Tj?`nWkoRm>az%|N=@nOH&eAJ_Iq1WB6%a`2l zu;0h_A&glI?}0w8!FC!k5lJ@`9w6{a8=Lnr=hC#*^3sBQgCU>SdbF25t5oEet?q90 z^)_p^-cV~OzUXo0xScIsv@a#VZ3MWaUMCNq(ztaXg~eucCEzLC)axm1Try@g7_2b+ z6)jcOM*~Y5TjU1(6S7_~uL6LRDw7rCHeNcpH!wDJ5Pn+fag&}0VEe;Z4xn49%!Km} z^Td8tb)1+8#Qb$tRtL+%aYVlQ^}(LPtjv_`{N+2BYgH;vsm{s(d`g>2jkP5uwI%55 z?KP#sc7n=&Il&PR0WapLT)yRu9`r=}}=8=M2O;M31m-XZX>>2?dGIx!W zUJ{M*$cbG3Fo>t^!2~eTxxbF5y@C+n1f4wYdG{IAiP9-Q)PcUXLhs;|r%)?APvqhYs1{%oJ(}>d)WeAhX46^1nW{8Peu;PaBB#61;0+JD zhBGrYndV}>eQ9T1xuwLTaFg*qgG>OcD&nJL?mNax((OZidB@t^-r~Aqr@^2vF4Ss@ zyvv$fx=M3%E4qg|ZF$8yjm4#}&==&^b+*hj}JyzocOmsgB(C( z#^d>aQ63+<85YO$egWp4PUo$k7k>{Xus3iu@Sgme#0JBiENf)-aH4>iw3)C?60u7} z+k|{z`^02(^7um!4>{pO!@fUX_t-A|I+tZ+!Dwx`<}(XM>Xxk6-#2WTFg;y0piSd* zm+O3|hcrvn<*C{;TIB+5ciyha!NI{v@`rvgZtjOK`VGR%@CEH_{}K0Dn$JvpXzUoq zX)^|-#}yIToRY=R406!kb=e&H=OzAbM?uPf*E^6>;OO=*Y1vYfv1xn#s`?@&(g;69 zCgC^ABKUUuri_{`s)em?dtRTrFVF67U8q&31*|2t3)Juf^@7?GYamUnjpiF}23+!^ zd5<0Vf=Qi`nmHuZ#%`oh0Wq408N;SSJ=-5lOOboHR^fSVOHIEvly*?K58W$%?c4sK zG9|^W+P++COik_Y&oP%Zde?vwA%4YvA@&RAy5f6bV*mIQ&fgPW<*Lvl!JzOEP7R}H zF1?!!=Tcz;TCocf>#y)$z8;n}RjEfCuXr!5$``(cI`dau;^h4~DwnI|8&{`%F8J%q zvHCuji{sdM>~%CpmE2io#~(HwzUvHsux5h++_^C{4u1}84gK9Yl)?}4F8G)TxdvS> z_&C0D)iaNabw;~cPpPPu73pb%MT^+i#)or1QSDv(_un;s@#3-ShKA~6{-HKMYTSEr zZ`+U`J!$Q?(tB<`#ch^X6CJ6E)pju(082k<);rEjNa$L1!si=o8(iKSZCusw8Eo@P zTUYsf+)1C$wC#Hkds_hx~`Fxk7U94#D(vQ#qQin$+^kS6C+lj+0Dwe4SO_wUi ziK={s?)s7*vlVBkEL+^ZV)I%Tu0krmv!=;vQLR`yn7tafMKR)M3JD1c{|R8}sjltxJg z2aj!de8YyvIa9mI)K126PJcvfFM8LNF)`MmSF`FxooF_3F5&FE@1oXsh2p8BN2iXC z!fE7g^Ln%fgVv+^9%}mD_k?rzAH~0GL)*Ldboowzj08I7fVJ>0;d?#Rv53=2O(Ybl zM8-+-z5%2e7!I|m+CqJ*HgB5>J$AXT?=o_nIwib)>XevkjrH&U0=yRz9g}!3%2@~x zYvJuFOmyR0<8}SPRGq_IF^Os?%j+z;xk2IMwBlv1n<6o(6e7j9JRO@?^|XWCUi!m(_M4eRc{m z+N&y*==B%hyMSsh+&C-Gs&4R@nl`mJWMmjp@)j+mJ zN_BZZN*$ z@G3h3QwXdtFj%CwOB}SMr%(*=xDzRMoW$da#AyB~e~OKXdDxUNYUHbU8%R2GDf!Gv z^tITr>Hi9;4u{YCKf<{~$8Jg(cYa!y6p5Z0HLe(BzBH@)MPfUD!&jxUE)lO8 z^v8Lk86s#NC(5S2IHGK#+Br~8qKwXsKc?%K8C|QB&~;|*P)g0-nKZ>%VkW4Klzu;D z^lP1)elw#p6V)0SYWmVreG=4?5^V-Ne?;)CmMv#^&Tlzq#X3N+w#>M)Xz#3)T|M*i z3a=(n(o^8D74{)w z{f*3%YB4uABQKeeoA|L_ofmUbHAwj4&EQOWGjO*??l+IwO_Kc~Wq+!hj><;mYq=`? zR;39GN+q#x^*Yph>eMjlb}~GAN_$EbQuq`h6@CYS9?PAg^@&C`Xu|NCUI+TH8vP?Y zawg{tJd8dJ4WBtP91=1U#{nx2tOC2q4zDS>tYLve!tn5^Q=@DU7%pKP{4hz7!YHC& z<01TbFc<=TC}hH5betn>(H6yo zq7P-O`V|U%i(BlwCS>AG@TyvAvYU{@i?+jC1N3PeZN{L4-}eEC#Bohdn-jrbyAxhV zAK>wX=P?9$#|!VHEneY8JhifGS`PgL?sYJRp5tLG5#} zi8wjv`9;yXMAE;(Ce1R{(X`LH6nguuX5Hanf8EO5T!nId4_~ETmwi|Fp2r{F;kqS- zOE;9;irQDKRfPub`h+1T=g|EHMa@0``1&`#?DhHNsv>P}U&xDN3SeI-VO)*aebEFP zKlXN#N<^cx3UV_E%15vG`)hX%^q(Kydt1ZMh7IS17tuzSclYxsefRT=g}-sB+MK?- zKKO%R~qHKY}zNA7P3*`BxdkF zPEOZk2yF9*X`fgt%K6&-W?{n|ns1a*)KgHFMJUp=9H8}alsF53WuFIFv z2f~p{m(T&92W>%%Jbl7D!k7AJ97=W-eTdeJu`4Tj^D2p5X#v)5c{TCJ%6ZJR8XLOo z4gOb6!-6>Zm$T>%Sm&p^$ym{&IF6*q!$(&nQYC!imy0Z zw|4D1@qbrao2#;+0o@OuD&d<`U^EX7l24w#zWQ70;a^{$7|Z_@S`WCXSw4kb-NXXt zYAtW!tICg>@h6{WK`poWD15&0(osrtFK0yW!&-w?L>h#My`1<8lH+Ac@-sfAx4Wvn zA>XbH%=(qLuisei>gz8zx-OE>Ae?j6O4D$VDQkY;c&91ev2m_kXOue4i` zU7@4TYvB3U;dyx29}dWX|E&1p2jH)I(C*u1D`dU00eb%n$@AyM%~*_&Hp{qxu*9Pr}LKHHJ#n7f~&gYzAZDgEijq> zJ`@VKnaYYxCUgcqEod{59~YIGR(7~OA&D{D+e zG;fO%~yZ$HAUteu?^C z5_K=h+}BIBIdfrDnTyAapUtvOiNUXk-C~)r0#chN2`(EWHckRrBvHdyTGS?`#mu^2 zG7`+B+9i-6i9IF71>ee*fK5tLQzi)-&&63U38N>mtzv1BpM(}vKTI-QHdg-<=*;|G ziWs;SkV38*W;So7GYJZ#pnGO!&8&MSP~fI)qj{{2BB|Fd38tDj=UoEblKSsrWs)(u z?3l@emx#p#u_l!gKi&*7E5?-&nd} zv(I1@`jg6SI&LIjpBgtFmfxN^sqkmWazdT|xJh{~QtRdtTyAoGZS$1zr|=AXbIEy% zae}m0#Oa8QvX-0B6Z7X3{hoM{_siK%fh2-2_DG21M}0DVZSM03JwFQ)e9VASJw+|8 zXR5?y=vr)n=p$c6M|#)mEo|$%L_UiHq@eBELeCWW90#{1DO@8Kgzc=xS!ctP*J-m+ z4vLQRH8Mg-HXZgF3f;~{%e}>VOPVS(95<+2;VH3{*L5zn>x<1m;>=-L{M?$8;Xu3% zRVI5Z7A(RK*rWo7^4k1(gk;mL-4(gHrClw}%e+MzZDFzAU~s~L;&sO&cCE9{SPka{ zZ>VqWtjjHc)5B%a=!)}fak{x$Rw9j2OE9#N!~9r z%km+7fDwnlHedrbhdIm*3ZMWAi4vl;F-g=WE{V4-+k|%FhNM$yQ?@PprJlCzBy1Zd z4V$CcvYpaS+4+*0Enl`|W=QyZ-}8UyvPshUzRzG;VEv!>e%|N&Jw~d}daS6HJ3EN6 z3voH*U)WsaAKFqRW&5=8W$n!iL#J!21;Jc84~2>MyUuMDZ}wMkQ!n)&zj-EhuUGi| zQog5!Jg;50DEWvQfg8t&(xQHbT*%2=c|KUpzl$B|Yx88`bmVu#x#zVW1uhS}WQ?7l zHEunAgnKyySHh3iM4w|9^N&N;|%lUZs2bCTGDx}O3ll- z>||UlHUH8|n4>uej&4MW)0W*%9x6-m=Lyb$CGMtOXI<9zOUlI(U%(#&=%XpKv3 zccUz7h7<6Lh<1XkcH~Y;oY7thDN7RC0l>Hnl3eAuOl4i7EM@;#BovpSEXwmvmd6yI zne2g-66Z0|nxr_F3d;M49{H)22De1)}ZZ}FD9LYJX^nhRX!TeL%S!^`vwdr{y` zniyOVid?*~73bR$^4nM; zgP5LyA_31z+CvS2vSNLFo2MPqM*-1H+S9+20A=wVp5QUdggzraY)Z`I6ZKrm>47qS z1dQXClE%_RV_BqFW`=$hHV=#t-mu0XNsIP=U>guOcglaFd8`424ScS$QAW4EvVs zvjQG3$#d&VS}XM2&8exIQ``?j!^5Fr_0#V^{q*~mbq}sv_aJEpzSvl;R4YMe=RjcE2xmYB{s`N^xJOk?&6l_Z>>$(8T(coF{J`FAM;dy@-yJ`1HkaA_ zs}Af~-m<8sR~f|lflyZ?HEGiXU*#N99k}u%(3F<1=% z$FxIvSvGrVaFBcJy#Dme48oT`5}X0ew{MZ0(>9DzTuN^VGhp3SS#^+sGh+uR-1Trq zNZGY_QD2L#H9KqvPjJ80E4|)r51qVxbYof1;XQjoX zqy9a96;J*8l;di2O1=LS_v+CVD~_&Ee`)*bKG1XHOWSuAEJf9qSv{GB;Ct*jrBw;qMlo~2I9E-TcXc>RQ~ zxHuCE)D^`>?!7>7Z=iSSmM$nTs6)dPnMR&oQr=d+>hR%JBH1@~QG(d%Agr5T`wr+lme&%r%SZyk? ziZ&ezAi-ONsyhkA>sU-($Gr74(n3mAZ5L@WC7Leo1jA-LMMfe)Hze&afW%%l3K2%4 zoYD|#`6Z^o0222Q8nG4q{Qel3-k3noAe3SSdMh1hj4)4Wie-6Y0)2zflnU~8N>L)k zlW~e{9>#>|Y=~N%*~3W)##uhl7LeFJj0ut15X~6#hmTNk}pGimT+2xKTmjE+KLzqS)OD2 zBmeeyB5n;y{7l}>ST$c4JnbG@YX)W46L==8TVd7SxGLX|0h%Hgub?tk1fS_97(mtX z@Oq(oOB6GhM;2p$QY;>=I4gG#>hj=9h2z$bmZ1edW<{`L){(DHOV;nJun(>|&I!db zqEKzstfS)9#6QA8*zjPWd(oK$2WWCKbW4{P=xztO!rEfFENuhDZ6I83?pfgZp!|!7 z1AZ$Yodq468ic@(dp&>5kYJ?@913--ezT~Bwy z^#i-<`k@AsEL$jctw^o1jY6#o+J=Rv`Y5J)F$|uFBgcvRis&q*vNfesflBNkQ@*?t z@bnI2dcLtRl%*884JFf3vSZwPfO9A{MUr{IchHk~z3~dT8MH*qQj_-|bT2~Hm4(I5 z)A`UuhY+eNzu4#}zt&GCTWj%qq1BkRo)X#_r)__bc&*t_+!bMnIA5ks@qGaskzZ?J zC4Ph2dj4zSGx8h>@pGtRaxiw^Y=vX8Bp#DnG6wL3cnq-fNxB}A?oC#Uz$~$aRa5ZG zF8Kls(gKALYy;~H`Ww{~9mi2QwuTk;h37%*kFY@5uwtk_3?<5n=H`6mxZ^87A-1-) z7$=+Ht|2YYP#q(yf&tKZpHxnLDG)d#kx@l@Sq7;Y&b%FE0HaqRp`V!tpw9Q$W4o{` zNgW74IbXMY6uXqv#E0bM87@OZSm{lYBk(*WkHU zh^o>dwU18r;`^R|{yxv9_PYGElWJdU-@0{u*7h=enj#8vzotG1($7`97CCg5t{uKJgMAVF3wco9m-s5c z%!kiE|Kao8b0^oWJGm|da&g1wxZBOJ)g8}rCj$YQq)uE0(iW*iKGjZy@Y|QCso>o~ z@x=~<18`61DQcTL*+O1gv}t2sp+lXeRQh~MWtyhZ-Ph32*8mQ0U$T6QHZ#-A{m`10 zYaDbAI-P@(njSjdf}D&v@;@fsDPy6?Sfbqmo}d`fchL@Ve%GpBt~`x?nEp)Ii)T6)+Ku`6MN<1i z7h^3?*c)aoPu2q^F8h(7)vw3Y8u?cqA2L;Y4Bl`~p7WFSZEfq@?kz7YEH6yh{;`gs zj*cO@vC?R)lu2~l3{=gPohD=)Q1K_qi(LBrslx7j0b?CKmnVLb&JF+c83d~?JY7Ql z1_nyW^<)mB|v?d(r^UQWF-qTfc%`!8!CTs22g0VN@&KA|&vCpo;_Xp0TA;`=}ytbQmB<12>M z`%#>NcWl*Mw=6x!NiSP&Mr6f_yL5TOhJxN}Z{yuuJFlCK@{m*)oRwKqlHGG)KeKJ% zyG~KX>?$d5J38o;mmYVO4m`=_Cx2G3=?L>}pJTp-!cobme1+7DoM6%gf4DH`j(N?w zu*XwP`+wtmkZR_oJ_s`RwR6qAXaZ7Bf15A~Nf&^2K+e~RGSgct`TQ$*?`oo%|3xs# zjF$w1!XAGW&$n$}^NmbUsb+dGm|}+S4~l9hZ&GOanp7#~7Y{Jy%>FQ#bVkpTIw5dA z^QeqI09Zh$zt?=Kki3y}K0mYvlQ51OFTKRi=l_E5aD+7yUKDfB=`HV={c4IimG`~$ z(kFjTE*hix%%gJgob!nXm~uXUiw2owM$s@)TqzVsE>@M^CcbWjHCX^@=ZHQ}X5^XkFGYNlk@M5HZ+JlkCNJ5; z`J$eS?pZd51;V8&+R5w=BV&ShC|!R=?D`AiTf!6KM=F1s(h+Kd>qtFs>8j$o;A~<2 z(Wq!`Q8jRxs2+GedtNkSxEMH{G+!vh=I!`RG;i>^O>o|{lIKXP2JjmnnLPLuoj&+x~CCxtaO2sCIlBBG4IrS|p~p?>#z z!;@Yw?)TUjUUBi7>tmjGu?{@r^P6G+{l3Wg!9mpj!9lVEgc`WS4jMDxxc;p$-?u`d zvlNv{>fYwF=SB04i{A3{eZ|Z6X^75u0h@1^WWM2J%l%}&{h@1qwqHa4FX}`8-uksr z>kRW@o)aQnQp63P0j&ZHMFEbVo_~|`t77dbg!Y7!dm6L~NA@g^MdRzAQ9WAJQ!)pl z-aZ9KEp%5@kLxq);a+>Kr7FK;Uq zuYpFo28gz38D1pRBrCz}6rWAr7O#m$x+ds(VOh=-0+N-1&jX4dk7|00V=VN%gDED| z!tCDzwGL6Z#V^gl?so7y8gfB==U;~O>XXJk#M6!sfe@OB4TF%hhu!6ZuN+xAJoEOG z%m2PTsfQd&x2c`u9b(@yZFio2Wre&wsYe;No5GpgArt$Oi-qwf@FRntx5s*r`C*0P z#=?BZ**s0+tpvYo=RL;kaZc11<|{Uk=i>at;Pv)c4>2Yqbq;rkeDzNzn$HxzTQr}^ zytb10jCa^}$Rz&ST+w`z(aYWKf|nK%6FF#wwLt#a;`ly`e6!$>?Xg~2N!LP>_4QJ{ z%Pg{IrCF)&kK%K+*!U7f1{b-i*xXdI?ofEawguslwa9ekmuKqpa*k~QChnh`7c6K7 zI%H~ryv9p;4YnbrqKsI2BI*I0NSLeznpCKu?GRg4^&BnHrA*Y?Rb2cvX=^N}LOiaj zImdg6%ghUIbil&z_4omY_`j3azaP zdyI>qHvo5wIKc*I>7nEr4Zsxvt)Sjvx0`hj{*@@h1{p1JZjI65-Q1R+W3nu0z3%tg z>+}EiOaGDP*qnIh#H!Zf4qtn%vR0l`W5NYPkdM6#`}3*&J!KyhtPj_47sigAI@G?Q zwLCqn_Bh-7``eu!bvV7G!QZG8dXW3nu6v)cb#;}OH4lAZ$W>wP^wy@=u?`Ub`|D5- zlBj})Psk? zbGV)%JOK_c1nQaH+&TMe#bXMDG10->c`0^iW1NZE!16iyim1#f2j2P5?|=BCpZq_& zHdeN0Whs?s+1X0W59Yr{v{4kT)UZ=S3ninO@O@IhQ!$W*n6j}Pic z^^pkBUAxBpv%CoxUPKt)lk#GD9sFz-t7zyF)ivCNUnj57FKT}QCfOzMBH2GKgC`11 z(P%6!QvJ~75o#bNpoQbB!N1{pOHw2PMpD2ER#AtQfIOUj{JJp~?_xX!TplOH1s%4!gV7-{>Cn6q##!>uTES_jxjUTS_bi zT4S~@r>UmCBg5lSEo$%-<`w4Xa~$@X1sNWk_x&;G0SD%;E2xaAD+p))GT4oGt+cKn zL!QL7z99EHfUyt=^h~04vsqDTaQQYIe+@}dHBPF>WIVVGvM<$j~K-BTN^L>8BD*cuqgL0o)a#0hluA#IN1M^$@)!Af9Ss%!qN3i>x+)o} zt0@ETKzghu4qZ^p;R&p6N*t*!ns-Ul{vrdYW}fJGVu%kehQJa1gG?%nRSSx$$;nYFgN z#F3e4cUIIW1HoWGSyN$jWM`R`J#BEZ+Iq6mmCK6C^Wppa@|^VaWk#GsiTTxGxa;Z| zPCN0FTk&Q=d&TBo7<_jY4D6Rx3%yXk6>OUo#} z6f}?POVZL#p9blr`f-ryP|GcroRMJAtI1G#6>4p7uSU}`tO}Am6FBzWIJe!Mw3cEo zS{wf${A#EeTrm>Td-PWLJ=SaWSgr7Tu)1o+Vi}vuPP~`Rl8V(l$~_z&{1;A-^&+Yh zp58@n@18v#M@hcN<28FvcQ!OHFnc{$oVsEe)rJ2H zVO5|6%^Cd+O3IX%-uVYMHj=lBu!Kphs9PHc0Aovt_zx;}KX`QK_WOtkic*H}-{KN|qT;}d+tFLKRAKwOY_aDD!h~*8Z7g@Th*B=ac9d+%a=tgO;v9+}|VL9X@@Fvvv zYFJKaHxN>nyX0)ovm&GVT*d8OM^+6D_BPfvHie80BOO~0tz6^xJDqDYL#pDsA@zoy zh5p>!?25uNyI${VtKPJzyDv98yP>eGH@CuRD`hc24Oq{l@q`Ufj6!M12}5OAqztgN zjXEt^5oOEXMX}K`UwjD%%UmsQXtn2*Gtgrn$|w;n=-+4bzl#4L2Ub zcEgXL+{~eSLn*aqAVYl`$fQOLf(~VFQXks_nz{F|^}@!)C{$59fw zOk-&d2(HpZ)aCuWuN3R_aIHy9y)f6l@XX}ZYm=AdudwNr(es1sccSxi;iU_aus)37 zRlsgmr>GbExHItG$>U@p11gqKw7smK)Sv@6C$-~nZvY$WEB79wqbP@T>cn*5#uL%Q z1yT4nm~y(YxYk(j%gKR_l$5(o1NVd*t7}^E3v0@%%~xa=>NLmJa(@e&n_4<*Vbk;{ zt21*_z6Or91jiWI#O5g%Sp}$CyyHdf6`C4L*6k0sH#N7RPzBP$(XC|S+nPZ=idrBv zKT>2vnl#5-OpJd+n`)3Ft3lfBH9Nz#)wNCJWe*#j$z?XCrNMs`m9?l3ZUUX$8%-@v z2WaJg(xoPqYSBpPm=vpF`^};;(T1$a`r|Ul-*zTy*Cgp0KW?GLg#CguAxC=+s_@7z z4R22iF0SHs8Ti^qjIDwL`|c0lj;z_aZ-0G5T~)vx9J>%Yvt?k_=5f2VqQ=qKfozFz zV1LH;{Q^wOtxZI!0#bs6_U~haK)sX@je+|}{(n>K5doz`8;jBb{*(Zl10iz2({fw{ z$$@DK=eRiWIYu_lxD#B9(lipET^=hB?oW9?y$9X+9yFkP(Bo2Ay;J9>yb$B?@d<%O4Y>LFF`tB5{iE-2Sh-h}fjv#Aq54fC$26;;wcy5jmc3~Ag##A+j+ zussqXzV|!((SXBt@FLd|kxB3|2nH`ftMCx83Kx{+DhzrtR0Wv&?Ne2r+B~Q-nv49s zAnls9dD+@!&D?)IxP1A+*cw0~GB%W!r#3baITs9o9{JCRy&*q^y#WkJVbZY;Xh?RM*w%GYn}f7w zogEoTVH-v0b4hUC9kJDa5|%?{?G6ANq*qNctUi^~!8*Uifg0fyo%5l~;lbBOZ3$$7g=I|LX=YKTCz zhd>3SU504itsMgHqC}Pm9QO?AE{E%7q-SkTTLc)ASR=6h4rydS8bz2(-nBgf48>R^ z5dT#}n(ttKNo^9kzkp2w3?;EjpfL^LJz*o~?DMcmK$eiqD1qwC4`5z~Z4$cA(=LDlc)J7`inmN){R;DQX9L5QZFCx1FWh(`-#JdcQ zC|FMAj|B7)eIzn-;o7we)TS-p3O`pukowR`MBhYqK}QjN!#Pr+UksJ*E845yTeP?I zQ0eBPAp4mMN++kQ6UeTwS|4 z(%tTEscG2q)wRpRtJg<7&e3xF+R=5J){Sk*+tXj&SXEkJ%G8-7i?(VuY|!lXmpAq5 zOEU7bt9?thWsjg*3`J0eIwN0?^WqKo_P~`t(}c8AdPh{p{m))-AjF*l`?270wxI-G z+J6?kN8*}#)$!GY_&>yUV}|W_!uHLw8Wa!7>?Sth zx+uU~6#lg0qMf!}1)Kz(r%OS*QE+3^K5KPZX~Wo=u_g7Ji-Q(z-o7X6R<3C)9nV%5 zW!F>{r0Fs;%3I3H)$5lwc+ExS&i3wg>wA{EE#r1)$%T5O;-+BD;cOK{d`?iG(w=GmpgTm=)?jP+NzqfJY_o%&5 z$9(Wi?wSmZnj_$;u-SU&Ws0Hq8l;ag^gOXf4=UZ+jMa3*l&W@N^8q( z4YsX^_71IIb&oPkZ7CmtpF=tg2~miXnNn`o>}LJg*TBnP6iHBL5eZ-)@7kRi zQP}Hjj;5;S;zz7t+lN`o(w32Xg6jQC4YtPWiUup#9NJF{ zxhl4oVO#5Q#-%4UeqLn)DGY3SmDnM1Dd59DJghMmsK^p>7RdNL2hHvJ;z7iPjUZHPnR`UT?crAjw}sYtlWVbDT#tI`$;jBUPW zWC3KV=8g_yR#s7dp~X_tzjMqtR%dE5(->4<*Ue(+e2AyotVrXn`lxE8TU|U!})K5!MwjRce7X)1bIxO1$b-)KqE4IUH0#ESP{^?1xwbC>>0cAV$guH*l z^6IXfY-AuRX((E{DOh1Du?FFe=kYR7j7(XwtUweRBhChl35U_ww{0M)fRYfHLf4{} zN}6LQ@5W;%SXUC%WZ%2@zwpBS!=sDVuU|AO@BhxfekZ)`sck&Q#_zCQL4w-u17GL< z5tT%NibH=W2>;N}{ze=RWWn)3&v5^Y;;UFd5E(1Ngm{c zOM()a517e-v{8-u6K+H2Qs}G-NaEtu)$3D`=&Wp(`z`kt$fWsBK73~S5pC2KoYe@( zmG^UvVWH3e0Dn(bOh?fZ74VPNcO|3`T|x1cA-H9^FMtQ`ybO+UkD+h=*=~Nkh*4_s zbJH{W2>gc6oB{{nGY7a+yscwf(mP0kjv|Fdp?j+o&rN=8Wd%1m7P%V^1V*D12#gyZ zghMk2&x885au1ox@to)Qh9`T6jV7;;0xhkdLww>-Qv9ub!FX7?ybCx z#cm{2OrR_JG8auJ7=cN zQA=&DB`WVfxM^f(){ib+0L=}34a6>kYqhVZc}uY(6tB|>@)IaL`UPHmx^MfTK1~|De0gG0KRE$6DYp{*faWivOQ(hI zKk_;JS+oPA(TQRGcywGpJQ1B9(~rUD9s&=eZ?X@u?*`=!G?9Ept4@K{KLzHw>dP}z zL49y)=JFJB7BmCXg$vvtxHm3emgHFCvqqmYTz)qSh34qHm%tOZZ*xa3Q4HZfvCjc? zX#xwBWKEDWy~h0kRD{4tIK;gW7R%x!mdg#5Q3Hww^5a;pYZoqD1FuISSXTHZ!xQcsveUUHU_EO?g+%*aq#fZy(cc*p%7m zsnHngx_5Wi88tPY#>`C=HxT?A!A5dniA`b{pxFZWqOlnCX1mX~f8)qKCu|F=&YV#z zm7qCXvwYmYde^=+Wj0%xZ`_w>OjCsEx+4BlAoA+c>0^e0tI=q5;mIhR_2|ik@crnR za}1=x3H=1V8O3pn_W%y|tyQ*MwA1lM$JUWkLQ8n~%|$D%>m2#9@B z4orKGMOx|x%W8(!tT1VE({c>=Y#$Fs>T7H@6^1Rt%S&?L{~7m{6r|;5mE_mtw3aMg zkX4jBxqX${?u6n>zIJI>d0Te2BDb-)XJpsPGMmX@&dJbemoBJiNB`|I^^Able2Y3a zTa}rnR%B@{HJTiGdK$r5{0RI2&IyhT!_vPH7AKd6#oa&>L`Wo3oQ|le{2&!7z&miO zP`?(sQ;?*h#WrDTViY6-w}&{Q)~^Jep|+8IYc>y+S67!02cu;b;Kdaqp^!3j{j&b8 z!K${ZK+tOD_E8++enpkE&I^%L8iA`oB(2y(kufH*yi2fgl0S<=>%`BFMMBA+&5wH) zMU;ykXYj=W1&pmrXq00bWid1o04a$Gej^4tiBY~GLQ6`c^tE(+x|kNIRxhF&4f=wN z(sdk3&uA>gG;l7tm_}<1;E{+{s|ezV(Yz!3Wg=w{$175It2g6awiBRRoh=`j%$8j~pP zamQDXFy&tponex{xDE4}#CUwj11ga?_aOJDMCt^uh;ZXl(L&xo39WI2L^n4rV>~UE zOerC!7eO^iD2j0AwkZslrZoZ0MBs9g2C`!j<)khBGy#^}Hq0M2d|kH~v_qP&D`^F1 zwM!|#Dyrr3d#D@ir+9I)0uK>+k;8$cG}u%&9hC);P;65e*EMf>{=UZA{72}|{IBI` z`qrK9TW9`Sj(O9_laHL!Sm}?qfkm-npf?D=8@az%;Cvt`+ro9oLy}UQbc-G2yoCD= zv^8R-Tm+CcHyyM!csk!g+hR^S1&O3>x-EE8+zq-X5|JbMunJ03i~ClXh$>qqf$S_= zqd1ypSuVbiBb$jNZb5QJ17*^42o94;&|?0D?IKx(Q_`ABkX`V0jmXT#{rfl0Ttw>8 zMS1`D{&V}suiL$L8RJtphd&c%h>KgMgJ7*Ap2fHREWf|VeXL3-vp}I zK*Dwek9Q7IM&Nk%jEq145yq%^&%7(d5v+lIWY`l)tUHPryr%>fp52UjC9v|~%^K5Q z^&IWRV$mgN@{eLHy2P!r9BM!xPH zxf}UABG!^dt4K-=YQ40GB(tlL5|W?qeGO$DRpQ!;VkzgV==cyJ=Pa#5CdgU_>9Yb_ zo`}MuqSHeKE)njeSmuh8xFj;4Sk|JpuGv-C&R8TMR$4?9=;?c{tGmRI&b=zof~OB2 zyaKyMPD!GZmr{nLDz4ORy#j@_Zz6)5BzXVq50rdUEcc#D0YWh91E~!(%#H$PD1Qhh)&p z&70u)fERuRxQ(dT8-in*{U?-X0MERjbD*;xxkfHXdc27~3T#;C$RVE-wudj0F1ZXv zXBo&ev#YQjTr2oYkrF`8VH6(2+}wzj96ogGu{)2SpK5GFr)>%ra~}=46NTE5oUX59 zxg}3*U8l2&jqCw3ne-XaT>9W%AU5}0iPasSacX&2!-bVt-){=oi0{C5X?`c-5?DKp zfL!h&th{lJVJsyPJj8c+`yu|Ul703Mm_Gchn6C7*3i{cr?4#g$>1T0_0OFH?^rJb- zJqAAya-&i1Ly=E_*@kmiE(nRWuo#AYzR1J`$h}?^D!TQzjiJV;!-6iy>*0bfhcqLm zyZOjq1$~VlGxZ_FJX;`o)=EaiKP$`uZ8txrUCMu1(~S2@T1N~R-3beE`?S6QN;tr% z6@(bdr(f(ZG*}6t$l?VvA$?I1ra-72?Or6(!Boj~L~R`YJ7|s3g?c7_;K~HdcI_O@lQBubp&~f{en$_-wBO?oYhlkbQ1sUJ9>z(+nbn5Nl?cdrC|1#OA z4vvA<%S`BPK_QB99v5PZlz3e(p#doH9%TNTq%`qfj-^RXy@ckSZjAjZ?q_K-noN?5 zCF@`tHm0{!X25+d+Y$P%b-E0>_ zA@U%|4f(@ie~9~#;eQ}qSd~Oi$0cwDE%-J(oN-F<%NRdT(9??(&=J8u^VBMZqZzNP z^L0)M-Ib7TVS?-%Oj(MN&(rS~gW|`v@TA0ctdQ6%3KLrz2n!I|1lajcWNRS$Ao3Ni z!oEU^nu=`661plXT_5P`uGw}!ro3eU-|cHnnJ4#)36GsH(rcc?Qz%+se~Bg}^$;>> zuUJQ+k4Z&Re<8im#Wsw*7#jwn#taG6UMjX^P9$3s)Bx1-BMEEt&eYMQaxpAAafHYM>52a9^NibC7#0MD7z} z7x^;ACoV<1HX%N-LS!xhYovh0v*M_j6LBEK4ML<0vH@Yg2i^<#_DbaWKy4@Tes}~A z0P=v$z|}}2Hb+QL$0MH4!|I4QX)4Pjx@J;aALSOh7c@}b`y-1LV*F3VnBO$KTL|a7 zNpu1pgEQ9l2A^Y_7Ru)QhGp_OUnfULdDa5JmtrLKhi#}nqZxpn!`}wyvIjew`*Nb@ zr?wz{rhS!~yOuSeKKG!<<8LZie-H`)=fcZcT9y&oN=W-@j4fMAX~$HdLMcq}KMi`Y zGVml+HEuzDS@(kv@FjGinqByrLYBXXf;DWE#{%PV)&SkcKs$T!e*)FI$pB{3p2I z9AfDtJ{?vX!UScNa5zc>xQ<6@EoJ=-9CLaf7*XaA!}lND+#%3vJKgRnmm*Nb0bhw%&WW!3mrCd=Co{LNuZuBZf0!Peg7-hAQnh}oJu zM(wl2YLPaYSZoog1?mIuM{eM3zcBBaw9n7EUeZ@@F2;VY5xvW}db$QAp$S1*t2)oPGN@Z|B{P-KhFC;j7# zoPmE>JK#+m59vd>!(^X}`b83ie|6_5eniwe+CyUdT95Ap16w`Ooq)GjbOL0CHyl2^ zAsn(g99E=aoIkXA>`=JgR$b5TgjexdW|^aLGyv{*2vG?I5tSf#01iQ*!~GTqcFM&2 zT`lr|NvQcE6Nu781W}TZq4-B1!FzE%(XeqQqhF7#C*>^fVM$@YZBgvw}FWJ{d>ov zCXR=`+t`%(#7`r6^<%|WCaNHSq|Sza#ENaZ3Oj!z;I5eW^9wdwNubuK}EliZ9Vu}X4Beuo#X#J43AuE-tL zMD7iY`-A^j4Yw~uZbUAyI{+7fAUZ?`UWXEq?*mzgVt8&K5Qv;P6CK&HVWB@7MXf>)4OiV6(uY)?&hV5tM{b#ms zh%D)hbT0WXk8uCmyZtLglYziw(O0(j_N-Ye|KJ1p+BH33^Z@~9oyb{+nAjZ;jn&Zr zqX*rs4FDcgqY-sCw}<#ePRgI;ZDAfPgVLSi zGN?qhus`a1$%dI8_Wwj!=#XGX=Nk3b`D!tLi8W;RZ?4F~t13|0h|p>C6o|ymzN!^9 zrG?Qz`HI?HB7BbRZFf65l~4k0a4gW5+v-KS+^ba1h5bX?TxJBRbD>*UNF+Szcg&Z9 zM!_&_M~8J>G(u;xMrcnv&oJ8}(u7>X1q z3YfHUpJFGq+e-Hw#;w=VT_@j9c3pN>Y)(JJ@0U4UwtC3XJG5uaaY&B%yc(Pa<#j?J=w!?iSe;w`@6Rn|Wo{M>Ms{e522}>utuEcIJp$gL?Gl?Gyu-DK z1H@bXWc(!_WB+sT*LX5hpjdXh58u6oaXbdGK^0LSDmoW*v>-(Z@$i?|j7KIS3tHV> z+RV%u%*bzLePQE~6Vz@->=imDbx{4wgAJuB@S46CiR*P#fFJgwZ3+c-EVd&_1NUhK zl`pX0A6cjwcSTPB?fPEKUpH>MFwy%i=CF5O-MvM|^W`ULUYtkDmsLpC!B3)A8Eb|u z%0xBINUTBJ*}!$pgtOrfUE3qJpM+T3igt33QHhI%D^RKHNhoU}>t6(00`b_X{g2oK z+4vLdT%TjC>*tY2^ZCXO{^k52X?zV^9nJ!)NJ*cs|CYR+EjU&_t}S^Tt-D3ftrl0y zDoS*6Sq#4P+zvvzNe?WocSh{@khVkCEOXzL4WA8cPFw!03=MEjgFcHq2RO&>PrQFw zf(zk6mlMs31FGQtkxo~m+vyBV-2&&P2ggI|U8_qhj>@v?(Li`lNSQIZdi_q3?OzqA zcTib!Te@&=Vr=SMEj~f9hIPI#%Grfj$EGn! z$OSFdv{53ib-cvlV;Z+_<))diy@)8@dnUH6#`5wC90OKNXSj4njRjd44G>SuxIA^&6~c4X?<S@JLbP#?nrATW~Ce4q6qhy*Lsw5SYAEiR~o8o^-FsvG7?eoX6lHDz`m7?-4V6M2u zl3&U&qAJ7C!P(6iMMo>e&aq)c9otkStO*n797lmJzoIx2nE|<(Wjt`s^VwM?I%Amv z&jX~3Wan}c=5D(%^$?)XEy`Mey3n zOm#;`_E6XNF77ew#|)-Dd$Md}-GiC0(3)Aewi>pNCah6_^IOOK@C#M$nS3d7>g?6h zky2Obh-qrvGwN}*jd~`6A?zd2Q+u!xA$BFHPoO3Kno%G8y1i-NzD=PE7lM9&aBz=3 zcze_6$R_)qK`^wakHipv6Vm&x%p$9mIiNUuJRE^7@t`24QzixR{2V4T)f2%FA5t))5mDl zG$7kIeNi8Q-{8j+(Z_n%tm*Apv!C=)#^2`^7 z*fYUX&vI78GFGgFrHhlU94M4CMzRue^-3|n&*wc0(nuCQ$XguH;$#utLRklVD}jn>EVALms1zDZLc=T`fk<>%u6UwIX1pPCZy)A$yoyY*M`EwN22 z6SYO;X%#O|r;w&C2_)$%wCZt!^jWfZh%Tlgv9NP!VwQPnVIRTONUnsvjQ5>_&*|lL zz*MmfytX-ajKiBu^-LJ&z;r+biBD0e`23HA5h{q!#6_vB2CcDyD&QF2Z-gev|0qjF zQ`i%6l%L|aLtF#f$#zhXSRL4J6su!O6|IB%o-kr30M{ozXa^q|o_-akZ%svC7)QcZ z?iIFng2rCqb09uDHc))tq+pd{d6UE_Gs@=(NTL@ozR>57(ILKX42d*)+%?IS<0k}T zZylBgPtIx;T$kFp&MNKhJdW${=^&CH*=HH{4Pr+3Pz<61vQ{p-UTn>jIBNTavKPpr zU8f5NevL!8xpMG3v~+m!-evYU*<~8&M{&={K^it+`uF3~RWMSl4$ahN!X z384uD;=HN)dFM^hyDdfP6us2u$eyA(7_bjzIIgNyin}y-PZ$K#WemZzbzkD{>=+Z^ zmuj;PtV!0V<>Q&PJO0IGlFtiwjc%s7(#lBwxp_8^#+%JMr%jc5VctNg)~OmN zsn#UcSMi3BPi7%1yVb2gF~9Osth7U20;)GyXTJmwVD$xXZ~Hc;b05~P2Dy$UeH+z31N}ey8TWGEeTQw9TV3D$sQbRdHAJRF zzQ?|K(0%SM=_0+gNuD91i){vkuF7chP`Y2gIW={2iu=$zocXJ=P5-U3#yhNj`u(S$ ze&4dLyTJ4#-NyfHT-RMD#4j;iX;EKQu`~(7^sVNF^1mrt_%~&Z%_5lT^*P3KHIwIR z;2Rm6*MfP_{L)>}wBtFQd+g`tJ$B@i_=>zqQ5#pYH4gJ!Qb*FEh`aiRTkEE^tJ4N z?7{679*@C%hT?qN@fwrb=0Z)Cxk{#iio+)(3->SR>Toz(+ge>M9g&H^u7~s`rD}Cr zZdFOaN%aF;Y;7)ET}#kuw>lcDNA5eazO1Fph^pSHiY%Sh;Se9w*Tm|qh;Y6E(v-nw z?x(je4-Rr~oY$X@2Efm`rJ*QDLrHe5@2GxRIko?Yz`oNl+P8&A`vw&~+uKa?h;l(| z8^0?qr@|9OkM7v}DDzd8vbrl$%x`{` zzx?YjJ1$R6s&_oWUEST+x4TdM_|B0fp!LQRJ9g&xq5q*ZplMBd0rz|2CiheD&MkR& zIC$qr{P`$_>tTfwxGOd8H*3F@_2bm1~3^!5gNmu_iq z-_ou=wzi_9vRIi;i+K$PgTtnlvcfc_Qm!&pbXKfQYhT;mzV?o*-{tDZHL7K}Pbu)e zC(-EfJhbB(Ktq2V%>wreu$fE&TCcA_I{T3vE~*0?SH2#o3?HoDw{Ly8eaMm?R(CYA zn)TTu1A%?|=(eq2((l`svtqH?n7d06wFcK~-4vq)59KLsZZPVzU@nC)tLR)1qLTljKN%2;(;K=Y+uTM`; zZ+>y|wEFbqi<{y5(3o!w^l)#1dhVz2ZHUGtwZS>~750&MumwLp`BP)|7)@#Zv7)V( zvcr2yI;zyg+lscYEk1EMRHO$Zp{Z!d847X7Lo(Iu?_s~TiR!R?YCA2l)eaU9Ey?L{ z6b17K3L?eQ~}zw%0Gq^DlEhl;?wo!(r|r$O5p<) zXF!^H+}sPl&quuD-d^~9p?~F*j|s8BuVBB3a0+-!8#UEpI~6ki`a$8=qer(+&CE>J zHZ|2w2KRV_p!wcc?)B~of+w7#PQ>q=kSAE+-HrHNOT4{S9KOm7wxZjq4or@XjXTDB zR%r^1)VwvcWZdCDQ{i$|oSCX_Y^krS3r!J@?4kw{1TcxA(u{>4K}NcEKA68qYb zH5=EAZP4r)ur>4|*;h00g>8KC6hkqTe^-4O*G`d3WUY8P;!7#(f{bH~>Hc^*VtgVp zZjMAFAUABb65M(}#?ek$A7pqw^+9~IAAvuU`XI{r*9QR=pHG1hIlfCXlGX=VoU}g3 zAnZ3s>Vqg#)&~LhyDSibbG9(;RP{j~o=1Is6Hq%#kE_76TiK0Db*N$}aNQhaug-tP78+PHq_iLP?X)D*&Jw_$wt zH9PkX`Krr#cy^-8;<(=w`0UqHQ%Yz`ateV{ZakO+<@NhR(Y2eMhgcE7v?ApnROX$$J#RNkL4%~Id#gm?RjY#u|C~N zJM!sXg0Ev%-ltp2;w+>-T{DRH>Z)>;;Bfc;p6aw^UhlHB>Yn}Gjf3W#6$ASmzqo$= z7mj@cD>5>(3X95Z>c+)g#zE(xv1@UoHebnmc%js*PFE=~Px%afUK@7gdcZr}A9z3S z4}nSQ=k?6b&+DbUEna4r2Cq+Z?}68)eqM6k=JxY;e0RzDidy9?3Mr|vo(Df(nyGfX zllys{)Hj>D7JUf0Wg`>tYiI@X^cIg4yNX9jrp8-FTU_qZmWlR`P>9bzou{uC?F#QL z1BraSz4WgBl)m26qod>Y@z#ZwygcUXwRE%5&71ZU0r-G>dt%Exf;xp=bQv@$+VLna#iIz>eiDi)wn6C|1h>^^hD$ z$3gCn`RV=O*0)$vTN_6MQ&SbcGwP{;C`!DdcE5oI(hl%#X9^Bm-G`L%Em%#&rySrcB^WFd4^L_WO zzgJI9%~Rcbx_hdp_u9{{D${&-P3W9v`G|l@atTL*0NGaIqlccAm8;cY!g+9TriX)^ zem=9Z4u_rhf#`SenY+kQ0FOQ+qo*DGXXuWQ`$-!Ie#bO%27bb5uwRT*UQXGFArq@j zMJ+dvre$rAw#g*7o!{Anp2(Rm-1~9QGdMWVV!4`ZIH}Y-7OPr_-V$0Y8t<(dJKy|= z1EaVASGL>U3avvug6d!2u$bj#r!CDrcCEfs7#uhr4C+pzFvLv-L-^k zN-~W7BZ=gbylY$YJp=)+tTpH@lW&Lm3Ru_A{K_*LqEz|3?g(d~Ap+73Fh5P`_dL~d z*gq5I^yi_NTRVVZM`OdXXD@GmISg+wp8A})M^|=7KHX3l1dv{_3fgyyKIQbwcdvSQ zk*8iY#H$y$Nq>JCAm-Qg(}7NGFnH-d4Q{|SLk)x~V#cy4PP#^6?fnlwy4+ve=DqFO zK;)k@pr91XbDQ2^OmMFQ3bwyJddd=Nm%I(=$F!W$q2NKI1bdO&LqkrHI8Bv)z$4@R z>_q6cBkCg8J`pAKIQ=p4i}M}IWtJb)9QR=6RBDZ#%)mQI5SH`1%SZ<#_MCTx`RueO z%&{3UrV7pMYZkxQ7yt%wt)q@KX76IeCm8@7%Q!p8M@_A&+BgaWVsV+-(kx@LQA}%G zgWqO(^PZAd0$0+XY{qJPXZiM;e+J!bd!d(xe7KW2h(DxlHI76-88u@VuqK~Y$6HJ1 z2pup{$vni-YMd^EWLzX|?gk&=#(ol<)H0f(`3>@PK|OKx#C8C{vIp`#mu}Uztz%{4 zHZGc^&ps2ZsmbRNWQI0HoM};Qh~;~%^!brVl#qlH^-OICu`%F-aQBqtbZ%7w&t2cY z6`ZbCaG{9Fv0VGkqN@HYm96{jo_JBU7wxJ4jAYE``QL0Hmcker-6|=^h;+9o$?d5T zGwBfGi2#QY^lU-vDZ3^pRL>n>H_N`*oVdG#Eq2`J z#u`*r9#4oYQer*|IC7=L%JqQqIP4Zu4*sY|A(ZC`CC95_vf z#CIm+vJ2pf+K(S8pPx`8Xi@mAXV2>~je^U=YHXRexDtP}9{{wGSEY52SsTry(g*#S zw`Ok_1kWO#D#!Mif=MkL%0*0HVhf0cswr(|IX>@9#@s2JvuX+4>ri}TDK#c(Wapx@ zo#$Tz@<{LUKLOr2mdS%hi>I9YbyEpGPdW5=&VPtfD4@o0{Y?d*Q>}0WpkZ)Z4VR9j z(v_yxtT7`7B|47;kvRp5W2-YMn`+5952*NVGLKjyENopf)icE8;C`NN`ob@7YVFJ) znHHV+b~*%02;FCqwr*CJN39U6L?`V^AmerNS3P5Jn%2(C#f4&Xf)L75##43FbKu7d z9;ok<8)(r^3mHV(KbwJW(|<@~W^PYXu92ulP-r!sFP~bN&QNd67*n98G)K?zuf_Rc zXe8pKt-f;Ecg@;j>ty`KY`m?;eXPmZ-OWu>qj=f(#kNxJ>gAE+%Qf@<$cdO3u(s z2Go2O^^lSt=+i#NRRO-<>)a>0s^?eBc#Be7ypf|$UO=IK(!=ZO*DU!| z=A+V4EIV_xZ*`U5EIO+Cv`@p8ov9AKiuaIgE6!9MhY-$aTDN;hwB_q-@RW*PExT3;SCddz*MrWiSc`j?Un_@0ix)MIDz8=D zOTwXk`*5zm_yOLKcFmiwq_y8rUBn1oARRNS6NqhW6rYNHzjlcO5E7Ao6c}0oP!fZ- z48Z_+jlcv$GXS!NdPnK1As2vr_6L^{Gk|o~&(%l>K*{|B(+Jl#-$xVJY5K+w&8X@F zg7>>1qCZ9^^GMu5qci>cc=KfSKbSwlf0q^|`SL4>z6xJ1sN9G&xzJvPr>CBc+`QAR zN@yfds|s->m>f#EuXt5*kFnjUP{nbG(9&&RC2$DuY@|5QCAb>`KhWbv6!`ITCxR<0 zQy>+_x9{E+?LU4uBM@}yEBP_ZWMg^uyg5Y7t0KA%dNTCog~L(%Fx8S)}w1@*ujcoCuk^I(>|C{aNhFc2@iLSP)slowecXd33ii#Qfo1GD8t9SgdE zc{ReZ1ct+m8UdFb!tjh-@FU1aoUdJPvGu**Bsd z2R*=iKyVa+AuuBl5=Bry%n^i;8CV3f0-Ta6nAt61D?(+r%_)B?Jg{5m zl)Dx2q}%gU*b9!bTk4eE3n8o9;*`$|-k@9al*BT($PbCO81ED+at2uTKY3E0;S%NF{+2#wjb;856fX6#bNImFZ}C-zeXCCAlZ#C zjZ7>d!y9iKsVV{)Hy9cjDuN*!B#oacf^0VU8(Ats4mMC4KPq%bI(!M9_`{}6)Fm8Y zjIq(kZttJAZr;de@26vo5SQG|Y0eg|>cE&DrnAA#i`me-$BWp|bL9AGtef0{`}n7` z1Kl#QdzYRg&ocQ33jM@)rPO$(LzYRU94N_pdmCAgLmoEZK_4grQ8zw+&Fu75|2H zJCMMNpZmHVh-W3>X5AYkBK!k;{o5^@uz&Qr$t|z2pW?deEvIn6cxlTSpBIHeC*c{b z7x7C_B@A*)(jEnbrQH&=N1ni1ZYkQMDPbYEI6e_su%cUHpC|)Z(k;GEG>vODXndOU! zYsu{tIjjhdzSdYtrG68b9IHh?l4+Sdu0J^-}6zU{H`r$=Q|?|tS^jmk0EQQhO@<<**kj^P8_RLt6Xefg$wuF!YEEX!k=^^!oL-U5xW~|(T9IS3 z$KRZ3pG~^=sX4_yM`w?_Ipa86V2`Ca?KsDCPq;aYA{%w@qen7Dj?^BzM|x&9-5$M1 zYG#hb9-l|1em3DAtw+kUMCAnJlB7)mIFWWq&?bK}(Q--Arbsyv@*2G;mo-szN$jm) zFp+eL?=8PN(Qrxbttc`PeF?nBeoFGm&DtZpr+kv>m^`^eefsV@xq6Ash$8vPoVzhd z2Ta!BDB$iu=kCaJodgC-c01fvDh>!&8InSuHAGc{rOU|RhY%{6r}mwv_^j0?=H zpEkGV3Kr7O;(=j;ae;s0`2KtfSy+E_;}~y3wi5})TZ!k{7zLxR#NwrIzAxhI(aP-B zIa6CnZCJBvWmeoFZY2kfbASm>I%~jEnLPq$W3A{XLoH{=t*GN$LVW!fjajI?;}_sx zA&s5rppS&$WLe#9iB>|*#v!hXixt7{httoj+umT2l`QPnsWBgfld;#n-LVO$N5iP^ zJ`1NtubJHO3TG<9@a|}YQxw-!?>L1s#$g6+jnY z5-CP*l~r105&QjpghLTJ>!;&%~((3pH>;46EE4ZpjmNnmF^I$?I?IYW}j8< z{-Ea~{LY*IzV2@($>%wzr>8rW^0rkTovA0j5U0+7+R-dXow+z0t3RA7Ju+iiI?&Y-xls(>vrBSCpHdrVGGrzsyvd^+LUyTb?!l+lN@kAYT#V$Lytk!vW|%k!*%#|C`?^)S zc+IdxhkuOTa0qj)0K3k2f$h0=nnn{34`QEeSs$9w`wnGtI4x%~Y~~INV`c*M4-WgB z6OxP+K0soAv^jTDK;4E}R+0%?CfdU@Gs3Ur+@)}b>*>;MTRYQ%^`E{_Y@h4vnX832 z?f(z>*Lk~uh@2bkCCBm`241e2)wBe7|m2W7+#+-~Z;z5M_^lLXnT!iNS z<0N*dkM!lYT*`kSSu^I6GW>rV3wP6Ufnn|R_x~uX#cBVg{2Td~Vl;^z-adjPYyMg- z@TqDyL_&n={vXT-aM_bf`EMw3S0@)3+D?1_1&>+wl6kU0d==OGe<&hU_c)W$KYhe6 zsdFiDo__91iN8YUg@FH#4lkNrh3aQrp2R&XH&G@$C=b#x-2I^u{&dDZ86K)HcWInU zm~_@>*6O$xjqa;^yU|VgUmuBqx_zXCPmR0%5+W@3j+4>hK4O=6xmoX?0(a@eMLyo6 zO)CEUPso55jf_cn@4fIr>-(f)n2*Y3TyEAs7CH$L`ul%DGMB2k zS-4N}yG7z6wDq!!K9ZLlxmh?*5xW-RBGmW)f;28`aTY2Cdb2k z{x8Vm9_OxIeDmRT+M_JZd;1g)z~F-CFC6S`kY@l6;o>&N`||{^Iwd<&D}X|W(x*(T zg@psQegA1|g=-B>XD656|9H?LUc^5hMA4UTz(iyDHW)dG^^+I-S~!}4+^unU(og3? zeR@v?6@EI}&-9`z5R*KblQt^zfrrb*dAi-uz>)TPuR8OSR+~Hc%3hsTI}EhcjYDrO zyJ96`OGsqAULHdOmA@DXjtcOpo}DGxvWEUVs4Hht$-=HUmat`!y`###=eTUKr7xCu zighUwqBca``}n90v4IlSwE!HuThkD?Vqys?k~r66C3UBM_V;gv5JDv(VD}>P)D@q8 z49^FQ>HRb&B3w@79{)r`@hU|&hIv>h`X{6~fBLRkL&*tG-oz0$MuvOk$>=a2$=fg8 zNjU8hr)Ghy)LvR^!*2~VZhvt-pKYLf>>upM(jC*-WRd|}xy>ozZFcM!PO;H^M}?GzsMEdyp{HezF*n&L#N&`M&CK2 zb;V~V0Y{by7l{h*oOfE>rUW?VElJ##mi;}MRgCTZ3m0D8lUr(*es{LbE*}*C^@IoO zk6uaYpzh4>Tt{mZ8$WP8D2z5uYsYCUaTm%B;oJ0GA9L;Kqh=iN-td+@)2S14>}-Q3*f2ds&248Q{Qafe;XY@{ zmjBJC`wX<4S3Y-0uBq2VKk6hDF!~(G-Y%4SO)sqakPURRnvU9=?WpBt{u>~Fd}}@z zb%ObfvQc}56=jvoTi-R9zl+`4z`O0%ax)$BK$dPG+{%3;ei-Fk3)cO6FL#tEtKVh zR+iQkz);X5xP&bm;sW4j+n38GNhv!%UhuiG&Hk|P3+uv`+#IDF!F8B>j@bseo64_B z+C(_w9;g<=vgTi;X&faYnxi+36)pXaPfsP=Dx-Z>c zqbHpDBrXm1MYNS#dYY@pgD2AlBbWjYbaPp30M_^6cWb}5(;Ek(Sh0Bz{PZC3m800x zY8yAD0xbhhgLDT2Nhs-N-8CU~LY4OJ6;@rPZKm(*vRCs51B8% z?Ht=x+?Xvw_KB~tpj?Z8?FtFGJ9Lw<#@wdEAq3ZF>&tpY7R{cDtP@Y|jfpJ;{2H3j z3em|s-A1ImDTs+rwJob>#b5ddm&&3VEo@G@@@zbG{5c%2)FPN{A#AR_wu9D8Up4N+Z!cqxE@|m$v#Oks_k#V79Kj_Jwx5CXA*C| zUkF>Zht_gXW?M7fo*|30PmmeB`O~mzqmZ#A6ZvOxNLX}%?lVtx?qPH;S^V|0+6KTi zEAQBOCM~WQ)ia7a_i#bIg16v^409O>B!f?X>mlG$q>KgUX3|kPwVFRw@M3)s3~sTq zI7emL8E?Qc&=21vrP90mAnK_vyG@GmH(<1v`0@b@<>8vs?&-+>oBUtx43R(lxX2k2 zU6j^cb9PA`&HNbXsYD(G+~~FVq!N4dQEk%$ZBQXKUgqaK#=ABd6OROvHtq9cQ#;lJ zwc$uL&$MTmeYw5g0@w_~c4ioMy}s|El5#DNYtvR`cTKbTVy?_Oz=0^pFzJjAW!|d( zjm@0Ulv>sCjHHO{N9CeDE|j`0kN8tO zChdFa?Oq)D@~UY@cG`;(mNklL%65IV#7eT393`Xi+|!FAmK+CZiDPq9ciQ|0vO6|= zUd$Pl1s8QL?f*{@Cj%(HLppgU>C*n+)JmT!jsI!5-gCL<{HE3*>1xhS{%*RJU7lc$ zI?5)v(fsEDeDBrg^;Dh;@J1?64jAwM5+&fcRG$AK3Mk!WL^k4ms_36MRqt^MFP&xg zLK3E0tulbxJ884u3teNaGEBAUQf5twwnr^j{y$;8Qno$%-^4g6T2Sp? z#WMBtC&T<|3b!au?<-RHzt@G+&!=-{8w)JHPiOM(W3|nso06(iQ|5AMj&`4FOHv-c z!yZ2t8u3f%jF0R$9Y42Eu}w$rH;UA7%K82o!(LyEu;D0?&tfv(w>Jl*rFP9=6q>Uk zV)$UOzfi9|?v{-PazEL#yVKhbj=Eka=mo)eDhhVF>(xoKnxjfr{M|IuZiu1V*r4zW zylCdN41)AZg9dIosMAHFwcBv7pS4|W^2u9H&4+#-)CH*~mb=~+8{4FfFmGTwsWoZ& zSl1uX9aCeIG=lp?I4VN@P9cv34%^{cuGG#dA4& z!Y}m;lgWt+bng21k=K7_+?Wje5#6W^ixAyrt6MIWj=s2XN(#&^ID*0ag%Jzf0#8N) zYmqT6^%yImNoN9QK4@h9mHyK?%5VDZXS(^+QHR%lMczNPxG1M*O(M8D@{$ys0m0Bo z`?7^do|aH^p1+6Yy+?Lx#q2d6`Eu>7b=J2VPySrm9;E8im@52P-}ZN$ZzV2u9%b%n zBAvKN(nC~9iX#Z_hYMa1-6Z%%;QqD1zr4U9fELLo_!Ru%-vzG$6IX{cPK->%mD%70 zgGt4svm+%*+d^lJ(I*v75m*Qv=sTI=(N81ewm<1ePx-jy)+?9}s;m=^hV( zw|}KTWX|Ww(X?9exq(S0$Vqt-nkIO_UKV>vyar?J6Dd^p4U|=Li9~4Gkh9Q1V+>|% z%o2z$tucZ1mJ1b>{OvJ@T5yd0c@!;kTTP=Fw%Q%`=Y1x<_2#b^t3B+-MDF}oe)5PP z)k|^i#-i=#2i(w~f8O94qgFS}b4Z{Sh?iLhzSXX=cD0g)SVG*Cp&IJ0$?EELJaspv zZBj!gtq=O`xxP=DIxmys{d8IB&K)_be+-XZ53XFvl|2kJ3mP??nSU9SagQqHir^BT;>y_(=s2+=`z_ySgbX z6h~jxF!c4~lfc!!sF7riKFT)R#FvSs&P$YFqGa`tE$;o#f|p-UUY_#U7Ri{K!kAG0*Ixw!${+#m_rXmt*s|Na8lD|bnZ z-m*JZ{gAQsr16PyxitH|aC3BdaYB<2d>C5pN770dC5ZNA0CG#GKPqxTqCb295(2^^ zTSIv+2v&xcKmsSQj8@sKvnd^`)#0ygbZ;`UYgQU7?vv0UhWMui79~k5tlT_&R|N3! z6LiWui)KD~W6!I~^X2SJM5*Fa~6Z-;3c&PuDHwzZ$ z6{dJm=@mSF&L=!hcuQrlKI|1#dCt{7#&i2=b$uzlyW@TF6BH$RVd>N~%z zCRklRZq6*e^&@ad=ZkjVz|whCM1s3ndPEZ1d4x-x1+8+kZ0zN)%-5v6v8H>>-uab+ ztgO-^d#s42*%+~1=eZa$Nv6dZacf`1B!`C2!&>Q1hEcyL3&(%|qE$Gcmhx%ZEZB>N z!z^(mG1@H3i^_RR^-fMW349(jlEiA}=S7RYWux_Jb)exqgDJN6w|L@^)1AJ?0QPSc zpLq52xU$47v&gap*Wdcw1Ck3gX=dTJ33k8r+y)dDXgdb%7pV8lDi7nHoW5cWedJ@3 z+AJoE+i((77^3H6?ieEGW8B$n)%_Lj^qplGzlmXPvw8Vfnv<-(Y}n%`ccv;PtTig# zbXg~>iXL{|@B?S+s4Y6asI2v(CPeo&KSAm7u!i)_-qye*h}l{7c?ol9)wvFGnKUF@ zeSpChzdB62w`^S_UcH;aD&co-`IH7Rq)dH`q4GH|!{dAg^en2Z(*8n1ycfw(u~LN; zKSN4bE?k8XALOJ&!SGe07%3qqY=wB~*d@r`!zQ@IoeZ9O%?yOBA2bJZB|-)kcX**o z_7H(cSNy6^St0_?*x*+JuxFxu-ozPxk@G7#MSOfC(luU1Fxw+JkD+5Ea2RM{h<*54 zz~)Uw^@elRrnhcWY+r$j9dp$rT-lOXeFT~i_}eD*<#`jd2q?}cyB zGY5U>@B^;vG?Hl+pBMx|93v&+NFDI(CwUD~kGubpU>^1dfB+``8z5Xo8&d}?Rv2w0 zcEuWIYY;sM^#m6;PGaw~kl6==^XbNqzx+P~@K$hV9n z9a2Uv6<04Nfo7+wrR`6|y)V+IifLBGy~@tKB)gg_HpVP3xT=lep4M1Y5ZEJJ{PwU5 zN1jXV5}`0b->ks1$NePfz7RfL&4cRrk!46(Hw@ySUeV{;1kbZ=zU;5#NNPWxaf^Ih zS%1q#Ft}S^mz9^;wgtlJ0-iD@MJ_f#(dAoFGd5$|MLc&t-3gr6`7O ziRL)*R=Y=a0%fs#nhmqiM&k_UmDgt@6%OV*4c#XC7nwa0z-lP$RYKgaS@o_x7zon= zkY$wZ1Q4jt9rluW+1ULXV;x`;{6>Kt{uvj{2 zM>Q(CFdLES9s02llqb;J*b&XUM|Pv(lksbe+%S66lSwZOg(Er<0PoOEl$oMvtKKK} zIi;?tbz7%y&HPyeXgX&E&%cq{Ao3zzofO>BBMaGZL}(8=*+5Ob^7!7p;dh?QquaDM+)Cg z$}_r0;<~*-t&truBsjR| zRKU@?FDjpgD{=`Ne!;q$zl^dKgX~rbm3m>RT842k z0kpui){lFVU{O-QlJKaotu_m5%7|a4e~%`kkklnyWfuz()!7YeD?L4}hZlQi%ZqFb zCp`<@frfCiTQ@@1fir3Cw{Fz1Gjhd1LgN?B9TMF*8)qT=0r^`r`=QY$+J^1=<){rl zKLIC;TozovePR!I{`|YR4MzPT=*#Zax1V z-|eq16|WwboK<8@DokF~0~9A%gg(-H;6Bbc2SY;23h!)4ufX426zTS#nKTva2**n( zyk|BxRj^e>g}$R(SNfRnMp6qfzUM~@w3B;H`JkOLtD$JqQlH6Pp0*gq0hW_&wrj8| z3bD@1aq9EoFgd`l=QMf1NZ@driy)Vtc9vNx-xE*GG%4Kq8FE9QXp>e{*Fz00=Ts>m zI&KkEOPi8*?T@fKm9BePiNG*FG$bKbASG5P+~c$Ni1GkYa%(&HT_;!9Sry-;X1ZE! z@0yNx9jWf@1ag{Wi~?GTojsb9GqFl$oIbhg^Bq`a$U^<{C;gH$VlF634NmsHM)+*j zh8z@le)+k;)s$Yi6WoTK9BLv(V2SiTr~0RNwDHV39r5r}>-~V=J`TC>ZSd=};-O2EW5mfi zZzCI#M$CXHw;JM2od^;7Yo34BTiQ8~yF{`)3oT{UaQ1{WwKby@IMn$;h02K(A*n^` zJ=3Lo3v>n*n+}}wTg7(cjV=y-m~Q=BD;1wW+T?!|D+eJ*phv_FO@b1kw>zqzu!c-D zKYbn=%Xf@8AAeKks4YTKEL5hvXP&ZueXV)$W#IBP%g=eqbzm`_Mj~whV?a$;~>hPnC=rOx0#kfN(?K`CWdK1h%MObCJGG z!ncdm&!bw_sr&0@s`OPl9*gnav!_!t!upAebj_+>gO|+iI{K+NTvNK{6bA9Oh5O%A zzXv7D5n+$ zo3Gv7o3F*UbLVd${_2yZrt0;j0QGWf4Od7o{>7Y=RYR#1|I4=lX^Xwo3SaCKKf%^4 z#jpA}XZ}>UtY=EPSObnfr)&q%hpaTRD28;1%P^F>nvCub`S=wHcd7Jphh2gyrnty& zd?rZb$X5;vGj#l-_i3b0D%LKty$m$hu0FIa0M#Q#vM)f4lscIh{3a1=<_AkN+^$>L zrBun*a2j{s8YKt5U!f!AKD{sPVdvSdO9!ec>Z_&Rlpp?H5w)!RobIHgGNO^2d1GFu zeKGA(d1A?=LzxEY->uZTP2+{1g48z6V7(9cq9n0#(Kq3bQjGa#f(vO%Y3n`QN6vZJ zA?F0lmU9_KHnR##pBaZKUA^PR@+ZW^a?O-IFZQlxuSBu zAI4jc5oPvpT)QRrc4NNccOQxVvs)#j`#aP|!QUj>Ew5oH#g8)$ay;doGl`a*e~Jzh zkuwvKX)IUnmj&o@7+m8$mhMv#drMf83;CAu6_iVxw_mwt*Y~O|%6`+NU{cRbf&kKL zZvs+**qS4Bl4?D|3Luk_w<0;elF8q;K9$5T)YBAmU9ao&mTOf%-R&9LvwN1|l644C zOZvUWNwuqwS3RZ9&VN~aPc!8ebw8szS3EGxD%3UOYVy=swmdHwKU&gC52wZK2h5 zdGS7mj@L5{h}(>jH zf2pYdx=S_#BVAUB_S#>=&pD^O&D`b^Ee8>4v}K0(JheqdCS>koG$s{m?}bC3+tlY@ zj!0}Szd-?cl%;(nPfA>~yQnjO$VfFt&VlEb;W~L)>K||Lt`;4E%ooUd98V?}qZ#EJ zU*39iSr0%a{mOe=WF}Wz8Gp+07rH^QpR1oi}y=~eknvT z!}+yR;#pHJQ2uuTmZ zDiwLJ;MEBU@ZK-kUyZNIxgUMoszP2=tS_E6E+$;7%JCUF7DGXs=Bmje zf4Pkd-8!nIy!=R1p_E)rprk(M`g-x9sky?<`{+}}W$|AXouvv&caK`nK*&M9DfU<< zkg(n}{3%hF%TuL7r$U&A+MP{vb%=S0*-5iQu4$)fEJ4?`qTO87Uis+WO~)3qF3#zy z?qIQvcCNnGvwb_+EhlJs=jgQ{(R^%uAhTUoMZvRAlf6oKkhhF=TnoXm3>sz~=COi@ zyXw&=I#~j%$ZWAs=qn7VZdL87c`}NVZ^8YA=B~5mQKT|kgJ%X_c#yTa^`@|fp_tJc zeeD`TH(1HCO8BOwj;D^IO2E<*_lIxxO;9m=NdcN(Ct95?mI1xa=k;avri3Qa1=fVT z0@%8oHiLVti4Kdrte!Ugo-tqhhlcbGXj5I2tJ4m`_hG$uZJDz8C+!NBq#7uT-iL7- zMOwuQ7a)M8?vsVhmIBqd%KFG{pENm?XzwIevqV2qqE6OCIcq3km^2Uer|;MNOopd- zBZr@ta^B<)?beli-sGARGpW=WjOn9L zhy3tx6Px9W7ZLcB*4|_ixpSMlCaNH`nD9fic%gCa~v&FNe;38|s&DyPI&c;Z` zB;LqQE!?>6gi7zr(J&N$>lAa~5JFJ)Qfg$(Iw$rh zJhOv9pVPc2OYcfN$BL@y2N6ZQ#c9M^MzTj~@L%dAhbdIOUH&@{tF*U$69r^KqWghI zdeJqwS@fgQZwtohz&|&R^wM?K(76E9b)ewQI*=~)+T_yEw-%49K)xPfm&k9Ahq(6) zPM`U4R7)uqZ4A_nHmcOiODw8o%On3=XfXaC9eGhTdmG_ocq#cq-9zrMvqWqaQ=BCi zV~&sjzTTo|62zVsEYAUMyQGJ*n8iqYXEJ*7O|3>oR}$C--)i&;$R05+m7j(#f-K_P zD5BL~j~Kl4?*oSA_t}4RL$B8P`B&_az_@{}N*gV8QPOHsvF?K3CgmOd76mZqTQ2pP z)oUmUMbcO-Nh!xjC_`qsOjDOSSmm)Nt|mSy#j_rB~Kh zO8Kj>gt8O@b#Z03ifUHS8^7QwR^R{XTIuoCTR})%yNp8lrN^1Im=H?8f0NGv9nQz! z$IgFI@}{*4BieyO!G*u}mFZy>qQz zkyTW4-yOV%bIr;u+9~P3V!sEdPxS8@8)gIlyrN6t%oAjfGR+O>zN5ZpWdOyA>pA}(nm%D=c;@cW;ZZIfxW&W!iV2v_BQt(> zeSSSF1u|(1y_W`t@kp_r`5X%sMPJN%vhK#U8h}Z!=vTt^^A#`juk`0uFS$L_uC!KG zy~;bbG#~2j5kz(x<_Pb}pEB<~pB;P~+lMIkqGvQ8@}4H|Nqm96K<{#qG0Gy+$M4Uk zPg&2LzRk}uBEwnbl{eTI9ofLsq}%>ofhUtE-|3ev%9E@z&4;49)fw#jhWm!StULEx z_g$%{mPhq__80pXd%e@STboO~XYc0#kBpAlmlB^v-x*&uk&TY+jzZrx;5pA;?o0hk z)N?^c?`m$=MZ>+$)x~qjbI84~gOOq-wv(vE6U5L#smPa94&D49DIll|=f2L+u}VA* zmJB0$GIy{&HG7(aHNtqG+^RSMrK@P|xLy(+q+VRNrBMMuq}7SE01kiPK66)?VUUjq zFCDMc3o|b(uObkW1%qXZ5-AB;WG1pJp(`PiQkPtJ16lOj1WX;3F@Ur+6;=q-s5Ehq zatN8@0^lzn9lb9@n*|B^KLk9qWp+__<>^<#LSbmYq--L=gDbR5?97{c9Vx5HV?QsQ$jO(;S zt#IvoT6f(0+}wC}yaYZQXvog+8uvUKQr#i|Fe(tEfAs$}L+tv%Ms$dLjq>n=-H<66 zbK4)WlO2Xsg^`Xq2ESTZ`M`dM2_uLC()ki8b?BpJA}I#Ub!GX%1AoZFSYgbk+@KJP zRb=-0Wx(a49~sdK5!lrbi3n}K>oZ%br>8$XnIiIZrwbe62-3h2yT+Lhd>9Pz^rLbu z0o(Q!o!_9LjLGDVmM_X3(>dR!bC;hX1uxMEYKSB7BQJs%mKO!v?;Ao#AUmM@jIgU3 zHVmV25aG|vzBatn9UnL%J9q8^!@N%I9N=%UZYbz{5d_gz>})6E_8y92Uj*(PEq7?n zIR+|`u_tgTCrH5=B$SuUaM~M_Bqw}PJUqg?9B!vTW{wI^TPXb+B$zu!fthyKc?`f z>Yoab8K9O$qWX&(vdyDLAU{m~cD&4bh~e;=yDc-vh$aZTHN>H0TKAQBto-?cf8})6 zMI{WF(NsF_hq;};*_o}R+yRmID+XMpcXZwzkiDcV{{kWaot`SwJ9^KSHX|*SxL&I! zM$e_SvJyo_N5a((2FR}X=$dHk94SWW-fH5kGOTM#JPyHLMd|KFxUlfg)%Q<5y}+>N zm~|crN{V$>i5H@?q}&(aI%{7C`Z6{wup_}o9}&BA)bMlXIHDI@b~~I12F%HU^Oo?( z3-Yc0j}v4Ac#!O=d=*0HG)&BJCtcGY(#4exUxwts13-R8-|YJPqgwvQ3yLD#E#4!p zAi6#rX{RJy5#nZ9X?GubHE2a5wKbe`b{$M5A0us2p4q<-F3TJjPZf;m^Y}`^xKr(v zDQ&`>(LV(yv^r;*+N^epkyfSYeqZgfB0*%cB{(~3@;5h}O@h!U48RlZ8ZMqJI7zE$ zI@nUI;-Xxzn=2Dj9J{I@K8U9r#*+J9PrN#D+~m&qQ|@NyXYSPZp`XB1-!nhQy5r>U zQb|ur{G!64Imwlpl!&HUQBt!BCCec}aNqwbG_siHg)vV!vbkc*Mo9Js3o#2(%6~k7 zRmq&aYFQw}S#nT3^Va|#E?A68@eLjN&={)E8`!UATwUY}lsSLW&(741Xv-M(@c(ou zj$l|0bdBg`2sb~tV018H2N4jQAkwUVmVZdscqjGD3lS)N0|A6khN$@3)Aw~gu{zI+ z6Z!Uxeol4AI+#W4OHqbO3Wp>8gkp=-fyk<|O?$o?C$-5M*CFs|!CdZ&f%s4!%gJASycHcWiPEbx4BIb(4&%>PYvOX!v z4uRdSy3B0C{xh}>;-{>J#J6t^$29hp1wDQ!PC>}z!TVI^K?M|j*JOyNpo6_&fbmc2;FTVa$}UgL z6`NE2fE$w5x$Dotx5h#PFd>w2cEIdA3k=7;S8>++P6wkc{7B2T(wN!M6aQN)`yW!$ z-f|60L2m0SDF+sRXPoqKm~KAS7D-vK`>Xy8?-dTyL0YA{CFp8+(=Kt!><`5G4@mm& zu(Qp8FTydR+ep_N_%Vrpw=omw9J8Uj$xFyLCK2_&)vl&D|FH$q4po2CfFp}jdN|n* z)tc^)zR6n2>1H8mULrm$$*6#s4a5AFxQ(UM_W3h*C$9)HO6vapH&~JmZ2kc_WDzjK zTJmR!DBqY3gUwq)zGDdoR)(`kuy?Qpj$k-U3u&ARM9@|9CR5^+$^XAW17}Q5g5hN2 zRQ>;H)p*muR4E3+?}oh74?1NC)cn}`c1``wzaYlpf_E%Ieow&;;K6W4p?O#r3N|;W z>gu0s?SUD9nx;}&?aj&sDp9^(>Kla-I50M1vLW=yV(MF;sstpFw9a@GcYVX8us&4^ z2q6)if#slJbAetV`N41KhUB2PH2PNQnj||3ec*yFMofLHQ<(rwlGZ6$0!k(47-Qsx z%tq@JZ>5~j1TQw{7-Hl_=gdLG1^-6t7;h!VB|@-%=L9;D-~`MM1)CF;-GwM$LRgi_ zgDi&_+E2XW82s}!%MWqu7-#tHfLJY$_(K^XN$UhG2!-_@KSjsXw>%XK_-|0;h1f>x z1TQP+*ne2x9brzcygQ2Ak8uA3l9G!EuU3mFFxDoT?k39YJ!J~EU@s4~l8|FG*2eL? zK5*1LtL`-;)!en0)50EIOiwT(EGeMn`(0N@qpIS`(Nup|Xyrf~%*d`Ax+a66Y3wD^ zcVlAKvHSP`_b=e>KNm|7GZX&xM+NX-BtNgv)f-;7+;VgmmVqwt57 zAxg3ox~Jbtwg?fN8zN%YvEe%+gkXQ8Y^<38p)O%Vf4hchakH7*M=9%#I7$Cx>34W& z##ExJM>Hd>J{Wx+o5{~%CLpQ)!-}ZMTHM6GD$d{ncBWBlCNraSBS0&8a4#EEg&`or zWHyslvNuYZJt7SHHKhdi+ZD5YpUj~NmWiqPJ<2V5RgJPh;sOSx(+};GBBJfjeR<~F zl)+6QF~oH2y;4rwq<#Hj!px^T0YN|JkCzONVB{C*J0~@xn(ywF zQflZ(NY`pLPB?i|cZin0MDy5F08J^zKTn&e4UDi%GM#%|l8u~-59{Yy@|y-*B(D*h zgJPyVK5J+7@LjN(d*Cc$ryNNDKA83;0oLAFgpLc?%o|)1IQxdy3{}$(2k!B}ZJ&O0 zzWmh${$C~Cc{o(x{{Zm5ZAz&RZI+ObG=`KVhE$dr#AIT|SYu=xV?x%kR8nL~L-yq( zjAcx+%*Zy<*Vd5T$k>u~7!-pVhVSk7d!G9`?|aU9p7YoJe z>jQnN$;wv#PaIWkTB%XQZk6teP8m;gvXQ#V8pfWV7L{cyoF523TGgZgH{%jyMuoK`u%ByRr$)DEezIuR|pOLwIHV3CMh+2;&nwL^dL9-iIi|}e*1T&Hv)tkbwhh)F9vli3pY199(eb0tLX`BMb%+F`p3lC zCvg+ZyA4%zHR|at#9{IFThpTM&!4_blxE4DRVD_B3a--+?y8?U2NwuRf%nW#vVY9% za~eOIU_TkatZh|YVk7KKr%|OI(`wnC8bcQqQFT}S(1(Hdn!6uuYtxD(l*-|K#B780`rtj8qTW1?7NI=U6SyHQy;cGHb&EbLqIL^Zz>DR^%=j34f_$#9 z>mT?T*O3rbCP^?6h_Ulz!lC&{6XPG2BI+O0cfEUHzl7^R>@xW<92`}Z0UNYasSr1f zi<37Z{Q|04eX7rBX;rX09!l{ti}8`Zarm0~C0eV;{e=ZOz?wco_-AvVB9@f!cAx?B>jo3z*0OoD~usThvt*eW?dCmTp2SJ%x? z&}PI(#jXTeKx|`)702fLqAB27KKcPkQa0H7M^7+$QtQc3(FCYEEZl_I2)t>+$NR z&lT4$J~~uycO{Y0p65x;eA~MM>ai^^cpJQwOe?Vzi}^2;eGDC3MehyqXVn#8zerd3 zf0=kl?zF1Snb_loT@1rw^f7d~qr&8pjZVdl@t!-ZuEzK!TT~ShIqrL3*DF^K440CX` z#&BhC03KLiK1#YdD!6sO+)Ss2O2Ci4QF}`T!*~_42G?GwuK+*VobRF0YxhHg5o@ja z-!~6+OU#7$7By#U!pNMVH9N3Yl3Sox!DVP1Z4gyGm+m|lYiq4KTpHbxu3`_ac5f$a z($q#m>DxX~BgkiE`Ro@XD$ml>IU4?~JN8pW?R)Td1Fs=&ELFb>#?5!Bues&b7Bo;6+ckt6c6c34ro%EsEE zXnrpLsVRHTRHEPIHKcA3ZNpSg3h8_0t^8JV^o5$@u#`My;SiCj@H6PUDfCbg$mRH&7kcz4=If`ILT; zZ(4HM*y7+3bz*}-RGJZ$R-FJqzN?{BLIhc~bs*K`XL`jT1YkKJ@o zUXs%@y3=p4DlTG?r>7JBlIV?jY)M!jC zJvDaUHtb;WIiq-yP226evJdk+^t>~Be+e{y7U9|KxoSjQ0>c00W zVSULlYjjZ?yna1r5sZT+>B6BQ%LVe1!B?W@{wO6AE<%_lQ+Xvyv>Jb2$FF;W^;^N1 zSGOh{l=~zhE*4Q%h{0YGskqf)=j&?yhRnApad@Xc;HiLXRiM~2#DYK=D{{CU?~QiqZo zybYoutq2|ApH^7<_A1P`JQD&+{bG{Cpk#1IkQl$A9ZBcfF!>*M^>g@rIPJ|eI}?J| zv9BXWO}kroNNunqukpVL*#fn-Wa@1_qhI=I>RYK66z3J4YP8VEV5lpV;8B)Uu&eB! z$m%a5R@WyPXay6K$3@uvmb>&m6n*e`ipwlc-<(j0augG_P?)7w@eNih5?RCUEw8KHw`? zwL0WyC)6A#uBFs)zS?g344+68?{qv+CVhTkrOOYWwc2lOyxk!&hv<N;3J}T( zdnlB{t~}ghhmM@z2XF`600aRJq9ff(%9{P&l{Hu0Wq#K}0}Pqp|6~AtJ0R8pa~a;{ zFG~kD9_iZI7eW_=7d`FEmxk0ss--CJ-%^9cxh}f!@zT@<`9+0Fjh--oKA?wd%#+)Q z-Ywm&3$bL*p%t*}Iwd{*^TkCjRyl55d7j9IDgQuNVF(gA8+;o%JMwS&ux6-{@i3Pb+*lV8@dUg}UjiCkrac?ZUAk&|o{@+#*NKp8OQzs6gX zsbqH30e8_@H(D9>`s8+PjfJ%0($aaAr6r|x4T3FKv69|h0uTcTq8;az0CxZv0R&*I z6|nU%EZfv2EW7hB;2s1-=`TEOy_5}qZ{mjk3IN&3)81n6w6Vb4_6}i3ofi(QAOmm% zR>%inb|JeEo(~#bbOE%aGo{Ik50P{oVt4YaVZ#bIgj!sBl8^MU^XbjE*a literal 0 HcmV?d00001 diff --git a/static/css/TTHoves/TTHoves-DemiBold.woff2 b/static/css/TTHoves/TTHoves-DemiBold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ca31b228213be2019a26efdbd801cc9f51f412e4 GIT binary patch literal 45188 zcmZU(Q?M{htR=c_+qP}nwr$(CZSQZ}wr$(C?fK7~mpgUy(p`D!TItGK>2%!X#h3s9 z0sgc45CHi97N8!F|Mmp`>-!)7|7-kWVk)vYSV4T)f!9Vj2M%Bc08wO!5Kv*eKA|bR z(5Wy0fNVe{z?CQ<1fWTH;8%d!#WG1SC!@Dz4CKL5RYgQQVGc@Vvw5gzvO18;qqUUA z@X9OJE|z>&2h2PK*+*2gJ({6n+fBApzkmPN_L^i&Xp*U~lhV)>1Q9+6<(}WN5t2?* zFp!m1WaW}dnsfgxPf_Ndq$FwJ)8}OsD(=QidN$z<+;Yu0bq)+7wuTHt;|*~> zzX{SMUA7I1v4W`e(P#LHpSN_$=T%H!ERIl4c|Gb}Cp zPz<0ZbLAir?t?%-+yeIvZ>P&rofFDgmTI12$7!;1h(W@0uGYH7}ntin@Rz3Rg3f`XIiHa1ee&YRHrE1<~yV;#W| zkR=3U)Z8F~XBiJua}*&o2-S-$Y>6Y!B$PL#_CZ8jEg0b@(M)awFxJ^K3lWK0!R9?L zNLtdtdl7yI>MGMyb@!i!%Gyaz*bPHRjJQUS!;L!PpDX?Cc$vBIRMy}(5KSChWFRuzuaaSx@l*xDRYqcx8ee&*VwPDn+; ztT-q!4loEsEHD^h@+SZ!832;R03?ZV1^pq!cSP-zSUwCzwH_2q#bFVuR}zO@u~OxM zs_odiqE@|9wW;hCx_hDe^6^IpYHwnzY{*uo>YmXR|IQBxQJydnM)|6rTn!8Y0LHY#)fCtfnewWK zr<9ikK-kaE|8|{c8)kPGC8_X^<>mJff2n~fBH>Oh$JnuQ3rI5$w)zt-STS2jlS2Ev zLjO*}Q^r9I4Fk81^PW0nB=hxWdI$$iL}f&s&a9rSP8DJkt1Sckho+leG3n~$c3t-| zx}Fcjq+p!Fl(Vn6^&1`>1w5fYO8(Osb-sKanbaCC5QOgfMWhq>=-keOgC`dj9na3q z*!uI~l*PaFC^MCGQr&c(5&^1hojAukNvlnZ4Qzq137KsWI89s;k`JHW8y{pGrnvmR zFo>Llm@5D}2;Yl9uW{Cg56sR;@Ur7?v>+X>np6VN4?wgv%eX_elW@odj3Lxf`}bF> zR{B>ACS2g{mgc(db~4_=CQ*GNCHPMUs;E-?@91b{$Q%(gLAU^mWgnwxAd}5PN>&I= zm7q(|ZO$usvfYL$%T;gH{85nRWVDP;1wk7bJDYSAtRjg9L9m6YBsY&DKg|>Hi3u92 zp1mD2C--WjV{zEx`F9-ZHa=HvZ}-DDI9ov32p|9!!SkorQtC*1LaM498H1zi+$dt! z|95viNzI9xvZhdE%%r%h003wFY0$yr{jXw)*E9V`V~JR$hw#UctP;|?iwh3P6amQ) zG3lCvp+Pu$sj>=B~DOG=QLl zb@#;F#!nD~^Gw{^nJs$}XR(AO^l5tLw%B)8(MIu5exv6KQ$1yUjgnsJr$97CV?O?tPn(HiJ zXm7S;MNuFPS9#un6hIT*K?CskP)r1Qaeb{~YaMm1R(Pyp-hbyAZC3`|Gnqbu&9|M# zYES?uF+`XQr#_u!1p4qJeA~2+g|Ox34kWzj0%CFEP|{PB>SiMSv#S;e5E3X}DS13l zR`MtK{k*<}h>5Mw!p@lIj(ca#)>{xU-Xb0XF)LQRL4adeL${`BV;zC*wZD zz-abb|5(R+Y9QsC?f@}}Q3ysTVgSOu0b|K14Xqh|RCq-0H~yqKd|e_IqXFn7Bf`(U zCKI+tXOXqWQwbHt1sz?sH*;G5Z*)liO z78Iht48DFHu>TBy%@x?^ZsWi_LjN80l>IlH0>=Q6RnRI8 zuNqEq{K~a|*BtSfT6C~?L~Fcl6XeU!n}FYx{<`cY_&X-Ke$NT(ksHatpBVB{f1_U! z%e}BX+S_3{91F#-d}smVtuP}EG)_~qFhiFV=ZOn`g8Rj|8TAJi%atzz68KxcmL#i&1Fo`rj%spLmi>w5!d6w; z){Te3z;*uaH`n+x1hr9?xkN9yy+pAPBatyOJWM~Mz~K!_%$_bL-i85h>M~Drhvve= zqVo!aP6rXaVdirFBulNW|HV{AAY9P7#LBu@MSBpC$Ll6KS6eOC2lH5Qi zuG9sMNfAYsMc@RMf9C)Q2$FP=DKp=|co78sd)X1}lW@!zSkgW-rkMHe17M=r0V7!D zDuJ`TPPklQ84Y&M0AY(z0{92UdXOB#_W#s}vdT~gf^HTiCYVn0F&dDH;>D;is3Pcf zVy206Wa3cl80wxRVim*+jO){EC`XutU?vLFE@kq7p0s0Kw8#qd-)llFO~MRQo%ztEC5;Kgy!wrZ9dM4V37W zax=j%gcuAFaE7I3g&pXMu{OdRCnmj6-TJo+WI@d`bt|1=6qVRYL4)I zA~G?A*hB(okqDL`RuQq>t~nhj>#q=nj@b}yHl2?#D(36(1R8~_Eu^esoq@E^l)NC) zHdVCF4uu#KS0Ad0we)-k^{`9PtQGQfRfg~%yR+OdYL`OleD$45dTx&@0$Z&ORL`kE}K3p?(ys)=|gA& zhln!`(b+B@u+@ocDmPW#spKukdZCI0XZrOIFZb6nED%ueO5`jG6()sQ>p%+5EguC0 z!gvi9pfT<3bFY_yQINM-0F>ZA`#SMC)Xc`y)iwXc%S~X?MUzlqDX`GiAh^IqkrPRf zbh^QL$<2oSr(k$^WO}`&e$P#yUQUWcdHE7;Bay|$`cS&Uix`+`L+ggAGP0jj3!Hxg zx(u^3wokjB77Ot4G+!4Ds(s&9!R$al_~ddQMFkK7L+>{g`4C@Sl1Sj4vRH6#=}`{l z;4T`8ylu6OdI~O`>5ZMs#3w(+>6e|0*bYseoAWK*>s<@#H9MDOu{*O{dh0?stlI5O zr_qiZN{$6-(xEMSWR-qXcX2!OTjmKrE}fd6;1HlLA&60 z=xPPsB`wB(lD5tigI>Lp{srfe-VxhVUf0`B`x>=PlHts+o9`~{?olpNxp=XnF0QO( zT$OuC<34Hk9$}?&)yQfC-`~R$5wRWj8x9SNMl~;#E3%{UevSfG%R8K6_`(U9HMxwx zMdW_-f~t4!@fhs}l~KKRrB}Xzhjh|D*855C3cr@oYrX34HQ)LBZMLs{zsC{@^p_8Y z$cwNvAb|nXP?~wBE%!g_Tr^BRos5vtE91m>i)jK;t&o)K`4;`YCugX&YVrxND! zj6Q1t&)CuC;BN)&`+Mec!3ITTfpB>xi2_5>E0;e$GC9iuL3?(cOjTTxhLPkMpI*|; z0q4E(-gz^2=j;OW^}P?0Y+}8!KKTLr0h=`XdjUXLK#+j{lHcmXJ=p8rC;i=nM{7>zf7o}VdW%v?Ef`UYHAGdtBT11okR(ZwbuhWe?w!Z(eoB#h zc0%i{kI>yDm_JX~UTf;EtnkjYfNI5?wiZ-0t@dB*}umJAaVqW z-9vb~#MCbE#_h7O-?X7`Nn^Fz!#JwZIi;Q*=PN+6u*;)eQR$IZDNa;S9$53|96-5y z3}W}*i&HHoM!MmhIO?4a4HKaK&1bdB%&N%=$CuoS-)`#43I{W>wK8QB@VYbOE{$BF>(=# zKfgr_7=sZ9#;kVYrcP=*nU~nZU^1IdB_f}tQtxjs-pTlbK%kJkOGaD+Wd?{yCY4BN zpJe)gLZMQs6{%!8p;E0>uI;z+|9;jfCY#Y#sckk5aWz3x?07Z0ucrT)_7@Nii8;lH z09!Azz^NACn>W} z$w?1IO`Vm%4^-HcF}Py2OPd&I^mBzKm2S0+TAXpBw9gMHMZ|h+A<-t^G1F~5KdoP3 z8>0J|l+n~e5RPDoZ1+(5hlr3Ep9yjo&`L$!2hBH?O&gik{Ly$a{=sSv`yb<1T+RXj zLnNtNeifiMIB0H%J+ zcL_OQa0WZcQsxv==zbeO4RHybAvCS2^1vgHW_kUjjACn`|D;F&f)JoNpoTuSY+so> z+g6eGWyFOnoSfn;v?5lQ^Hy>^^#RGjAJB#f63|9H7e5ELV5tCR@zOB3CCfp%``0$N znkSuppVaA+X^aULyh5hs>ByoGU*!0 z%FGR8HKBi}+0V>U4 zVOZKA1`OQk>K>2v8F=y+n4=rgQH?Fb3y&~)mpSg+^4!E2@d6UbL`u`ake%P8N|kb* zrnyUIz1hwv`GMwPJy~T#0)0Hvaw1vmI|KreOb7+znU3p>D?8GlC?p>&rS(5K;XXj& z(E>x+3>H%^a}e_dB&12YRM%z;dr5r^wnj5#H&)MBJg@FI6HEOk?2>D*lNcL1?-}(3 z9Z{__zS5U#EP`;}Wx#Pl;HJDwhHF$cQFX>&Z4KGLF6mX>W}HYOmo~L9k!wAw`0w0m za;LvHrdAS0-q5dL4?17(M>u~%P(N0xMOWIXBP%1aU#%#%W(-Etfiz%q26&zuo=t>B z_EgXhMilIf7AaQFr!?!|8y;J926!u`>XCf&v>WAu`jR0Fi&-rYNJKU<3~TCSkyEwA zPf;B1t!X2+nQ`o{NzxP57oL%Iv69mc)eSY#IjRfQ;py2{_O};;b0>1*m4H}BPsIzy zmS^=N;m4;A-8%Mi7Fc$A zhl|0CW{3jWyZS9)cQX#-RtM3O>9Is(cIdtS$QxmBGMUD|mvvc8?M5@)PVs3*bI=%X zWRN$+ecze;)~Jrro?7xR6u?%(5uPq4?~_p4&j9ioCjDiuHh)k3Hd zM^{zWN-iRO9DkM%t%muxo?!Js*WAploGxHXq-o6(Y>z6{vWgf#)x}8Qqj|;uNGW5-wzfW-&k1ggDZX1U82i1vFw;fABNF-5 zg*VS2{zx3(V>T>EB`3%T3P|`1@dzda6;P1^6Rsgbv$Vr8!nJ2qu+@=OP0o~ny?kc( z6SERCKbDimnfZ=?whSL_l0+pwILE_h({JRLRgvK1a3y^g;e^kfd+>nf{B^-msFji2 zAsCg?LOS#Qk(Zfs))xTG)5P!}wEYY7BC~n_zANE!UDY4bPX|dZ62wZvqB8n*%67kK zr4EkFxvsA0GKg+_Q(yJp{<5S{=KnDKSwE0xkZXFP-z9fRKN}db_SwhG(**(|41Jw* zH{uBM**!^fW1ApQTJ73n^%-J3WBwOGC8e$P-b&KFxkrAulW4lF+Fw-V4C7T&p#F# z<4gBAv7y&9JqJC_0H~5v&gu>1Oq1`cRvP{()AFU9yI`N!f+C*d9FjxcSSnbCiwSAe zeRUlLw~IKI=>DWR({imRT+%ZCJ?x4E{d9T!6v)QGsrq z*163x>m3<}z+VG1EEEl0@!*820s1dBsV0O@b=LwRWeU%|1^b5;X5I;50R&>v-;KaV zfF^@P^CrIbyou3)p!!MhA>EU800oMcNQ6|JPpdz0Iq2AY$RV$AwE;K=&#r*Vj#)PK z?m472M-hxEO_tDxD97v+BYdpf_-?hepaWT?@nk;;H6KptfD-WeRXJ9}3a7P5lFo6NQ*hexAxr6Kw^&ln&{>v>YR_k5V0mwAnA9y48|B}S#2 zk*`6^N}0eA(*b2)EtlkbHw7}hEw9R_bRYTdcbW`!}y`mOI zpIft;h$aV!0i2{-CAdM!EmiP@!g>L1XyJ4sjVc0hO3mlV0W-|yWakcBf4+XKUWLR2 z=ua33o+z)hku*4T6?{iX-;L8+P*mlJHznbQrDO>NAVKAHq-I$$H7)mf5?60y0GwW* z-JWYG)@eD&m7`J;@_~5uR-*rG&G7d2K(HpN;D1)!r)sca;Q|MuLGf==&wY8A;Di{ zZE6i1V38S6u4kpgS(r4}g&+a4u+aXyZBY;rO^k_zH~#b33L%CG@j+$DFw96Xq~x9% zoypNwu~6iZ_4`<^CG>3oh55`VSJkq#N~a`3K^;K5%}gb)lN{f1a|4s@RP!?uIXRBV zNez!$W&7-ToQD2(4Ikrc%0)5L7$1q%zV>8rf-YarfR+&ZEq4*d1T&=bh`rE0&F5+G zdY?U>1$Kc?Z&6|57Cq%j`xd+F+^n$NN)vmPfbL;elAFTN>sZ0OxnM#w0{A$9tboYG z&5U6t(|}Ng-7ss<@|4M?A-dUGWOnzD1bKDyiFD)RnTob}=&}LjJRWZn z`7#29S2j)pd=F{q%hmNKOj#k5=kQmfe^u6f060^@Jgjnh1=Jd0bJDo|;rS7dP=)j7 zz#pyRUA`!hxRG+YtNOZf5`gG&EOba`OyCRUkEz+>_-&t4V21=Te1k(80A!18na{=f z3w}#rXYjtB_aaLaPxe)3?(1xFVp&E!?q0zQ#|}?Po4oN`Z*y1HiAvJ9hO{MPEz5av zi+LxKpSddydd4nLIc{|NIIz0sJ@mS>)7!OYf_Yo>PR<`P2e9s4i#awiH>1@C+Uz`Pk@&V*)_Hnh^b9?yf zlx};-J=LBCtDdP}?l5mh7`Af=by|gi&()Q`ESR72%Fi!ll&@ZR!%caRZrx7h#?`)j zR5GJ_uZs*HWt4<}ESYuuvJ-1hM6T`IXLHo|e*LcvuY`AeL3l=e20nbL_hq@_MtQf} zl0F}uq%SwbD-wHSeZ?O6H(_8oK4s&K+^TTbHU+Ex&G=k=e%hV<72FPfeGj$N_hpX* zE4aYmwG}-W9gSMX9xFmHE0?nO8LJyZLta>Dbz* zrD|G#sZTl|eR_mH+kMjgL$~xo&1S-sI_$#!UmkArN3_ge9N` z5yOd6GKA@gs=2V6uAG1b5HNv4V*o5UsM>I@w8CM@W{7-@xxo3 zj#f6al3UzWzSs@<-TcqW;^NPoFA9^rt{M)B5&F&ttGn^}PKc%4PFFk$JE(WBXE%q3 z3oQQ6fRYAhds|gy-P;u=;_V8rz zhZA+lgyU_=qW)#ecVRZ$ybrMcG<p}eLD3hD7e$5rd7~1` zMdh&|Go)Tj8$n{P55l3-8>60P+6U?n`MHb@|G|(&&m26y0~JcpBu*Yfl}ObrTt0&p zPuMhUUc;45-5gzkGd0+_g)g1FdieVNzhH26d3}L}iIJ6~GCM*`P*cUs88mI+(#Oyt zL|X{6Fp!9hkd&AZI$$zUQW{N^6_>^nsp->O9bFz#=b0I(RQ_pju#=Tgr7||vpI)k# zH_jv)lgs6Dx*sg2S_2?}gkh)@tL1vtzhJR63fj#7%fzh|tzx@jz_wj9TuQU$cDvbd z2M8TNlt94>9yx#%LCO>=S-_M*%N{y;02NBqp#5JwzyKhDgbElqfg**B8aR0ZB#D$N zSi0spOtxU+e#Wagp3rEt|I_cta>c%fV6)v2Yc^iX^96^)<#Y==M!^I{h5ZSnDkaYr zn%tRWkZeRTEt5>|9h72H*Yw6n$2o>)T~%@PtXGhwoo8oYVPaz}$;i~!&{DK?0Zaa` z@|ijhsZ7V;CEO`kLtW9R2aAj%kh9jB9%-dlI?`aY&4l#D3or~`vHT+W`#2zOu=kArPXS+!rd*k<$nF|p?in| z3VRd{LK&4H$S9?dM4!|$4g^wIWC(#srt{B&@z8)oir{*R()}`(Ge_@3fPB!T&<9+^^RomCa{ao3BC^ zi{}CeiAVFUUq18;EjgUgXfzwlfCtcubp3zUie$s-lJfd&=+LS;21RjBK*(t=45PKS z&3$*#eK*v7cMzntHXGC0ic)=-+I-h4)!3J{qEewK)ysCZuBy@9dSKbCoP+hZ?h1zD zxadY9%W@o7J<)aDR$L$Q*6qr6Uc0@Ch+Gu0775X#3X$gLVQQF^N~!9;VNg}IJS}Qm z(Y08<;rwQV($s#iG>ffVVk;UDC}83QismtB;N%IC#<@C8e-a*HUsc&{s{BVswFEm$ zl-SiaUu?3tttQtLO)AtSq2JrtYMg0(xDDAU+7=FNF1lgN4$h6tP%B%m5=6LdiY&@Q zIBv(xg+K=hJQ%z8JQAJi;Jf=o&U{zRl)ZRAzeh<|FIN^c<%GeA(g~A;+FY9|f@)Zt zj(>HfrOEXHd4Y?CM59tULjIQw{y!OG6sc0HP`Qi+K!5=yUew@0lqqH2*x^&CL8V^V z;#Isci7p@{CMq9{Cb=k4Mlmw+52c9p`yE;YF(!@@MLLASrs0rOB8dhyKOqB!Bv>fT zriV_q0}Md$|3^Uo@0n!k6slITxScLnEB5pCibo7&GMS9lyG5o|s|U85t;TcwGg>WI z%f2hG24&uO?GF<1j~Ma6|2Luj75`U72{E?)Tge#0fRfwOy#5C1!vSODzDq)n_3|ttIax&8lfiq>1n!e-JCA&?l+J5#c|5&7M`6 zhqZOjuny!W{vp|i?yLE3g_#-uTGlZy`cd}K9dK^_9`C_V^(W1MS46w7N3HJ7ZaE8& zpPOIW-emgDzi9j6FLJiy?k}z1OFnu(OUy@OZpnM@(}k+I*{kHk=ciLh&)yNP*WB;jOePHe{OWB-JuZ$til!&~XPAW#&A_L4PA56SQ3asHc|DXupYSnJE?!Ps<`mrz$FK90f>n`xcbN3~TUudD7`>_Sap-3e9v z?Pm7Px|7YiD~j(~5gq>FFOqXXTH^~7uxO1THt@>AF9P`|s>I)rEJQB1k!W#kgYmZG zP*@>5ExoazhSr9(2DOda1v_Z`JEV7b<1SirfiwdWW~>>VrzQ@^QZf43GaSmEH=Boc zym!2a+VZN7&un=vNV2d9*Op~C!skOV`my_Z*p^UPU(Rn;5>BbE_S{zN9Q9rhkqf@+ z8x4u7m+F(+dx-mU=bnpgd*EQFaxO60-WY2Cj$Xgf$puo~m6m32+NL;U%&&78WxEoX%&*M=+OqI39BhdQbuji!b|dDR5NkVpY8^ zktJFWS#`)Yv6v0HH4%&cQtXoe>M|{a1iCU%@w~IH zb>yD;bswpJrUJxexW_jHtgRne{(>ilu)d zqjku--%N@Vt&vKtYLi=!Ic;9Mk3(l-xg;lG1R!|%ku$IR{dG3}*7jXv6AwpFRZZ#4 zvJw`om8%UA%h|CaK|Yf%L&aHz?+-%r6US`V5+Ni7s7orM$ndL3Sz&>JRX;P;a#8tTKG!*-i3qrz#{L7w8z7rx1x^*;~C zr2R8Z$!x4vsi3ZWHyhYX5y;r=D#ESxq_sx1acD_h{F181GmY(B>vR)8kcJ5E#c|Va zaGn^|mQ63FnFaO^cY$IY6G#q{zI!-V-XYV1z`WTTRd#E@A~RN1vNSapKt0?{>pDqe zouuLIC`oH4MQd_pYGtpRK9e4C{;}CMO4~Tiu+IBH)38{uHaGD=;$8cK3y&TCew)+x z|6aY9al0ZvZ2bBi{b}7a>%w*0B%QSL)+1!9|9Prfv#;CJ>+AV<^7@`SVw1P^nDx?j zsa|Xm+$9n|g)?#3yCa^2Ae=}-Q3n2lL;>9a`!wj~y}XlmfU1&ApYWHvR$?>uBFZJ< zk?DnUZ<>y5kbf5!M?SXKUycxv_||X!2SE1&KDr_NXUQ*95co zeD1Fr&>0%g>F}}>8o?JE3vf487QhLQbSgu_TUhY3H(F}l6V?s= zlhr~Q2D4Kq%*>I28iCZbKBxQKMUE$5=}PkajRzpM5DT5W%G*QqOVRkMmM6rCNm1uZ zA&}?8YTk7d?)xye%f@@dXS*}mW!mXFwU+xW@HG23kuG;~v5+gGDbFv*mzjYkl%OXL zKiglB-u97f5!z#h`e9a!;$sy4w|3*bedyMtSQ2aASQ8?>gq~*97rr+#8upoYapCv* zw9noL^5f}WB!8bNS~E4`?5HfsS#sR95IRd5RNvm)-oDAj+rJe6N>fL+x;2$rIr|*&T z#?i)Q!0Nf;dG+4&)pOMI({s~vK@2gRKa&TG;AO@|`OXO`UZI~n{|A~bUvB*QF})mYz7$ZVg3y{WZz zxwlvO;1{Z;vr=!=_{*YYjCs+u=7_R2rG>&Z>~T_Os@^gOdsa8ifgA0ncde5AxqHr$4MC z+u;L#jiKi$Kv<-pPb501HXTc}LzE2mUsevYMS_<<%4mqOz;Q?-OY|l{I2}_;9>j}o zrYAZk`XrBI!;=_FA$^kIFv2vm0zfV+b&$&n7Th(P&>+7%Frs6|Z&wj=s6I1RT!fOl z&ll|{2~2BBSG7vjHDp8?9(x~6mkbV0#>u4Sh0-oA58}S7+GlACWluMV`8F=j$ToDk|T;FVOGoAvk=>K zAkr9Ah1E_X3KaXwNO$vfVzo}h3`tef^QHBXCjeCoHmLeZ97q!RZ^|tSty130`x&*% zmRZ;u?ka)=twRMZ^VTk%OY*pmlnc&c$Cyq09i_CTCQ)6bjrxKNtVC(R(tL!tj4YRr zyL={yICa;_{U&|kNB>dByy zaG2Ev=Qs@e6DKt(F}~;(=*F=r9Z`f(WEg|v280I(MjAe41efEC2p)>i!+MZZ&R-pE z4?gtBSB-&?Xz(?9{7&sQ&5_7tew%uC-K$@@C6qEcUqpb27{ZU#MH6|K_IG|UNs{h+ zFzDaYtgK7yG#6_%z4Z{G+;$ev)Ij{aqig1c!>&K#jI(FHQ6=f0P0H(YNx)wCyT*Ot}JH z8u2>>?@V4(#TVj-%b^=%)8r3^z0I`qv0>^NsY^(McFJyAOqB#wVZ)AUb{L*wW-v$T zYtve|v(Cpu%`<;43sV`?2NQ&GbZG2mB|ISyt&0%{vTOsaaB1hj zo_f%`D6WX?s#l`3{RD*O>b<3X6!wg{ejZ>jFtE}M4=^t>o-Nq398&ZAH7Q)ely*2) zP8E_+5cRuX)|qjN5CssK8&@n0mFp^S^-PMc zYA*T9TI$h;p)zz9eBVDq5=)LbwEuoc?9P%0~t&V{cjp-81EGGiI($SmPOyWjnNC@nqF z6f@2aCM>hSxY1lA&6&}XG}AHpRBUU58CHUk$8DUhymZSUtxX-2uVu6M{N1$@y3j$t zvhe2?i%XvOi)MK_VN@k`+D7RT-|8umagHmlSR_b}0zC%dc8=n!CB!Z%!I}ovf16lG zyt!lwFbs%N|1!Agv%=Kijylm-`b8Aza4=7(QGcjz3u9}!QM;qWJaQwBLWOF@Xw{+1 zzRf;tUXMu&+PD5zt?k9v@QwY&*GkO2qRS9DysvA}4!U|~-_^#0G**2LudWY*dGOcj zNhwfL;gum=R>tyWm;!>@;aoK(Wk~`6mL1{V*aP>z!us&arPKt1k#*Go^9Jde@rEM9 zSRlR-0{m#z3n)gR2A4&ivckmR0K>y4Ofck#e9r7qic-2+#H5d`&N@EM0&d(*q=}qH^x;rS z{I7>!nVQqPUpMHt%VypfZ%^pdYW*|40g&=fU)I1~yz~iu{`*n>NfuD|xnlvQ&+aF_ z^UmZ-5LT4Z@u!NdAssq$(yPvYG~S<)f6qH2od6R zh9gLqB4mXv)F@kNT*g*g+t*N+TE9CX2^9$+d}`l2u;0}{wfcM;ZOl_7gp&sE9TpNZ z$AAnEPTj~kc(Y@F`{@x7ACzQ|sv1e_s}KZ8lEOgX(d})dq2^fg3QyC0iJ_sQ<@jDA zrN>qGVO+ILJ~Q8+?tSw^zLe|I!BY90=ks}?%sSNyYS^4*C7`-(VR(QK=R?AIs2&|$ zt4`^Fwi`JYR8&oejIv=xP95P|OzSS}z?#8^CTMpx3#d0@(n91>xZ3l8YXRzNlM$tRut8z!uM}=i zn2)h=??4`QNk?C175(brh#?a0*MLPP%U5(EHgEYvi`{GCL%DHiZSHA;*DX6k=7~E0 zgv3Fz{u<{;dNvn!w*!EE8#Oofr^O@1Rod~VDO+SAWG>#2qTWYWexGOs7mqES+d)e+ zFanQDJ)lz-fDj!#RrAGp+$_EB2XH8H9hh<+H7T-Szp6;x4MFSs3R+xb5%TCbgy6vf z(d7Ua!{c6nXC0i2+^y!|XP7rEA+0T*=D}`=QDX_s8jtJ98S{ussU30wV2Z z{^M|?)t>|9rwIs4ir37;565A5geTwZ>4xpm2lZA5o>^@Npr|`vDUdCxIzlib0x2)o zUZDx!XsUT90*m)jvWGI3bKxR=Pa4M8%!&UPBxd*9&LSSYd^ zC%3lpp|&QAJyl#w>}=Ox5XZn_sT9d+6_N&90MU-_6^oW@;Bz&Xc`o#bP&c6*6iVC> zifw8VtZ`3v$ixI#D%Dz4Gy##rvtS;3##(C%DED1dFy%E=)sHfU z1{ER=uyMTv<3w5mnxaF`D!5ATfM!{<2?)eQa8k>Ke>@EqxzNQ`iGIju|D!1b)9sNj z8Q=ea4IrzGu$WO-S~0XXElCN#9bJlsbf_HwH9f8ES9&SY*NPdq$wvj01`2{fl#8vr zLnI#Jdru3UG@M*Nm^N%m4_BcytYW%gA=S7Ts_BDy{`T#l-Z*)i=0)P-XXtVyR68nK zX)P&JC7j0s_A3(3I}PS`Gw-@Xzx@?O=G6`8Vnz))1*P>102|*o8sku~q+RtAFy=P? zNZAof{Nfc8ct8NU5T?-E1#MzKnMqESmhb8C=oPHtbU*locto|Ji-N|uNh%U2d>{yrLt2ur9s!n%9KT$f#T#e4p-s$3ScGnqugAjkG zOT&Y(tr5~8taS-T-1!oLPOy#n1cgwy_3beDuAo^>)TlUxw77}huG~cXF=XK)efZR~ z>Uu`8 z!fI0!nAIozwJwqoDCU5s&Jg)${C18_n|#y+O9jz*ALYmsy;d9^W<|F;ImnZ~joFWx zN>t#sG+1+br*~yk!s1tvU8(P?EH+CTzSp*4V7}~|E9KC5^u6_VSeRo)M`DRTd(O=! z76mw50m_Fbm2>^R`zlZSey7I6WhV@w3i9HsuH+?&Mm7linwQ-r98nq=s(*1n+w>gF zZF@v9EnFH)o42BfqC@u5hp(*IKa-WgE+r*cG%$JE>rF? zTV**Zj=hS38W*TfRDWdl4TGUAI+i$$C$^C=#$j{F#gq}shJXh9V}N`kI$?4G=Ak95 zu?)v`jFwX)p~7n^mX|oiDcaWUbEkNY){X8(Zbp)>p}J#AC42ZggPgjsFxa1C6E8iRaq&zCxaJ(sS#c())5heq-t8Pzi!dr%l+NTN zHE`1CBc1G{YAXXZyowj|SE9;iB0Y9jb62|DC(!H(O5*G)HG7|E0(=u86px{7*8_wV zJCzr)Q=FmxJPAk39@q8v8br&Kg$mR^`35aLV}+eS%AJ?Oo<$E|ant3r8%v{FMQqs$ z&5r%$g%8`689U^qJ^@ksYzCt`~2KpM40!Dcp6X^FJJ>*=~d=a&ss zD959bjje@{Cl5}}YG=GddrUls=OPKOE~iYYr@)Yze5f7oEX^InJOsQ2_%=^3H+I8{ z`<(y_qOg-|VRIV9UY@_C4^@-CGB76r{V!Ny90{MJ-z*ALD~Zy8c<6_9epnU`-c;Mu z|0c}zl!Ci_Tz1jj5hmhpFuvUX4*)4Z*1zMAXk>FX2q!ZS9lKy?9ad!8)gu~Xn_&-T zqFo)~f_;bvk_?F4JD088Ir2j-P{YXHNH}?Nd#Gl|kHI8-R+^eQ01@NFfRK$XzNm@Q#{mjN zakA9$2~L#zv2y+vZ@<>|xBQ7zrQa&w6rv2^=jTAjhGRypzGLcCaN-(cCs>QPz%%j6Eux5>av5Y zN1EyII>o*5aFSeOik?aMcq|?7*w`%mq2^}b(fB&{%Rb&=kgoko3RAdo^Z`;~BG{iP zh2^@xjxQPc?jo+sg{H`8+1Jz*#MbO%Pl}M7uA`OSSr52E0p+reXYRbqW50yCMPW}U zj_ZfTq`Sj5@`IoPNTb?1HV=zi>KplUmbfmJd_NYg=(0!vk(7l?8#xZ7V93j$!r#~gh_kHZ{n^vFG!N*<+jpN~u+KQT=WaTo_tirX+n7g= zblBcAZ-h`oCZrAnmQru3*oajiC;X4gZ^N$G1I7raQ;$PB69rD`nN=*d9*M~R!gDz# zUH~yY5S}%?2S(Y>B6LXu-Y3*k<~IptvMmy9FBQaBgjc2`|2a0v7;FzM6MTMz4u=;G zp`jVWu(7_F?KJ5J2NcV5Q=#%X>`iLlKGLzVL3{lr%>Bm6TKCj)ea8%E4xND{f0V&V zQ{aoAwv^itx?%6e*bbw_mhNS7*&BlYG;^) zO-$Ge!AfV~dM1}Toz%vav%@7+%K{0+OHlQ+F)$_8JCl$ZvPVEH^9x*-Y!cKY6X7rm zneNFVGlGf*hLG)Z{i$Z_%}B^BObTSQA>Cn~Q!pnRtY(e}SiFTd8-6@}kq?9J!s{}e z@Ued#p!V{BD&BVdr%-$Sz0X2yT`fIE-ftz^d;a{U8{c_%D&viOMS7l_zg@Gum$+_N z)OT&bA$6T`<9Z~Jb8ljVRrrHo&}}zNebk}fQ0#>@lqWbNVm1v_+0fugA{LvC2;sfA zj5(v?HAnL|vytSztja~)I_aAWOTV8BVK45>O^2F3YwH&7xjTQ>T#Tc%mL%3lZSV%k zU5I>Rk1Oi@jC&Jbqi^qP6|O}JN*VXf91SG8RW#gCqH(QR+N=oJ&stn3;5k_Owc?J| z;GqCs6LEI~6lU*WKs|?nq8tL+YC=~v?#%?Fgua9I80d@*iy}gKdl-@6l?I7WZ=bQT zTD)2t!G_W;O>gWy4`nm!+-1kJ(Y;?veuSi7VHlMJG9J58O~ieE(_5eptfSz;D##33 z8Ga=KDrc@jsMX?%NT4ecqtXqiX!|hPt8-9()+E(pD}Av-j#5tFXWQ6qUULn$cNlR_ z0GKeRShut*4D;@NrQr5XWV#&y3(aDH|#6K zwZgxW%(}4lN@2n|FH0^fyd3lfxB7C@kV-Gu1AK00uSYWv4BVqofLZqVN~IS6C7sc} zjr(eS#|H4??X#BU)#{r(;@M@_gbN23XkKJN>Pj4nVR`jbGej-#oqOC){-EV%O?x|j z#`nS^dd`{HQ~>SLf&gLO`h-9X)ZLyteE5CpSnQoy;p+2w&B0%aV<}N>g&=jmqcm;6YwWZ znno}fk46FK?{|#scQ4~IWy5rE>n}^7Yt-^L!23y7pzd$TH6Sb{J@nG5=CF))C8Se! za50Q`)3$L;c{j_BPTKRfu9^MU4r%P@2F9$v^{4M>bAR``ZE4#ej++~vtKY;^-gZaa z0J=UKeG!8Y^=i@PIlctKFUz+H{vh6!P*asamnR4B!M0 zyQbKW)}q}ari}Yz4CNgY-E-%FVIp<}$0YmL#mC`bHqVD;UvfMkxL74GSbEX2s~!4C z7c=3dr0((Cs8pI=UXV>vGi!!ez)S1FWwJ)0GLa&M%joe`7U!Iurx_&=`gA!>DKp+c ztdMV+_+49_eZ2$H2n(@ihXGyApZVd-Bohv)R9XiPaySm77IFl~09sqUfHExDD9G66 z+|Ak}{}`P&L|U5Mow}AefX4N~8N?eRI5}CK|D2dWYa6`%z*43wyR6$O?wzA)dU%e$ zK_s)ZUs}{T#J!N-?NTOsu6^{Dj~`#}m(Hf%U4(<+qkukBYfT>u-yrpF&Mnv#cX$1h zH3t?BKDK%q;kq~^QH%FNZ^;K8W;xPXdJ7jLI9>Ghhoy*t&yciOCi$F~ zL_}nl#+Kc6$)#*0ZyV(%l!ho!xR@Gx@Jl((yg4NrhN%-f)0ltDR^!i`POdgfCb{3V zW!5t6{pX@8UDS0-Xg(%v7}qJ%P&NKIbRH#IJTG~3nLd*EJj4sWAFZIHg$j9=#I)FA z=b{wvxmw|Q4=9$Q`#Hr3*9yU!jJW_VNgXy{O*H&V^_Hl}99$_^UPi+B=97gC5(Od6 zSK@K&*0BV$b*oZvR^1XLA0l4 zC)rh5naJy)DIVuNv=`11E&K^_}QiT<=3uioNx5-pkEZj)yt-ZNO zSX7VBIJ2AAh{;wXUKA%bi7>wAc91S>!C>jbJ(!IDH*}N(|$R~K4}l2(3Dv;_!A>QN#65^?@Uu*pJ$J?L6L15UM2zE$JV*&N>FA%5FIo3_}uS=$Cc_Mqit@utMH^_A-@*4LSd%Cfg@3##V(8V}v{p8IUl14F#0P^)6rvO#>4iQBa?>|VzEDq7 zJ|Xiq!ajn`m2f3O(r5UXA658FvqoO9%a0;uz9|KbyPKCnpMHAlQ!}5Aoe| z+o2ECcyy0(UcYX8Vg#I??Y8|Gl)J8yJ&N-J$(fL5x&nn`C5Z{4f!o2<28Y6LL+SXz`t_V_)Cr=NuY%K2n+ z+4XE$UH$q&B6PjhgeRUtuBhH0k4Hh7BIc$w^`J58?NbRl*merSWYq# zWP2Y(lKY6q3Xa|=ezc|wiieNAF;s?d9ONxHB{DImQ?~f1aye^nkj04_ZC4+~JXaJ) zK{BZCGH7qHB1JC3-1(677P70KODMU^ORD1JQmDkPV+~V?bZqd9eXz5xFG{wKrh<<{ zxKHaDsSb;eU!XS(O*jo#O9QFeCAL?V(l?%BrE1Yp#oHf$OQYrnkpWGqUZHOb=Nc7K zX=IudyLpsD?cj$Vb=+`nyfC40CSK!LohuG@xlP|!!MGTn^8?=^w0n)Ran8~y6^^Sp z%f-Bg+xp}d})fwZ(tr>H%aUvsPP; z<*Lhf_Y6O6<}Nz?)xew~Vxj6((@GGvTo$CdYTUZ;RObE5XPA`4*>X7%=K_)G+SwGH zV9xbk8H$;w__RKm45E>Y)3;)*fbPQKJJdfC8j3VWY!gpphKdy`qO(US3Pr1Z)I>5< zrF-hbt2IVB>^4?2LlonnPc1IHIc2dZR%kik+%r^4f(Bw2#kLX7VLXxw$iyE(r#ku4 z%yVX&0wI;aOJ&b)iU$c(G=oG%i_xvo$^~w-mkytG`^N2fP)m(I8w~C=|CK90ke;~S z5i;A1&eSbTi|}Z1rAA@GlVB~?1fwU-Rb!;EeA)U_3mIvVHJ2#aG;1l+(W2bNHgd9$ zx+2m^gJASs0T0gof_CH&udd%;B*Ii@Y$9c~l9;7h&b_?&Y}?x63bg_?C~Boj1r7!S z$X*N!)eV5N3`IvL4bo4dvigPM*4X2~_b7dAvCuuY0~c`zN0-Tukm!q4Doedm8AyjO z6cNJ6S(3}usSgT=QE&XOI=)wH%@cw8maN=9+ZMQ(?x@BaX6u#^=vGrgxX6$#-A2BD zm7S3F4c=8OUVG{D8+?P|n}Sd~B3tsN-xqVH$d_71G?@v_ql8rwODrNH$>ft|ZG5l! zfHafBi?n7sJE1N;TIKmxdW*vk`5JyJP(xH#!-g7i3j~x1;2E$mJqxm+++Tr8 z5l1t5hIuoD>sT@=J+Br#eD1n@&w^Zy@#rn&>2@Vy)u+r^fwVnTxSgCoy_KP|<6Yjy z35f^`@l^6tRF9Ch-l{(<2q7^qgMSw4D0uY-Ak}k ztWqNP?$ifJg25t7h2M5Xn<^6d614U(GAdJM_+dQpT-VWQQ~^ne=LR#pc{rGiY{}Wv z3S_Nga7^Xk$_2w~76&Ja97N&%ix$DS9PkX!*Z9xHK=(aDW!i+Uo4n1MZ8d)7exF;L z6#F~Ur>-9}B%zZiz z(M=$%8e1X|gJ|KcW%$v<%5UI??IHynBM*)50rXs~T@};Ivmb-2|0FkC^3bYHeoCW~ zINk0O524=JXa=nhV*5X#oz*_Dc|M?7Li=RkUD={H(hsIwNqKHdrS;P<+YiQv|CYtO zv;B6o%T%Q(SrQddv-y7t^pnggh)Urp(7B-OVQ$`|z5xqgJ;i+usG{yhAUpxU2UMa? zHKNg<##G#Ua5T&FI{Ay>BDwwL?+UF!>>hd#C%9u{v>&~iR_&K`oJOx_)Ds!?)QbI6 z1MnDJ_;qOD>hRM`Q|@b${pnEgU+*g}DCRQe6qP>F-}?EF$Bp~@Nlwty$?Q;; zIF7#dt-q9(6~lDD@9szbG&5BfR70!rVtV_8c=n4UDE2aaj1C{C^>Bi_f;soXwqDh~ zTH*F=mHV4jin+PMqw);)5s1lV}zVRC^!qBG==T9$fB{ zaU5YDZx1nU-hw(m1VU_N()=*9reI)Es5N`Ts1A?O31o6RWuZO z)?iiTp0zCoJXVn|5{lx(XO%EesQtgMHhaHxP=E> z{oaKAIF_31iti2WlNpO2cBS zly>LduSjvsz6jOLk3I{e?iKH)GG{^BK4V~x^;~t=o8Z0QxHbkv_JzjnF~LZ&Tk3sYYM2i6aqQMSau}CyZm81Ls(|WNRV%3n2Mt2V?*HFoa5F<5PfF7UcWx=U2#)EzvOoNg`g?y|ub_ z6J}xjc_i`QBL@i-4EVw3Xe-58B3|7^R@BW@Hp1pMhBJ#25)XvohHezD+_*s>oG||R zp527FI~l_@Or66H)Qg1ht1qvd(V!YZgh4VQuKou%`Arf>DCC9L z9o)j&`2oiA>?WX4`Iar^HQTm;9r$j?whBWuG?O&Y``EpF`}Xn*wojErLKYvW{*^Ao z?jVa9U@v~&+MLfY+!TV*ew+&|OwL!3 z*vbDY#Z|a4gT9z^cWg_o-{j&%;OWfhX{;Nb=1j%aHQS5W)8i-^EUexX0BFMm zpPxRz@bfXa!d4X0bK(<%EM7(pFKDn$HrNV+EoaXXcIjRReBu`}CEPf1xVL-q(~i!M zN4H`@*t^j&DgeAp!DK<}li$x1?RW^;l(CsJc~FbpbrQPZY7N@7ulL9@o8 zz&M%Wl&UbGRK42?Ux`!-JKNi_!shlX8HHFR${kcYCPal}T6A(1bEtZZi*gx}4pM4k zE0i&LLyna8R&~r`l=M#n0VXian%aELCXbP zHl9$ONgfg<#+GJ5H-5*7vYdTlnT%9aC?Y38M#MUaFfg;%`}ph|t*vOD`BCj9j`Jsx9VKxUykBw6uwY(Hq03vhq2BA0lGfuwANDB%GhH^@t#c; z71U&u^dR7g>;@Kw-4KB1Sh@e8;~;)eQE1vKl)|$eVCmPnzLS9@puUO$lY2AF{*=`I zbU+YEVLl}HwY*;ua+f#|hWL4Di=7LF5ES3rD-j3c0bn7tP-=>_x9 zTJ>U8ZD9rC9_bnp_TOY|g>=2iRN4${Wz~HINTQ(mS(4u*c~zT4)I&gzSOx9D1Hu42 zg)I?$D+lJ;LDueFzbU!D**3`4PB_Ow6dHH!`4zDmQ;cE7Z%o*MQcg;CUY8m{3dh`s zX@Z3%#xUTEP)}!TP|z|&1`(F9DR_VY$+DQ*cy1Hby}Rl}?Hd&0#BlR&T>Oo^FmyV} z)g}@oaHZ)0o~uK3%4DfD8HSZ!30^!Hg}XtRm~0595RQ7_3PFuHJrN?v;xnj`7JTb> z_*P4F2IZ%p2z~HEc~|T4;u^WKS)j2b0Hnee2tHN2AN&M^Xaw|3CYC*A-jwWoQFGT zjg?cr`G)XX9(N3{$uaht05LOZu}A!51IXoMFQ4clsd^tFXK3~#6IFHeb@b>)gp_Oe124%BKQ@t%<{Jh3y@i95kFp|*fn(_=2HaTXA3v07)Ik|r(2 z_U($jE*9sszCq<(3z+rH0DAzUv+7_IYP*ivO?Vg*%?^&NK5spb`2K3K*)h;bKdk+dgf&&?wNr!Rx9q(-`FNk`scygHa?weTgoyMHxeZowZ}_ z?V?#u8%nn!G$k7)AWzh2-V-kgSbnAM0C3LZ0j4n+S2~p>Zag9gVF0wAhUm{_G{z{%OH6}`$ zyiVip_jN$nXU2`P6uvZ5l5$PCpTMBVAcz2Qn8XoqAfk7+(%~CZ)q62uQQ}m1Z~<`f zEXC~VNvj%8&Xgx{{=c@i$y#M|N6tc|d(rnu5TIn4xF+ysPqQZ1M!2z#E=%-zE+23T zFT#GuC&u8h2^b9%L?09Nbe|ittqy0e{kxw=K`ecqOb|nJ-cu_GP6&(~&JU-?snqemoLxzYxWEPWV+%SZ-`}64 zsRG_5TMJEl3Qdo8(_ZO+)BFu`L1UAdtnW4e9Xw}bE+dapNSX(YiMr>J`{7V^X$E_g zh`X?!;BK4E6ZLd^Px^$j{l?mWMoA+St!jy2R#lPxc`)njVzF>>OqRlWJGUb#PKVRl z%3|F!g3c3RgF8jk2p+*;kTk{U?9?3_e4Lgh;soCH_Db^A{c=WR?s+ySoFv;N&FEXS zEv_EqJz7&pQ)l2Gey-A58|jVz0Gt7Aj(5I}65XWZC0;20kCEDtt~RS=0RhG(g97%MTFT%stAa@mu6X+S+51y_##0b-lLM`jgS=)*1j%RpELJUnp;D`3L`OEnmRjJoeucxiSx;Aen#NSbTRLj&i)zoreDp$l~MG2F6Oqdysgt73r_^%=sVQc99^m2)5 zSgC1|Rr|D+NVByr`=i&8vWRm3*A0|Z&EeZx$IE;V%Ioyys8@t8BI=10T_LY4Dvp$p z%6P_q77L`+J$?0}`X-SmA>$M-9#1xeB+Dw-ts)Moj>>&WmNe12BzHK*H7KxwerKss zAdKTc5V)tbj#G(8^Ulf*-om`aWA_vhk0aF;g*yq|ILc^ko`=;lzocka%nyzI_h>fEY+Qz)zB0nFheYE7e>o0Tv>dKA)4&_ z*I%}@c$p>suZE;NMyt4O>#mXdstkDz*#_rRw2sg2XE;|QRCiYymnOB{mw`kUtNYS$ z7%P#|TcXIbzkOz5M{slbxo+N|e)tT0VPvlZFnC63iWS|$)iI_>2D9^wip|e=jjUlV zpK|=4xGE)AFHY+*ec#XI?;NjuTooSSHe*k+`eTuGB>M$P5bY?ck>0%-=7Ov);s9l= z3tRI2^bFQ>+08)fL-sC0T}r(n3zku67+qF#?|p5Si^TNY`rI^==ICyFnsL#Jz3Gpl z$fxhlFc10)>d2`LWwPCsJE!hLa6NV0l>m!dK1&*1lt(piapf~rP_4S+WCtXVcJTE= zLBx7Bug$B)BzgYkZ~cm@5V_Jt2$JVs+R!KI*z(m?Sr$ke*&DL4^-)W!EUT(Up4kAl0c>7r3GDR* zWJ-^szDwx)d?n+!+FfgNF{^2R&g0qgxC4%q{^5`0*oY$~2%c(ZCudrW}TcvAaG?*z@vpPc*nB&CCLmcZ%NG5f@lw zG?(JKOFmb64O2FkGlx#4`!Gw&R3t5b#fx(uakSaTCd};&XxhY&H9t2vY%APJ?B$X= zLt$xq`ED5i=V3z1OD=(7>tG=vKS|RT#Pr)(EW2&_qYqxPACEkX%;(MA^rZcrXQ(%^`dCUK|7GS~m_Zr$rQ=mR`ixtNez0PB}1Q&DeHf zmdnF!lbI6=36RMcbq6989%IsWEsS56zFM8F<5s_bj+&kQPtLVQR>UvUSl4BEo|mmp zf4$nwx7#W*wK&-3{I^vt*a9Qsna0bvp1pXhQ9YGwrjx`ey~rWktu^S zU{;gZuj(=}SzWc;P9>kt3HE{3`V?yuE$WB-+$n!RTX_~RI0+-`SGTnpD>9$HrO~#W z97GVu*_;$fipb^Pdsk*gdQqN!dGNG8U12_g#N-Xq^&>y4kJ>uf^Usyes#W@rZ#6$> zZPAomn=&59pSN~aY}R!8c|7jc!z)yuC?ag4A(O9I2&IX5t}%=aqlpI$WpIH$SSY8u zbP~=14tCAAXsOaa*hI+8jw+i(&!*{8L97eOs%b(BnVy|}qE@pZb8@G80vo8c1@^L=b^>mu)l`I_pfD~Fve$f{@Ni9x+ z`)IYFwAglhc{N$3($to(%I;a7y}ld>8O6?9dnG}kH<9gSw0>%jtvbM-8rtV?HV&rot0Fn0`2}~GIGDDLCHyzRr zF`~vvd{XGLehlI$XDEQfAwom}=x~*<6V|^47tI+8!?<4bfQG87bkb|iYnpb|B3NX^ zjQ3sESGd~dM&UN|Y`p2dDIQQP@)X6aruxl^<2q1ndNcTI2PXz9zsjIG8cr|~G*2fe z!=txpin1H#|Ide?tJBMTN)Xbb+G0&##foze3Fx!-UXnI_FGc@aEAhgzlCb=0%O6tO zYlxoUgl8=8v(L)TVvE?>(L5}BO=NX@>ug8sdLGK=iq=KNJWHfYd{(Sv0i|jdQwWyb zTo%q-NDUWx=1@?|lu2oIK&&nT?D?O-JgKtG?sn38s$_+M^>p_2aCuwX*L)4-48aY` zcF!{tv7JNg3S$L(DB~bkmTTn0Q*d)KxGe6m_Mwd!y6~S>$kJ6bY^ZyP-%Z}0lp$t$ zjh3s$37qzr^y-(1?`pY=b7U0NaOb<(d2oFK#$6Qr!GcLYi|+&SV* zTS>Wik6Py!P$dMH1zumxcl5*OQ`7LYsW7}|^4Rd4REEpslLk>}cea+{5t3AiRL<3i zxv3BgB|V@;h*Jfr;^Zj2bX!}K-PxQ^OFBQYdsXe)3U)R%5OnLV%4-p@m3@vD?jRK0 z=cIn!q#7zgZwslf>lSbY%O|N~y1phv@cCq70kai6aJ+wktp<#^@m&D!o+oe#TC2Vb zhI`wPw1nl3>~!i}oWcbxK^%Xj8(2Womqu!AQ|qQp&+&&&N2DUa;|*658Vl|U&=IFV z;&i9;%_lXLP_}FcqdXZiZ^W-t#C*JcG;)9G{>ahioF~Hx>mO_%B8M*P>8azDC)YpO zKJg?@fMN)$!o(?@OibZeW6)#_?Lo>7Du%-zsgdV2C#*h|49d{{*@#cKmVr;<6s>R9 z$&XsM|NTlj<$h`(tH*wTo7c90`r)Of(_UTGj+=y2`1A1jXWGiiyOWXoMiSr?}d)G6m5kX7t$nVzZbl@(4>%Xp(s&URAxLXShCu{iP9+M3(1ig!H+8 z{Ys#9A*^4?>k9HDBPEUip8&t$0EY?Z!8{y@Bn$ea8=$`lp)W&&1nu3>0*1z;(J!Zo zPU0QnzpcwOg$SEn24D5{Jbr>bnNZ)#8gc3Cf4P7yV5xvts6r~UFq$AL#x;mM9m6y7 zShzp=CM(=O2$MFIUu-Cnti2^wnac>fYEDN5NgMLxV;#7p{-ohou8#wBN?QgLjCvo0$dH16(6{g=}GE$Wk%_PaPSgu^Vg7wF3`r33fLy15$ z{t2uP)+Msn$s`>{&LhJ;s~quEb_MU<`Wsu})7bR8z?IHa0+}Gsly8OMpWBe+ zgw>Pms|4+Ni*;Y=j$;fh0NY`CuGVBOoxq0Kzegthd>?xYL@C*T5}3E(lTu^dz8YW1 zK{4d@Fq&ItIzW+JyT!`bD&5Mk(l|+|W`0G#Jzg1KztaLshk-BZnh=jnEI_S~6Y;Kp zX8r8Ch!>~NOJEsi5=F%k{HBr&p2E}xdl&OAVQ;Z3Trbm>#!|Sa?*-)!+v*_AcDRWR z{MU`A6q&^t?%ip_tpy$c`f^iJtK+x+rasXJIxua`Ad0DYM z4aik8fq3co#Sw|3GZ~F=hI?c-A@*yJQRJRi6gXSMtQ`=zeGcQR)qX3t16o+tV8lBcmls9kq2cItG98ceLTv1cL32!Npols_$Z>8Mg%kJU<$L111+#Kk;1jeTK&|EZ0xZYgA_{cQ9_TapAc9{>P64PyBqVYQ-o-TI^t@EyN&+pJBQC<^tv`y zLywqss8aMm9UUt^#W?%Ka`@x5El1uSm+(D=Q-cn&d1IC$AydJM5%?^*;ruX-3-1NZ*z=I*S6`LFw2l6LdwhUmyb!6xj zz6IIX+}64gd`q4F)}gOqyRr1%$0|2qL{QS$=BsbiyKmL^WG#S~)KThDyFqJHJ{Bpe z>PtMgf-|H^;i)bWn9EV6nUp1368dnGQ-$?eY0me3#m>$@dfKrCrGH{rG7S0_oOiOV??2a-j_*0qx)2g!JsNtUy@;B)993v50xYkHumR%v8d4soCl8Ep$2(K$(>Dwq$^<{B7 zKz|5`V3fq7=78@k8VwRbo0718z;UE*|U3i+F( z149~1*)(9^$cF!_(d{C^VbMX-=$O%bW-qbA zAXP+3^b^dxil#kXS768c_|3QkBqiDs=fFE`U2_dYnPpm5P{$oujy*Dn3oS=V8=yoC{cqkOB5Gr}`R!5(8 zb}$WFtiCfsAk-{xt*a{C(G1>->)YB2v6%kEIA=V9!=~|(*YJpSe1xEJX{@QLXm1Uc z$z?YK>5!+uwd-?KEU~$*08F87pM-ykfnRY9XCHD%b-h z$rG*<=*#n-i{dUmo13(z#xxF9e432+7Y#Cqd54Q{Mz(`SQ#>a6)vDc;U;V{5SE2 zLoUA8e%|_eil6j|SI9FAIXVy}q9oQMQYbf9Xn|o9pzTy33Yk%(1%zvH&Gq&vN@kE4 zdwug!6RIr*g2h~G?&g^+Kr3h9s0lDll>nW6O$AKj^?w~6A;f*Pv_nSA-_hLGvc0Vo zzn*iglSon{Xb+Mg7)lj$HJMyFBuk0J%ieDVZ&BNjFAa)1U9*co!#6P>uaz_um09iO zQY*0fR_63_z4@R-`X)gz>DxF;xhr(Q|`Q#I1l2V8su2DuqOIvPI{S3 zrI&h!d!@%T<5T@$^0)(b3}6H3OeWiD8s324x*h}cl~$STmrRg~>Y@47ynzQ+h!Wn zD(wF8B2j$G!DXBu-{vq-AF~GyD;J;^I(UAtHFI4QW!vRbg!u3C$t;Lp2k}|tq=9~d zx7p*%ao?ztiTs}E_jTgeeneVF^l5M6+32%GZ(unVr&*Daj5+K~>_PvXvnT!76(Ymo zL@OklQ!6LvxZ(t*FelkL@f2y2lWc|LYauh;*7a*GdaEdUrjI!@5H;hBoY~9d+`uI% zwi?I=t7EmE2%Fg%I#Y6-WM_IA0V-~<=x@sL?hv?#?%bJj>CZuePf4BNB(lZ+QyYctPQE*0p;VBrl!hG0jgFN!e zmHoI5Dnq5hy}@O`>iOQ>x%~>_%nXSYkmq~28JzoQ# zRU!ezR)YFD4^29v&h*g1#naBaqXo{@O}CV05_1|DM`F3zIW6?kq=A{4XpuaS6l}Ub z`|1^@bJf~Dmd0d6T2dMYKKMa>d^Dtln?D}Q;Az2W?7ww=@4(z#^xWJ4A@v30>wSv~ zA6BA?dhh=eg}c*g-Z19o^$2%>yB2-T&)xrD5FV0eSw+kr`TrHd zd+=0}I}o8Z>ST)gN7a7lEYbP=pxd60JSo563_ zM&w!o_?x`aES-=Vw4NV&mKdaGrFlL`lB+Ag1`bLBu9e4{f~WG_L(Sb3yp&(S?=-?| zA@GHMbEm1Sy~`MrMS1&{u)Ix=DwqXs`n#o>88*k+Gp9Bktx=1V^)B_k+rIxJHXuDi zi|44pVIYRIQ|n9W4W)ZNoc(5CgD&mQ>cZMW^M_%WPZ#eMMrs=O4%y3Bo_MrX@|7in zM;l;D=U^`O-?iwuW#EMn=@)6dZMhAEf^yzGpQ?POv)_xmr?=}s{8#uQ4CV#(TTJ)& z%x7L+F0n?R%^!egYK5tAtFkEcITquke`hODh|;uh5QVr0*dNj6 zp`q~S5dwzXMVn{H2)q8SG-j$i+`}MAeiCL6 z3PJEYoZZhz91%QqnNIm>3)PqMi-4bk$M_KYUH-#>pH#d%z3D`W#~H!EIrFp2$`KlO zolE$cig(h_6%@HF6X`Rqfa2~*q+hyX%j2S|$%ryn8~QCRxB|}OyxBeuLoaj%1qprb z5gfzX%McuLtc#{fL-P0MD|Po?zV>`LUiK?h=gW8DA%a?Q&q<4s33zFcI7#C-=En|S zZ5uk*(=GE>@^&#*E{LJ~FA^3nQe!14DalenjK+H}k<`sN;zt!xd2HfH%$TnxK?Y5a zTy>k@OAQo0-m~r_-SeU`g^VDqcSpp=apj^wE=l3ZWSsC9go+Q9_lb9usZ2axR^-cU zb)z`V$;_F>QGx?@o%k#4TGQaRFx+`a#ILL4FI6pcXhAuwv8*pI;>#n$RMz>IPN^ZK zwlpYFcoF(Jm!S&FDnmxl-#{%xd|f>1M0*R(aMT6;A90Zg;j#;c{6;zw{O@al6Z`}~ zOE`{jMjYdf5UACCu!zJ{fQ6v{a{)kEKLev4i9cM1+M~eh!MDv1VbBqOnZ13ysHIVKnYX>&cwB zj5-ZmrdFYiP+O{0m5B%<@Mts2dRRqL$0*_je%tVkfN_K zcuxeBRKP}f3}BOR)r$A}G|>$CGV-&WU|EZ0j%p`5xO3{mV~>u<-I?Soxj2E;Mx>pb zLt1to@|HYC*Xf;$Z7BwjTy z!4w&4<;3=fVEG%HD{I>7bqx81%XD#oT*6w)W(9GngW6=9lqhlmjga%#DSWCYknNNz7 zTMEhi5q>Uxu3fG?a;T@!#r@q+T1oGb;FV_yUTlUaXFk+%(CAzUx+ zD(Iqt;U67?(?Xwo^Aepzz%P@20DPYC*@(c=EHvuNpEhXfi6Sa9@2?Pft!(ks<*f6z z$FW!A49H|+VL&40=x$YTS(|&dWL{FGs&fphL>$XPan zZN#QJJYB8Vd9>R z;25}+EKE<4Vo_A85&`G&pD@fCS{k?2FC(|G+hI=_^t0rV8tmP_`U+eukrbKbib9i= zeBpy*gltFwi3th+ew?V5&S~Z>!JdUB+j%!gJa#F0HtaTKdu^B8cWdGv*>Hk@k?V&K zXvw`cooW^k2TyL*<?{)Zinxrl6!OSZYP!1Se0c@rD1GvPo~~V_FGbw z3!B7j!U*YGg}k`v-B|F?0Zp;65L%&4}rf0lUt!{ zGrbDdZVAiaRFalP__`1Bo=!&|<37FP+){YX^(HGi5KoPmn?j1~W2FS24GNUE|#xFV0$i!ueKLnIiXP<+X!2BDl6j)8*qSv%!J?Qyu zO?@6Uy^lF051M&-B>8J)rC%-zml!Bx1Q zLF)54pP^zXI`XG!aCG%x2T<+mvP*gbV~WA5t}O0uI1+f8VOrY-s@!VsyO$;7RS#6n z*5kV!KuJl`VWn!_mX+D(oj5-xyJv6NPb_)Fw}C?0j&r|45$=gFD!(j*aXfiu57Nf_ z;KTT;-|yg3)aFY7#fj`ia6A1AzB37 z_M4c|tQN-9Gc$yd^djOROd|$ExM>10Z_PMixo4Ct=%(UtjRDtrGE3*q;d@}}qbx=c z-@Yse2lTI28Du6jdP+?jyEaZ2d&544JM(yVlZ1)NKtOXo;Z_2$xb2h6K8lt`)MBd_W->I$`Nu|vD13G!9Of`%Tr6o?}S=a_b}X#xu0BJ z4~;6*bvrv>z}Ds3+pWX-HC@T$rf2+%qvXgu$VO%MQ!+J#t`w1Av6k4<9pI7jUVQxN#|maa^c!`LZ; zc?b#N!J?gvwX>g(X5I!vG2%qpDv>XP%A@cmo_aCa5xBDT(U%u>J=eV&!#5hh4x5y2 zO4H==pmxrIu~HkAZzYZMxI_G*Wrj~lR*{>CeSk_&rLWfNm)mSWRCS4WiRmztQlw_^ z49zr#s)<<>s>xDH4S*lV=Ci3JbI38Z+~mcJ`AK|gHNTp7jmPE?kNfgKNfMt~#pH|l zBn%Te36aW=9$S|4YAXG&KbMQPVB~fVC!7t7h{M(d95|solYWU1VT_z{YMXVR@M@Q5 z&Igj@Cm4W^Dy+rqS=ZE3VL@RngVj6GwVM$4SbqTv)3>g#sb`;KM~RbDb;iW;R8D5n z{~}oS=!g|@;1w!27=>buBF{tZAzZ~Ezle(cF9{{(-qnYumNgVf1X+??55+3Qsu(hr zlfP1kdY1Ja$qNL2*UFb3(g-#(m%v-G9>YqoxGV87N7(U4lX+HNf+03S7#X{lg%xgW zt}Jasv_mX8yGIV{I2N@wfS|esyOn!-&5oe9d+a180Y6x7w~oNs;x<9~d=owL?QiSV zHkEbZjElF&M?VEQyh2|2`r<$ru{$jCcQ*H{`E7L)7=?XH%jMc@t9SgI4sH>Yvo#<} z9598cuFr3^nIHo zeoyicMOV!m1MPC1ULq^@_HGrf;$c{L_;Gv|$3Z`S+PR~W6Ovc<;UYo!*nS_UL@W3r zSNJ}Y<;bALq6GHq8o4$ZKJy@mlKH2`Ojd zc>Q_e$@}>IDH_l^IcmwZHN^574mQ*nP>J}pZCOnc{wOAu7lXM;g*va|Dit2~A|)v? z$j|wm$#*XWm*`3}JqxkI9M6JEB~DwjQN=C|FO^aC;#!y2DpVr=Ft^4-N(z-3Oq(}O+iLXRPnE0%%uoQ@`pcv(J2a> z6_5)(k=eRSRU%XbyFA8kc-dybYgAU&u9V{;%~7mx!WeHt<9BUl6r0iixSI%*i3Yf# zpvcw$^q6k98zJoIdpq0C`{p#R*Bh5*#cSflf&`+Q*8#Q?nfn10;WAp0O`#9%*}RKD z+p|0Q|Nc;;FoQWZMh%4%fkkQEvpdTfo@6f`NF6(H-G^?AC;M?+j!WXBr z;SU!c;@?Kk7oN|Xs{o zP+;_Y9jP9l^BdSxVrIg#u&!ace!bB-5$#~~gt2-U4)8QW#pSp79`WXF`T6B&)QERO zrrvSWrc{T^$hXRu8nK{ZeX*gm{PozW;gOTn$#^GyzjQy{3H;E!d42I!c?~sam8G!- zHk8OI%WbUyXT>q>4mLIik^#TJt;v8J)@4Yoa_Kscnhq>*g=IT>KO+%25(Atx!?YbF zONUbsNsuGAjFvQ$L;$$6xbr~+wv!}<1_Wf5f%X}aD1s{&?re8}>ra3FO5LOzW3k~Xn9Ul~ZKD4A z=TBRU0jZuE9-)A&kTSQnqR0T(xL$Cb7fC+5UE0ZC-dKx+L2YD&rU<_b^@v|$W+yo=9M5?nl@QW)4?{IGuW5fFHa%sjt!{9ZS zc5CELFiCf6N%sA_#4u3?J?Aya89c?sky5swME<}HqF*N1E1tdoJUq;!N_qHdcSER$ zfeUa1kl7Y=RgVgDV44kcj<90Cxs3%wsh70ulej~3Oq$&? zq@`okqCwmD;!g{VP6_wzuOlW!ezbp!LZ<(0GUjvyj+PcsKZc#VG3#_uZS6Rx6ir(n zo?TnnjD8l==eADhW$&}lt0t+1=FUt$`;WeIrvnFWIehkB=InW)8UcA_gAfs$zcdmR zO{uQo$Vkoo3#9c%Iwf3)j+Y#DkOq~LWy(Q%%Y-E`{8>jyaJjK}_dBY}Nj>f%NLkcW zh88NA*V~kqG99wIfc7=ebA~hn@0gocteg~AxwY49R>Dd?&R=j@Um7`CsOp0Stry(u z0`}g3(Ds#{|LP$d7R(k_-F89vW!9eyw5Yh-k^m1@@eT>~_R>Mg)w#oHP@IW#2%9|) zV?g&tMn>Cymhi8-W66h(dRXi@2x z#8c%+~J=aL((M3uTq9%%2H?a(dh*7>gO$IlHTz~sic1;GdBpqS~`5Wjd=J4#&X|4X^s z`UQKbqsHia`(0dd*M671WAyV;Ln9uZxaWumU`2gfjUJ~*BqoHX{t?TE`B4YRl>fy> z-wDQZTI*(;$}^q(t6FRX+T=f~g93BfDwX84deWJ?(eKd5^pN%jb?_B{UOkEaJ2QCa zL4M4mYhzX)`N+vtM-lY4@a%Ox=AO*?x>WXs-y={R89-IWfFliSD}}yzWvd-eP2y1=oB58+XPnF*id59VukJPDDx^S>?#wbGEbmM8`#?s z5^1B;suNWl8+5nh^AQJe;2N1ol$ayOBY%6+!sKQPpL~mc`$Q#7N|6Y5J2=6S;;y1l zUb{&IfvD);>Qf6n$5r?fxo`EJE#*!7J3zU~_708p0Nji-VD3C6aVxSG7B{RfAgb^Q z>c}`Z&N-~ld3sjd&eDtyhy&P3YzU!f@@lGd`PG#r9F!UQU;c`SKo%UAm zF$si!ZJVAPBvNtJBnsfYFbs2W&M-X8a~iDW`MN0C(9@37ML`r19S77ji?Pp4KG+MZ zU<(q+o?MFv7v@2_kDlYtKZ105!b=9cZRL<07j0aF2D%#Q&#EN{c@uEkxd%JG=jLW4 zH4O7S;g%@>Y^j*zb1SzD|Bw`-+BN`wAw;{VUYBHaPDfJAuUcE^T9wrbAeN~{k}Oq5 zZ=WPPC!*muOXGbvX_^2$iMyfrlG_|Ge$=75h4mriFgn~QXfFOgjhc~jytP+HPhG-Q zy_lyHg(_M*);Dh13>paPuPA*DsnGi-cwD^%=7>$Zs-!$yeHIbrlw^1}g9Cs4zS&AQ zoX`22$Ms8{`(}9VpRLUHYJb*ukbW|1p!+dNqty660h;3)SQCziC%_5S%Jl3nA6BE! z)oU>tL80dh&vESum4`}^V)q}mW3T5tao?l1w$(mAa#3^F=5W=V+v67r3TzxX?%2F< zv|+`@+u5edzkypGF{5N1UP z`8mY|UGJw`3G6w&MY$_Xcux1W9h5|)QJ&w|f> zru{Xm_kg~|nobrh0m*%-<8w}I5OZn~TujQjQ|sYEhdx#-p_%t?2M3{*tu9b%nY896 zeauDtGy}D$+xNcLdMbv7S9^tryL;E~>#4O8GXJOq1{Bxc!$Biy))F^#tuChMo-D7e z%XFdr;%T$or2UD3-p9^N3zF#H7IWP#n~Rz{H-XDirBO-7C}pLR ztnPsMkb@)=-R&hEQmYD0=3&^Q=)kRN6|B$dF)Lq*g7-1CBeoGA#nlv0X|hTdy+FqT zv0ga^IHSN3roVZNY;&Pj|J3%9_3kL;Zx3m$$i)MP6JoPMg4b|RV}Jb;HeNkqC#Y9g z47qxHxrz^DU=%nM!jdG6rokF|($~>nuRahnDXSBtM62Bx5W)rFT34-E3b?h=CW`nv z=~I*i#5x;FbYvXbLr^P$DH61OByJO*!J@_j>EZs6QJB3Na+tGnie=>J{h9t=E~Ou+ znm61{K34$2|DEp%?1fDsuvZT5){y4BCf~*;GE; zT9<dBu3D2eOnEX4$VgAhpjh2QGOnew+B#Z-kN44>O}PZ zkS4FslYDBG;@CN&bGVwRzf2xRey>^JENH%u);4LC3|P$qK`5><@V%=N z6GWP48rHu9{M2Hxq=O)a&df5_glk)X6+s zJyV^%eIV78I-RHfZsHs;-Bkn{s7u)3K?At8muo~`LN5=bJH`+k-DN)pjOkD{6b>QK z>(|9KMMi^r!Hd!G$?8ds7Ewy1N(3C7WHPFZCaZ~KvR0Ic^3r_AcIzPBC)UIPrp-B! zFd$XwjO>ii8LPu--P$9{&1)sYP$c}`PGInxO`P>33t;9c-(AD1P^^=rpJCn!uSPoM zLmcotxmPQ*H-+=Np)u1m#JZACZs~>9ROCR}CCw=N=Sv42 zc@Rv$^yb4l$h&vGu^XwfYvf#jU~N}Fx_+U=(NU$3S7t(_Cq+c5dre(l4)Z9imXehb zu~gc^P3Gm(Qu1%+7=(eL8dK2Nbg}rQT^`5glKh8RBl@1bu^1?gsTGy2N(6M^WJU zI{=WTahTCs<*O~JRyKJC@tIMtox#_9k?#gB$rx)InPJtTz7L_hNy`qpi%2UBVeXbx z%#yy!!s>nL9#3*JuvS{qY7Io5I|zUX4i~3zpj?hp$A|bjB`22yiBsTkQL{K2&5bYG zn$*6ebA#M;OG)ipi*n;paWtUA+=2ox%s2WP)d@^maCju8QEj4bu3FEr;Zf;N06d&= z1@pzObY=?SOBmuH_(mKT=5BKXehF(fMq3N2EGK3P+oosI7t z**XoBWy)$Lf0YwYR<;@7PghS@>?8X?Z=7k$Bx4J_)6A7qekm;|-(bW}&z7#CDD$eI z)eSouE9QsEcEiX(*vl7p0L)J+)OqTw)HrK@idE#t|IXx_?I~4t<&{C)h@0v}YmOJv zd*^E2FD%Pt0XbO$9j!ARo;YUJ&-<(s2Rc2~=}E~_&Pr}aRc9J@CHr8Xc*d#~$Ezoe zBj3tb=2esz>C3CN9Z))rK!XOX12ck^?1#O}>a&WOZ+9lc#IH>T?0Di=kp+I#LFu#%y!jn;mxdB}OF`8rx#t5z~#q-&&Q%b#Wt_9AjP zp5~5*LS!aQA$W=p{YJ4_Y?Kv7%o3?_^6H5=VA9xTqWVmA#)e6zBpZ5H=5L@b!Jq;N z)16?HyoByV(!rrsCa1oN49=9!ujYsI5&r9?*)~Y{MD;`kcq14LRtY}@kH7ve3$Nr@ z-+^Gq2?HO(adl}e8@MunAUf(vVF0C>%8n$>i3A_S?Ma{|z*s{6_HY$M8nnq;Up#_#B zgdRi1tE$J_o?{CfC*sbXyk4cFxoaZ3z-P+^+CuO|>}f%xJhk=q4qJ|%PJ|S6D`4Y6 zI5)eB2+T{pdOV*KMEB-Huva(>#MZP1t^{H-O8-B!1o|3hpUBOfb+cjw}o(f@T_#U9Szp9QX-z;1hgZL@K zR>1HK&>1MmkhKU4Sj8A})Og_G*}Vn1Ks1AFkcTO1irl*6*(#CaYXVFS415E+?7r9m zF8Bw8nMdIa3;jpt5PRM7PMFIfvaqd&j3OsN%XiB_!~-7fq#@VH76ektH!_ypzvuzk z)jloWBHFL^5F6Qg^tiiXBnvvTYe z|KHx>y~)!0FTg8!huJz);p*dKqz080$y0EM+ojAHPU4hN%_%UduZ(9f8}}KSEKW|N zIGMjKM(S>#2b&nT+CW@B;(6x;L0|9OpBUw;n9f_CHr&ugIwyz;)K{QZwWj3z641$bM<@+|oUkB{(mDvp|NP)bj#F#JJL-zfoZ+)H$979bmy z=f_~C<})&~!2YI+(Uh2h28inm!T=rg7NcE?!uG$@hH|g)r{0(+0V?Q4Zs>e$%uC+Ydl{NL*UV3 zdUD}ISu1q#OvrGXo<@xo<-{p|OqK!PUlI=$4|r5{Iuu3cGRh-Td5y)!wjNWgD0xG` zkt{wMp2>s3UK(#u(DN)Y!)mH;k12>5yf#*|y0Iu^;Jpu`X*?;Rne%1+^37yN$IStv zsF{&{+~b=G$J5r^GAU>H4Nun{`KSD+i))grXjd<=u4VAAa97x42>Z8VaHfx1 z$!H{T6K=s}-43j5X}OIj$Xmk}6%LKMTK|mTYiMWxvK_=aGfzJRH8H`A!T8w_{(==h zS8JbQgX=U4WC$l)HK2nh=g8zRQSOd9x8}9M>3Q`@^)wo8@gOTHkbC=C>W(j?EJ!< z;FMKzd`XQtuEY*Bh?!UG297Mj@qa9_bP0janLS{a(T7p`&Grr@W_;>?_=s*TcFEYY zai>wgZCm`aa)pVd6;j*6J`s649Cin;*+D3uDR-a9u`5E1odF#&D=`p^8)O?8^-6L0 zRhUU(w=bD9rqb?wh@m6uF&i+|fkSr4+9jr@?4PAdp7`6TZ$YiNM-y1}-Fj4aR8#C> z!dxqR4d9bBT7Cjmk@KpFMNNk9VVINatN+%;|NH7I*inCD}x9_6%nTrG08TXJSKyVT>TaUIjk z@y~ul3%s~nBbFGzshj+24@@i})#RbMwPR!c&~r7`beX2Spq4Z9EZ9LOo%;q$POL4<$MnM#-K@C~y`Px3B~ z7F_niU!xrnyUIiCA6DcQse1At;EljnS4)g&eNUg_*?@4&uEHjt8OFSB=ps0P2V;{; zl*ldqy`Dm5Or&`HedwJAsVj(WFqRsysJW^EUywPw+6J)VBs;^4#lfKAY5VkHXSy20 z-JOi3V&>Vb#*^o<)P&E4E+BiDhd1!h=!CXZ3@hAz0ZSki3-7dl5%eoR#@+M4QSVe_ zQ)f@Dm_EO*5n!v9klx9NZtc6z#tI;Z7Y8pj+tJ+q3}sSb+BIX7#R5UVfsGX<5WJze zFb3N0@oM4cyFbUkgAW7;<`4;1AlQT2VIKSexxFa)feEu9R&?cLv5reF*b!WF7r2A_ zBPD+oDT6f;Uu{tCib<*~7V#Kok7U2>)3a#@=$T@eel|Q?!;YZr!$2E3zX>}Qz2C&R zjNixG@)n4gvgpxG03>?&L?+?06;z8XxgDB{id59-7TgHK>s2Qi!y=M*RzqjrEu-}R zkX|ul#U#l$SyD0lFHMiPcVnC`a+7(6qD9;j;TiYi%eP;CUuE1*c7sajsSjL_gC98| zZ=1QX8gOujY)2}Y_{M2(xD`-C7S~q+XV-+(cydRgPdSEw(~V>ZniG`C^`p5djYy!B zav>!(OHc^fAU0%0K~yjXN17%^Av4~ND_Qt!ub#Biln%9)Rit@z0YKCZ#>p#@o*Qor#P)M$CX z;%gZk)x>PGi%5#^KL>9d@Tn#V<*K|WoWw>;xST9i6!dyts!X^3I^{hcvJDaN^YYT- zgUG|G(2$k3X3sZJYEw0Iu5Im_5h< zu~$ST6v8mzsg1j1hIok^mpsymVsK;HXYUPY(%WFoY_%7xXqVFZu^8BlA2YI1y}CBN zAYYBEZD5gF^_jtn@)_8~8mX;&#w;~@vUAh&>CT-#QYOwDUfQy3b#GuRU(7^w|IP5} z(?mAZ%=Eeeo7Cyad?xNDm@I=%UxT`rAOC6B4#W2{97DubTXVrBG?V(9B-#)t^51)g z68YW40n_}%b&E6{*B$KBVJ>!@uhfx4$8uHZ)veMa7QLRh>ZvRG3>z?L2;MU{jTlws zxdkz>&@f(jZd-^~s@1Aj2R6(@qXssJCL1)fd*-uNE!tq(osr|0jXL<)LiDeDWKfi1 z6sH9KL(hzoeE7!r&;!XD?@=i~Wc~sK3PM2@jD{Y`X`YxugbKqFE<&Uz(b!_dMmCY6 zcpM25CE?;pmLgRezI82G4oW~sL`*^&RX$!aDJW(2shVC=LcaFC+$wR*B=Z=Y3Uo2Pa&33mx98c#9O9J$ifUA&LC{0$S zT1~jWN;J|wBBp~H5$nQfaiG(?&p1w1IBkO_&FoMuTD571*=Umvo3?D*v1`x11BZ?r zJ8|l)6VF|^bmbZV1c4z?7#x8_g|~l3poFN>!a(_Ilq(2i&H(_`8B7+N!{zY>LXlV^ zmB|%KmAb7x6X9X8cJ>OS)8og6;{{QY6;;#!5Qoys*LGab55g!;(kw5^swO``aFfO1 zJV9Micw!9vlb#lrrQjplhzZr29sK`Gbqp`(^6(=4?f<#G# z5yfm#LXJLCG7x#FJS)c8V@*vxV(D2o){hIbywT1|h$$~OBna)i*-ddutC z4rAshN7&C!)&+kNDyyh!!(cqnD4mDM`9gA7<4d{d%l0_+>hIAx$_$IETv1t-*-sDn zIel^jeZzg=4d)_`X_bikfEQ9o-+$IjofbkP-Q8WzXYInuR9RXD zY)^vdUkgH0&~^f~odw_qZ0HsdARz{|(m(AAj$lIpUG9ep(~)$;QmmI1K9jh~2R(1& zqveOv1`e%kf&J9hnH;me!zt2_rNB`=<{`rom{^4$({z|4sl>2QTWly*fzcw>G#w=& z^oN^~vuUH>HsQF~xyi=0si}ON8Li(t_k^Fn(;t(h#or8@7DkpkU}m{gq_Q6~Y!<|~ zYzk*T+|0sKHuh};ETU%4TX-&8IJZRiLY7EIx9^T^ton0P;Z6gWmCh+!I{GiZY}dK5 zX`M#97EZPeu*p92-p2FU#`z7fRa}^++eQA$d7HlNLe{Yf%H2L2#C)HpZ12+A zb}w2cYuofb%lDP=(iYtB(|91rWB+c-v&b9ZQXSV!kxR=g8nTCezqboQUo9B7Vf*#N z35cK5WgcyAX-;ThZ=3fL&XRumw;mzYs3Z(pVP28o57ihacUR7FPS$*4%D9YfF~}3Y z2LKT!lyM=IHa4zuA%qY@2qA><=wboi|t&K6GnSe4bq|(O5th7xoAz(NpgRA>e?u3gr}P@1dCk7vZ|t}KzOSwy^_EZlXlUBQgfbqH{s2Un zP{t$fGrovTRUtz$xR9p09RQ+XFQ~r>0ZiEp@&IP|W0|7_y?o$uUT(YW-ibmP7qk3r znWOZ;Oj{|ZMI(A0jfyoS7*(1?9#Dno9ET+F4>v?4(=7FRIe4QRCi7} z=n9otjlZVKgW^^hy@h-facw)iv40z{-yO&)2X$}qudTJ-`k%{NB&ATtsS^ns+|~%3 ztXI`E^bD^7dN4L5E|+c$TO3aN;1wPLs&|!{!pn(H->Ak7-8fX$wB!)>fRg)Ikqt6T z1ThvWaf7>iVwMGtS)m@}jzN=}e=+S>s`=(0Vj(yMqCrDrO{P6`AAr!QqhYOLAh%*X z&HAa+$aidFjM%|2Y9wJp^hQnNj7rRHbeHDaDOpnt$4etDGBM#tx(q*tx=wW)45tPF zLBqR)%f)2ap?m8GiYXJ2C@DaarQ}kR?hgr1*j;llxv*Gyz9x7s>YB1GEMdT@2B?U{ z%=G-4l>{A$o>){%KEPb$4jH3Tf%hp*5AaznZhxg!JKof+A;y^K;y~JPD@m}S`kdpMqjyTO2t`G@nDld}SXSTYY7!Shcd1B^lbZHB z30zwUR9o>L4<)8?$u*lO^Ur88E~zt7Nc7Pe2_5yW+YFjwi|1Z=>5aQNiRrWQ=YO2< z&WpD{?15T{Fhn5^X~;qzicp3sa0@YpG~}TOWvBytTIMjS@2h^tWY1--tM{jgb@PF~ z{_^RS>K^9%;9)UBtfqszKYzSvsrXX>3FrU`07O6|KnMU(5&*(Z`}PXZ+wFJZ}KlLB0c>J-L&1fQM%Y1vJW(kkHK`mXFG=rHr2SCfJI^h z7h-wE`i!+JHnCV=i{%-cc+!V!{ufe*+tETTVGF->-?xiQ6ryTQ;wfW9DbB|#$_K|PU-u)c-6`@+Xi zSsQKO7Go?>AmYhUb5NMV9Abh>^AKQZ9~zgdZA_sHk_1<)fTS8P(?D-i^dk9bhNVdC z60l|MEot5*b0Xb;_#=T!BtR=N@I{ZW*s34p(51xEEt2YC3JwukAiO*2ae)@_)izeq zJas^J`w_<1x1n|iV$T$yHRUYK21zTfpyCg)=jB-Am;zfvmJAt!sKb7?)nc|hCP#~JeTejSJ1R>Fa9<5uOlg#>^RR7^sEFoI&* zjai-rjEotl;SW#%#$LT1=K5AH7yH8DGFRPC!XFX(7)SzP8BFg$Kk&KYW#NPy(_@ff`eIgYpX$yFmd`=%nxg zs$VD%(6S)_!qwS#O)mYQ4gF1mgPIFy$CNHZrkugGyqu_mL^;$%I0!k=G7i7sW=4*n z;1R}QF%IA4G$RLE$zx35aSs`XtreWnBR6;ONo&9Z5y-&gmsI0et4gwt%+6*qG`rd| ltx^p(Nn*WO^w@L;t3h*EBaV?&T^hj;^KcI7DjH1y007@d&MyD} literal 0 HcmV?d00001 diff --git a/static/css/TTHoves/TTHoves-ExtraBold.woff b/static/css/TTHoves/TTHoves-ExtraBold.woff new file mode 100644 index 0000000000000000000000000000000000000000..43ab1969a78b6d653fdac4925f11662a098d7cd1 GIT binary patch literal 70156 zcmZsBW00mjwC&rrJ#E{z?P=S#yQgj2Hl{gk+xE0=+j!4>_x?D4PO4T?S;e}l7IDs zebo;o!d#7TFWKNw8d^dJ7~ z-vh^dUXa!d`|#4u+x7UfR)9j zu>Z=n31gVA6F&vg?+Q$xeNPX}BuFxTNBxNPf&r>8RE@-oWC{oQr9LvY3FT=Hb6HFwL- ziZvCi6)ZxXU}5@ZHg)3Zhc=&so!lf@4Ma%`#JChhbNvHje;PJ5KLEWnL1!$BOHHpv zhwIku*E8!iGrh*X{7tI=df#>O_4etP|1xqD)*WbYpJ~{8$iJKRhw{zFvdlu1Jr}8& z3#Gx8+jRVRPOit|RGuNU-_;vi#vVF&yip=N*&sB50RHK4TuF9RlNB)+zPkXi!L{ni z%zEP!_nyDce$KFq@jLB?^eXxc_Jq*n6XKMtZTb~QF5dAwcK77=naP(|gjK-I<>3xY z0ZHWK>a*vulnGvh51;1D`NOB9*O$T<&_gJ!;2CBC~amB7*Sv^ZPl}75=U0 zikgqCvN`nnYLol#l@O!s)k9V?Ly^gGy(Iq|kIBQ7k#0w858l;nOACT65Fbw&&&yFFQjX{Re_bE3j8im{I#v90Y3QQ!>n_e)dYgo81 z>ON1Z_^_2d>#=vs2-eUREKDx>7rb_S((4cRxD=A$lb&46sU%@-X(S3}Ak2Za*wVzI z;~wWnA^E5>lL)%4+dP*0#0x2kjAjD0T5}&)uE$p{!^Df!jW>$07#ckWTv&U%%GTZYp=rCT1cHFCai^++LS3JF{5J~wEW zQ|_SQYE0WlpH@Kuz2}AbLNy9+!dv)j8RrqxDMUPNFmfPle3H#}ctE}a-kT*U^D+Xj z?sG=p2sA8AC-XeQ0a<4|nWWjZG_C!MvP$CxIjzPm4*OsjS6DBJ7Ad0YzCC}tg%E$% z^6fKMK0$rTo#pZ$Xrx@!zregB6|M|kK5?!)68b~yHN0tkqCFCQ#A{9=l=0qmrqd&- zmlS(qoES{z(Z7R_jPDWaBjCSa4uEFD$1zJ{&#`g_&H~pK|`X(MB z$18uDpD(Wt!G=dCTRhCYIA`G`F`!Utz;e}?1+)oXcnEWE@wr5uHdpR0OdjKd8^^d? z1!f=FExcOJ?$wiBwiPdj%h$2?KXhM`{O2z>r1D!e?Gt7hWsuH@lbH<{%S>YhnejPI zEyi(sVF*WMxh0+x;|`+_u6K%N4XbiElhAx{9 z`DBi9h=2+9f1S6Zy7yhG*TVEuWjkC<6Bi?DSgvxEPfakh_2AU){E9^xlQj-)8d~2v zZU4%{ac>daD?cO;FwmY#em7mZ3c7qu|Ly;re~{o5Pan+HWs$b;49j5_(nWGyskC1k zBFMI7JWA;(6y!rEgdgOuW4_VhR$UCaX%$S{ba-(hZ^!?|d-p|rs<+*XhMH47$dq^7&#B*t(k^XvH{~(3vQouu@1hNj-r* ze=?SUH(T0euPT@1vPfYs&VyR!JiZF5yT+z{WtZ_`YQJ;6o4AMQw1_b)=Moe6TJ*RO&blKh#v6JcD&RyYEu4?`@yyYn1b(vTmp-kDP6!uJ%j%N2wF} zq%~es0e?K!4J*?z zRX0@~gk20ffioO?7b^NXj|UIp2oCHFE)|Mb^b&ICJ>_Zj9m4EjZese3Gb|2iPx`bv z@Uo0RQ5?B++FNGp`sC5zna{Z8%t{UGu|+6+G6&PkbgkZXN;ivv8&!KwQyZ4nanD6# ztNES5cqfbBd80jdX!_;!S_ETfY_3Ze>kzKOS130o(-}Slmty93)B-82OE1#b9m&<1 z{qaMCkP&E8Xa-+1>BWy6KJqsh=d&{5<@R)7cdB7C`B<}a_nB}U9{4Wm8|odioUZwe zmTu19aSOOnFUgk%fAw%S&md;aT%U{0C!e$n<{Sd;GXu^xpY({l>c2YU7}6i!mya*Y z5qc?JPOiR|<+eivvL*w?*^bCSOI(W<|2OlC8Hho6@$I9ci*z`-Yu z^7WMuZ3aa__clq7oXN$8JM&-J#y{v-pPd~0uBWR>Co4X#*&6U#TRjHv&ts17(vVAv zaTX<+a`9XA2)4m6S!-?!`5N8eTz;@0}E7h=cN1HQ<@&lDOqVFVX$6gq))Gq4dm zdEWPJ$DQfT?{#!9*;no!FWNOBf@j;UuZx*HgtvES$E55?+wEf(ud}Z37qeV4}9t|Iv<$ldbltN^v8I?Xtb6FLp7EUH7c;Wc(-$?RHY${7$NYiWwl1IDL$2qO( zlWfzEg|k;vGXkBgogW*SCt7_Yn(?Acs3Tge%seX+Y3)OZpyQ22$UnrQ#xUa}Am{0{ zL#R#6%IH$6Oq09F>L&cMo2*)hvbQj8W&c_?v9JHSqQ2sthL3P$<5A$Uub`%p;7+Mc zQp=i9d#hC9FFaRE<|$oQ3-2i4QA=kXz&TpV9iLIPh;`|0R7?L=ARvvjV&WPuAZk#u za(;nYgvLpUNRcINP|BD7gMXFdb$a(Dk4dqMvY*xabfd5*n|s^Up@nlL`$mtcI`epp zm9Bf=jalz7qC@#BpGDjJvj!;gZHbm9;HVqlqFuGfVb$482ui{0uK9jJBO+eKRVwks zeeh0iJ=*rY*(TtuYp}n0!Do5~M(4XK_-I4#Yq#I5{G~4I?|OUDFQ8aKxlqhEUi{AH zki|KfeK7qnLpZVk=Q8#=eaT*zv2|?r(6naHz-E!Dd<+gm0G@nf`X~2p8ywX=YP?Q< znSJ9K)YPrFE@&Ln-X6V~Bf28x;8nO1=7|=$O!T(R1%d;U~i z@}OYjR}Fy=e++XCbq&*nkAsQJTea%7zG;40ty|>{9@3Qfa?mMgiiZ#K?-nH*CEf7P zVQx_&?W-(=0e6CPr$DteGoeb(ki*^+V+#E(hAPh1iYi*I=6%Q2Ha($9$pb@{B*sF$ zIm1@Q8k{*rlDbxsr~sL{*`I5gIF08#W>%|K^M=k8-`SS^Mpuv!x;fZQXUr^4XGA@z zslZI0@>7BvL-pq{ta?M#M_WFWXM(kL#SyW(&Nr4<$t&QlaEbv=g!HRsSL$~9A*jYAeK5?U)$Bwnx7^tDL=ZOdci@ERRN=NvS=gAOXp zR9n>2BDs`@>IzhhqC>{gAzpmXb^ZY(In)SHfFX&9h=?dD$xKYb80iB#jC5>FL`2LO zax9lZBp)j&DGMnpsTd16D+>v!2q`HE8Hw0`6w3kgN{-7^^Kp(#E%!;5_sz=23kU!( z3Pb=%qzk9lr~OWgWyl5x4ZtSC$bdoz$Of_);?IG*f{dn_q^YE*(>rL{>kEL11VIZOKH4q=#7kq zdRLQ`5d%#c^;rB>@QtDMlC8s4wm7i;BqwezhFZ|_ez$$lwwpFW=>RejRMy{Y(y690 z&Xfq`;z`GMOg(G7!1DMyC z!;hlRzfE1V|Dnjg1;OL7s(dI^f*|%3AUoaU5|l&JmLgE;Th(EpIu4J%K$9VNAlj z-0Z{*%VNd6)%3+o(F`~vd@|<@&fSpnH>WlyFo%%0kQav6i`T|O+dXbki49X;%%%uo zPQt3Ov!IizGo(|sQ?B#fUH%Bv`L6v^*Q=JSx@qgJ^TK_@J=~pf!O4|aPo}Q; z;>`V>%GV0X56MRHAg`Za5Jw0yPS_#>#|<8Aa2(rQ?h4{+_|oh$_1cX!P>w}qK$S@+ z1G6lidqj6Nd|h%ay;reAv30hQaUiH6Ny|1Z>SzaPHNP>x*`U7H1~~R@V2hqT!C?e z(`(3&%;%63Y0~6&!b>|Z9E(~Ay#)ObD*|=`t`cquq65?>u-CB3Fbip~_|3|Pyf@Y1 zIQ%tHM2NU7K}}OxQ|FW#k5QK{2%{7u6>}M5ElMJaBAx-+3`<3Q* zrP`5tsH)4;?Jr~g&YmuPJ%`$tWuSWU5}sSTc1=qK*m*v}9HgPBDnAoC`tdW_KJ=_4?65LYGb$f98n5_&e8M`Eo zWp;=xifoYSTXSU>v&M$C<jXxqO# z%rp^c|Dg{`TgTLyw45BM8PX}z>Ch?EQK_k~Nl`bd>RDd=(S^`E-Br_L*Z!pfwQP3Q z#uKSf^KemiZFuc?qq@bsiPOfq0qHQ*gxJ-Pzl(vvO;G8wmIg zxEH!FI1c>kFF`RB`vD^nsBEo@trLS;yn)Rz!xth*CXm8s>vQVOb?^8WoMlgrO{M4e zQVG+VJX(A}{A;{w{AGL?;dIu)G`ve@`}8FTqgw&*7=9O`S9n=*_uw+hWyZyjdjk(G z?*^|VVKBi7AEw}6K6e2E|1L{mor*LP*_zTxmN)PZv0E|V(2HpU++65Ikz1;pRR?7L zKpsopUjcT3CqV?k+{O{@NcEQHDQ%l&t(w1n7a4AMIOjM|nY)?ynGae+VGZ+kKeOjK zp7CD!_PF2LciWHKZ{C{l?aDWXpS3%;uj>_i`nn2wm3o-E9D6fd`|v%X7L~8&xN_oz zM_oNcINNpR^ww&wTHHQ5Nw<1)peYB(JtlG_%2#llXSzeWhPq37>AZjYo%!K73F~=c z4XT~}?A)xiS{bNX)p*tD(qOM!H}Ep>^$6a=_Ndi4Zs`cV-l?mvYp)xwt1`&ehvEYu zjU*pY-;eRh=P4-ufm#>6*n3KOqVk^N@5z&9T#~(o?Y7$v@MytWlRZCrRDW)H%JPQt zws=SO<@Rk5Fxh#7vBZ$^0mtA zVjb}&vbPkMDb9~?`1hRq%nz-*HC%9rOkw3e%%|`QKGNSLZws%(kK|17*b3Ok@U&ss zVUoR|y-B@My_G%dy)$tMlIWy0q}z#OoU5%}4o5pFJ9ImKVSR;7$bzbv&Y>gUChv?a z*+Yc?&WW4dFg`lVsZ6yWd8Xs{B$MlOj15N?I`<^JX>_zs=#&b07R=IesA$GgDZ@fJ z!y#NC^%=wRj-U)f2pAwL=Y>y7eE3V4{^-7@_rVK4{X>~*dKa3gRmSGbwOQ=mUc-{g zUf=v%UmmxU>2S+(-+(=2?p<9T*X<{H-ZSkMot5xvtun)DOcttcl>wFF&H8S+=a|a^ zYWe4W%gW16sS_5DT0VaH`41aDAg}R@9lL_PivwCdX|T0y)0c*H$3F!vTHd=rWHVdq z3qilEa4I*Lf zbei65-)(Pi>-3=F^cEJ3<)){Vwyr)zO&fSO`PHm+au8@XynuPmFN z*?mT1gnGaHm`c0s_J;H468Ls>Vg^P(%Ejtu=OyOe9ZgIuo0-vI6kDtOR%J~XsiwjI zi9S<~*r&MOvRc7!9zR^Gpx@0CnFkki6L@mxqMZuMzaK)_$2K6y0@~w&@f|pOx)nMO+QQA|e!^|DJPx4Gz9A4eEvFc3^DZaxOWP8n=|j`V2783cb;8(e5NkyoUY` z27<9C;OzF%d`zWh=-H-LN|ZLd*$|UhI_<&!1$mVhH31kO0WfPpYI=ul=^<}Hc&E5!ILsuWx*h%@M~0Ub@CcPZ^bELxfSAAYw3 z*u98+f=gTcOTK@v9NX|eqmvvmSQl&Fx8g6izaoYM{T!w5+40iF4_J0W4=}4XDyT&xUPu=RPN9 zeFm=Z>Da??iI{t|cg`tms#J{6{dn@`jI1ZwLtJR`koP!7L zu}v^(mdE6T1dSa=i{;#$Mn1Fa31K3asd)z1ElfndiyfxrB65$`lwi{u7S z@ka6(`Zz7#Ce>B+Cj7Nhd=JR0?_XAfC@>Y~9e{$Eut^-#B-_Wq#SaGFVzc^}cU1C3 zA6v7&Y0jWrKnIe1`+^?ed1B)U^}-1`9fEAk#>NfgLlLCC&t z;p90DINi_n&`8P!b0ABG+u@D~U-G%lSnx!U#uYKFA_e873=YHTXmE?2M%*)Zy>u471C%o52k z+uY{tX(;qAIynEBCjq4zY&N6^21oZT{T%_itZw2HfEgS)5FiCFG&0LEia7}Sv|{h+ zPYR`hfqT`ePG;s{mj9KM2DMgyrlT`8{-NRI*W49tRvxII zf)BU*%dyfr70DUpM{}kY>}Rtz4d0L&a*AnjpOL|rYh#1eK5iF5*1~H5bG4a*@W2-61twj$=*SJ6IlCCQs z&-QZYUUS8-KB(PvaDw%-quXK#zEKB|*(WiQ8!-|w0xx2|ygs;BVzJm^xFzJ(Y`hTg zeqv4Yt~jYWbkt8hschkqWP40I4l2-t`bfxL&J2TJ&xNE=@lQgZCK^}$YT^(I4v}`F z{H^wFR`u=GV~9q-%;Oqond%1avYG0Rlx9NHv6*FaEDYkUuTfUTVwEVuP8zeTojE-^(PC zaGV*DF2o;w!&VGnPe;-(Y91OT!!b)HT=4g0OoC8*ydHxdbd@A(+(|YH?PPnp0wFw@ zoUcHl)$W6Ye(S~a)hQC4>q30A!MD=Vx541k2O<}9V48@*Z+?3D!PBUmSaF@___U?8 z0;EdC-EZz-w>lwlD4PRot*K&1=!%)8-MHi(;P*KBhXyC+ALiKlw)wS%<>iGg9{Q1W zT=l2(9dNjvYTb_78mi8S#%n+Gmv!UOi8wR0jQBa9yb(60xwXTg6J+qSa((|VRY}UA zmGPX+Lw_Fb`h$9Wsk(s|nq6<4SNiB%PH2H$AkKpezl(X7cayj7y=}*#7OB_55RgD% zg?GYV=H#6w$Hr@4mpPn`6=ddyZ9r`T(sV}lrJ4QMW~Q=H=F@X#bHEsk7m&tZvV3Sa z`ryu}-$^EjAGf~cbYxIlISsh`C6@uV)GSd^y1Qd|`IF8)lAE&Afv-|4{8eMTcaSwKMe?5aRSsLFU$<%L!!nNCClw$DMWR&a0u(AtTGR}$J(12 z=uK!S`@;<{$L!+C2EUdr2zwof)(-VGOIZ3RAM?a+;6dUhSI{P|AL(_(>JL;;eYl{! z*-Cx}r+Fh7Ha&3rr58!2XZ8m7^!&%^fU19T{#0;G%Lh*4==I2jLkXX`LKP%t@tBzn z5}u%+q37J5JMV6s=W8ocK1BE~2e}Cl?13R|`7MD~SIVzouaK_+Bz=}bGecdXwX*1v zOyY11rpG~eX*YC??q;)O@Rjdrn%@B8Qp7~`q`A|>V538C=t8*~iGE*UgoBQ<-|k@> z%0ZXaiMf)Hp`wBc+kA%4^>&2!enwF~ke4ZH>39{wsXAc;?@=@0Fz;UO-|zWM@g(zn z>?urqjofYe9{PTDGCbKr+NR-#z3jq`4L^MP*=13$2Z5A3;=)|U1G#)X^oTJ)2okYa z93r?IWz^CnQZfg0Jn&`145rYL173>mPJZ?X>F5ByfXEF{#Fs17wwU*jrc2RSl;P8Z z$yme&LgFEE9ItqXvd_K~{)gP3bo+g$ucqnk7j&N)c)ieqgD(Ui;d4-j)USfS{EvZs zPTwet1Ty-V1fgJ-l0t7bZLZ%wb?q;oZ?Y#Jd-G;Ut9;D;{>g&;+>tvU9uu9fLsBSB zx_R83h6G$5PkgTk3&}qHUL?GGjTfO7AS_CN5{!yH|9-AQ*g8U}4a$)>3yQf$yE(*d zuq(ro+}MaB0a;usgz!RoKAx|~iUsw*hB}$RFD@n&o6OaExPBJc!FY|4(}1jESLc*9 zb{amg9(W_ecT50bUVZV2t|D1Mpe#W5lTkuqjBigYFW(C`c35O+de{52ytvrZ4u9&` zuCZV2ERQ2n%wxP8>qY$lC5Jv{GMr+#Qq=mbv>kI_mhL1w*if{lAB*3mHB~6x+sqT^ zO>cW^P2X>v#VxPJ#1F|wrRkRTZhX7p_u7_b&Qp!mhK)`n`vU`a(NrEKoywPttD);a z6T4s+VGHGG+Tls%v67^EwwJpk;pYO}ey)g!5LEKh6cewrjfq`kfM`esSE_WE>A z=1&V9Mc~KT{4xIYG%qwhaENV>o2bGPiW|EEDnRU{zCeJF1yz53QMEd6PBmSFon0+e z!-isMSf2l#QoysQ$5%XY7=pNiPMmiALXE^uoBS0P5?Y&93=PEqP3$f$>bP1?9$lHV zwI*>FuAc4UNoBDn0u{6;CZY~w9h^ir`fud)ODVv3@}21!{A|$R?``=~wDo=E;^Ju2 zFq}tB?jj@&9~{bdqCnlGg`8sRTNX`U!^HcqJ-jEP2ZqGFQ{7bnJy~rNv=H3F>f$^z zrv1q;@BX~`-LXH7KIk%bA<6N-!??H$x9GX$=?YuY1?_!=eTO|ExM6i)4N;ccwsIZo zgJ!l2UI(tD%)5RC@3fT_?q!9!T${<_nM;;qF>FMBwF<;wCURbP;__X(X>f3;cSL^n zb3EgaRr!#}3T?CQJH>#E!H$?+<06U|;>_lFK`lJYA%0^djC%3m0d*Sm^m*F-1mK33r_;?}6$L&MW<=enYhS>WD_@B1ZShO$;DDPF z$53Z$)oIfzuRGDNV@*qE_JMN4mT)qk;@K|}xv>h5{in!+-0+YTTRF+0XG-UEj2VIU~0%}t6=S_=_-nej`yImzFaK#d(gliMjm7FiarIU zwsC(=%!yt2eXwo_vm*ixmvo$|u^HFc)b<&y2W}}c2}x7NC_2DfWS?h6V$jyo{f^H1 zDp2P>8i)k#e_MwdK7p1cr$;~wS95g>g~(Y!DzNYj;is5;-XL4oXEuxL>E$M<-hRz9 ze3RbCc^xa>!l%(Lo}jv28;bcdR6IQP1UhMGO$IG<+V+0z-s?U6b+V8)l3vcTCE~;XYQ@95Gaw~Tt$6cNocT z%6Euw088b(76A8rXtQ);%zvzO+2tm`aabWdyE>8BxcX4UpFB> z{unxZBrkD68`CdRF+rWHX*S4F`T+0VNfbPLd`MY51e#tVQ~j1`Q>PMOu3rJ3P?bh2 z{$qT<&B^!ZUc1;s^%Zza98MY`h=Q}p;Sy^Nx$rZ%!s=nrY;-%8wW`lfpOb4$mD^)H7>f6I+nq>~+YrYa{Z z6N2S^uiK02U>c)dA?`<>CA-%tarc;|xsfasZ^T^8&cVfPKUe>qUGV#Dc{$V4(!lA1 zygsFmYj48Ui(M|sNhPAuiOSL624={giFar>zxptQ6j(I+AOkl*Ip_sEEa$0*$UT2U zYWuvs^`t>G!-2WMY5#zQ{h^IXX9>SVzt-k?H!>XDUv@6xFR#M1>j)ks-z@OxW}-Zw zG<>?Ug!uhqiE#`q)VJq0xS<0HDwzu|Wt>!A(FE?LYI1jle@_VeNB!8`7xiZY)B*kp zEBziDrpw0bE173NZ@g2{EX`$lJ7T^XW(6iKUx^z)3nDOTh!jo$(@1zU32HCi&hW@L ze#Dxls&dj=#NxtsJNzbO$9prUv*d@b1Fe^7V0+)11!CMzcVkn-m@(~Kww^(?fsG@l zfy?g9)i6W&nzan2APWcY$`uq+##PHFTjQF3vhWqVIoc-SO%gY8oj_7_+}bT(> zt-&e3hL0~CyOxj{ZB=y7(%Cn=>lY$M;~{Ki?Xo|+TPkJBhq}kP5A0HTL|SL0uLQZ3 zyd_K8JM!~{X9;Tlq}+WAyrp7+HIK%rFl(Y0URRRV8?y*{fy>X@GY@f=m0tgz52dXK zt3KHM5j%SIJa)DQ+ERPFL2Jhizp=8&I!~PAc}in^QB@;3cC+gGDVW?x0pKga$v$nL^u zZFjd$Lw_7lmYf6B2QWZSigSfr3Rro9f?y61P=)1iw79yBWo4T+R=9lZHrpoR;&WBat^7%*llvd=FS^i1Xo65RjHWK`ufgPKRMGc5XIV zf`|}%9#(E@&Cymt3yjuuS-v{SsQPN8DR(zAGTKn z##db-72d^fEYBu|sm7<848D4#KAAH^V=rU5Qldokw&LNy^eE(miDDc5vs*(U41szz z7uXWQp@cb0XVF09a8j578R^CjCWTcpQE(|vgQP7ymIAWIm4t!;iYpkAi?NTLGzXa+ zT8)=B1i!k-Wp0=tiJ&=#*f1uH%xxZW3NjYl$?)`%uy%A)Je{@wwHfkKh@KW4@r3k* zC1p59R6!iokQJhcckWN4c< z#39B6H49aWN{ONUP>hJ7WhC?1)FKT4K|ZHm2U5JNwfZ90GWF*a`LhP)?Y zfAOrcVj@#MHqg(bPuMXHW*1^yTENvNU5;$XLxL3Qf~LWzVgVx_ z=J#GBI_Cr9)W&U>+0Ucm#^%^O2s=l9snhLJGx^t3<3ULYjgurD13@(%hdBBz;5)Tq z&F@?0uL;~dIUTRy(2LHawesz$%C@hxI>9G%37mlNv8@$`E{m7u9-35aUQhJe>^_it zXpF-$&y*;CaONZu)!|JsQ$=eoWEX0@Au*SMdWz8CHYv~vDI6*oHj-KBE|nyVRM8Sz z8W62GOcfZ1I-(87O}PE^I6kYpF{~tAkw>`kI!|zTS!NQfZx!Wh<_*?qFnM5||AuxxY$qpJbJ$>RQ1ZVGDFeI#C z`QoJHme60j$XK=Svg$uFbeBbzotJ-5t78Opj)T~eVUr_>IhQue;g6|zLRZd_(Nh+V z%Wk}g!0Qb+moe*kdQ3xFP!xNlF;a8ik|O|Peiic0)-1+aMT2c*x24gPtB;;p1HKTKVZ}P|*Ju$J-84cH{)xDL1b_S=)mhtopeRir|%Z-PFbjKF2qH=Cy z>sfO-2;y-w{-B&a$)y+DMwurbV#5nIdxkfT3=VX|rtT>+FGyz5@dd?7Xtk$pIbWZ; z4T%cK+5Z*3-#W8|qU(C7h(A0nnM9#Q_5$WqH|s+8+Uy=zpWvFgqiObg)L9uJ-MMW` zwP=ckB!%3X=NN*g_nunh_fI!%XUg|pMPS?hj27osPxQ7=`!i?r`b9##GhaaA($7`FvZTi&bPLI#-(3(v6Rp!g6N_?z z<%cv=59SgGeZ$}CG2Ou*>aL2Z+wVt#1TRsSw(=uEo15Q49u+GGS4bfXa{3x~XBM?D zZ?!LHy=@<5P}P|@!!_;g^!VFp0c-e|x7JgW8dpesuc{^2SsfiV(D)e;$Of$UMC8$A zlxWxYr_NGd&QcK^tQTe{P^cb$8clB)i54Y2NAnhO*QCUt2yqd7xv+>0ucnT8uJI)S zCh{So%T;))R5QSzmPLqm!|j5HQw1UEAw|x+S276H_(!$ixl}RY)v`yH13gLcLNCgA zH0Dy6=j^Pntc?Nh8mb~+&=XX(1qQ+I1*2$>eGf0$$*;7HP+wS)V)}yyO5E>^$aU7x zgS9l$etwP+4S}9MmUW#@WS56AqRqS#0}K|mrL9JKy(6vL6MDVhfuxG3IV&~1zYZfG z7hbkOSJ>L!t8FTs7x=)@U;MD{9rW+VIM;zuryhWM6=U$6HL|fVZleJ$Dnk3(LEkIp3}~)%ASmB`J$bM%f9!8&w&!n#HKU1B-gToW=q`{2 zVS-SL*u)6O86E6dlt$^KL3D1?KxXL)Df6@hmH`4^O6dvcdjn%UMrdB4$WkT`h%!CL zYgoA&*YzUBN3owfbenK2v-?z)wkUY14CtAtm#3|Q8*d<>3TCB2HPkT!JG)M10!Gnz z4wexeKI#sA=@f(C@8vDOKisn2zN{l}+)b)R7c2Svm#263OTkJrrm3A)Rm)MMp!NsV zxCHaBrGL7PSlxXt&~Z_MDI~U=jpb!RP;x|?V_V*3ynQ5`baocVl*5J3gEv;TA-|5J z&v#cj`jy~X3B#f(hRX8D^2Bh-douj7FgqVlK}QoO^$$9c9+5~+QbapYR%E4;)_=lj zvmcf+nabLV2cN~m`9^}}ci;A^Hw+IV);FW7?U;3LxqLjjZ;p~oZ6vHQpdSEYvJ1@&-I^kZPzdw1f zQDIu@@QR!Ov(%;dGr^K zL`3J?1{R%we-;J_h=Rx9^ftlmyS9&L0x5DjCw~jjD&&gCB;=DPW9L+V%jh(?8qYB? z7rxt!LgKZ>J&evNP$lPc?*B%DU261(B9>HLPn~193?3Y3rx!_$#7}6Li8g!g03=s=_6s=&x0*05yjZA2PgT=$0qtyDXmR9 zVrz=%4e0~3pIwYolQ+yK)LSeUIJrx-!rjs5nH3ZckRavYT9xKNt`rBkgI<);AC*g* z9)50^vQCn)s=H@us}`yvRM!p>=}t0pP}-ncW4${(s?Ds`8KUamF4?1e(&i`^h^sE) z4gw?!CFar7=Rw*Z@myGfYSk(UWD88RsiY#+$rB~ByXi1ep}Ut1l*)({cazAO(KZ)E z+pB|o7H;^o%k$LHmjbC3XIj=*6_w^z@&lc16#XAsm8jsZ#|#RmWT#VF#ef!>NA5z| zExuFKR_7Dpte95<1!?@qE&TF$VB?%*(BDn6_gbpSV_|aT8DySZ63XoMH_0adpFdAe z9c3o4t>P#PCn4n0J7z_&QdoJAq8tZZRE&9>$CdJ+ekGMy*a-&v6w1`S2BimSo z+QMja&b|`EUlK~IlZzRRM}@wFCro4!gRs=+met2ntST(w1|Z;*T1ciLHRTdT;p5lK zrZWTzN`T%fhH&n$zovuI6dng^1z(kMp{FFr*3*IFmD!?LI@mS1*osVi)$9I&3MM7QfziMQC_z$p(D|mL9B40NjZ5(cLb1?J z{7NSdH9!iCs#WUCtEC!LFIpvE3Znxjir9<%eN^d|h)^H0CVetZZK}{Cw30inOP2Oz z%sZ&2gg6yAraiD`$_(8RNT#EgS>Oc=vP3J9nT!!3fzGYvNl?O|AMzW;&=2AOU{tiH z%$Sx5uiYtWgc>rNJgR-CQY23qKK?T!9U`HkC2NB2whX-tX3$kBLoc&h~y5q!(+L8 zDCIWM?;=^?WA5@JD--)V88?=J4&w_e8Fgw6X`BC%)C#(i`ADU-Yn;4WL@Yg2_3&hP5EZ$7^=-b(M@eiR1(^?DjoSHMZk**X z2WHI)@{d{`-e|>v-@AH+fkh-SiRS&77VaH+L|`KtcpTfyTwa9ACXI&iGq3U}4J)#l zfG4qlmEw@H@!cfTmjYe`os9HeB}RLCO9@VLn}xoJLHxCpKkuj1RA$IW>JZsVw3C+3 zLIh11?P#lbKxM>liPOx~FZA_w2vh@Fcb##TCUSdPTdNhfLu~e4{%=X8*e_VNd-ANN zE_tYfZfV%YM!r{}Yw1b)JR&`rTkVE&qS?QdKh$K&^v8{vyisGB7~~52h7`lw}2#Y{ctkGp&y44ey&LjL_~+}y0`+>dMUoF?a-8MJW;ClXkF zh4O(lM^yfP*e1LnB@HFG4`(U2Ny8dn6#I$bv`Mro=OJ+-RYH$DVJSQ3)W6hf zNx7T|*H;eNVM9;Lmn6P`)P-N{xdh)%~JQ&c#s2_iK$*YJ;F^Dw?}E?bNxEcUVHEF_fsUi`ZW&C) z#Dt`8mO=36aI|4FXFNABfCe8J05y@eiHWud|5KH$H%ESaREZ}@)}sTDP5N}S4R8aa zqwtf1^y(1m3r@^Zr+_TZcqKp;G&hxn0Edashvxmfogz;O!ct`Xj6!%x5`JzG>Z#+k z5B5ad;=Z9chQEqinF$3DaX*tJ|H+XY*SHm{!1((30LikTB(XPw`zsK48;Zv!!CqeF zE6(9lY2LMR7OUhXf#w#QH=EdZOf3; z&|732y+?}lq0{+!rCbP~Cs*M*b!TG6js!LZ{AZL6q?ev|qoPxioG7K((|{sF`q>Hf zKZ=fa8C1(F8{2gWu4s9GJL)JBIY?i`O>^-TDxZOFH)P&qAA0Xxk_H1WCW2cZGL_jz8#K+VzM3DSVH#{-ORQ zGTy0#@laP@$#~BUi=!bQvoO|$gt7c$havtK!*rzeLd-~pOsMOA zGgu6?Z~y~5dva_V7vzGK>5I2>+lzCyb_|p(wVE|~H*q(uE`EG+)C00dhu5tiw&eAW z@-GgngnDmvhKfkr65Oi+Z+o0a9PiQ>PjdvPcdQ6_ykq;UM85Jb$~yS|y2Vr3tZlzD z&+Q@d_0xZfcl8x&e74~67NCB`NylU7lLiU4$PsbRo(#z>P+~QGOR9d0EU8nE>w{#K z>Imm#)#@Gbs%>1c{vkQ{sc9Y2yR?pI-Cv=S2z8_RYg7|GB$Up5#)_gpV1E>@WregN zwXEgysb)obZ}y7TpFwm|RJDRth@xtm1OG0~fiIgo3%(ACsaZ4Ozl}znH5>kFg!&FL zX#d&y`wld79DsNp#iUane=ZC06X9I=8GHxYISxL*L!+lW^;{;tdEv}MD{tswM>tHp zgZ)A1B=Iqg%Lr$i+0GGK`8i@nc8(zN0IKg!C~I+%6)$SKPrz1*qC=5gLHE9$Jg*+v z?Fz-)3AQSfZaIRZ(nEQ5MaZd#R-rfvVIpPp9vMalEkhjVZf{4|G+csqVOL}f{)u!w zl+_mK>Ix+5U+NDK{a^rGGmDe;k%Hi$f^Z%FvIZQGrXU4TVv=W}`kXePmnI)Q@xc=V z^_7)4XA0qa4EJIwq54M=lBc&&lF?|T!mY#h|OT2GA^`7=b9j?e?>da8TTGiz`k zvUIE&gws$#JWwtM$~u67E)pQ*BW%vsR#HOGxh_FtZBB z^i^o@O#cCFWef136iok))xzI>MNV`6l9<%MYDJp!>sT%SD*=wr!mk?^avYiIUqd*{ zg}edb?jZi#!_cy$<|o6KGt)B}>@Sk{h2kfri!&`>9Qiy6|L_|L1HpM>214rxRF`J{ z2krq$d2~h=d;VNKTmBk7Q%L_$RAb=~rKg2N=;FowSQ1#GQKoPrEpL6*_B)Rq^tc^d zxw9ycmBnO5qkZaZ>peH!eD|2M(FwQzRbgJXcJn)4t9N=GhqCj`sZe~pwb!iCeCpy6QrjES_ zE7=>QLb=z&4hP-6YP(1`pnU|3Uth^WbDZYOe{G`qHIK_g=Uc zu_0=AtWrt(%-a=wN7AaKEN5Bc-gPimU2d(`>jiE*vj5__(#p&AzNF-db< zNZNuVw}l=b>HAMe@*Z%c8{MX0!u^BHXQ$wY9q&K+DWBQ`XzXkpe@8SYDjh&-$K6|< z-44T5Xc>gh@xQlUyY_Wab&NZ}e?VIyefm3%3ne*PCQ|(q)0KPrOf75&Z zX)9~g4uNHE4|rkv3jYc`6)gjsXNsz{@$u$)zX|Gb%$~w~1NS&K#W|2OvijKAg`=^f z7sg^2PRCAz-C!$bLJ1?1D?g}w+BDnURf_9)BzIRWe)#cNz*}2iSKZNGUDZ`n8++{N zeUF;*^Y!WYvZPb`V+TvBIeTIGP;p++RlNUbxU1ObFsp_8k#4vnKIbRIdqKoqAnX7s z_bc{|MgB&vBoC3i`wdApqI3^Yj{ofUqo}Y=iSJpXk`9jycO8*!F)Y=vG(%ZgNS6Io z?oFBBeKG&pJ5$J54+(dCkan6uW_|1(l4ORM{y%fC%HR54p?Td=LS!ddLxtp2$V;h| z#JMs5&aPvA3@K|NIYu~H10`c30cK(SbPiCDfUT##@r_e4{HeLM^%MBfZ0>#h507_s z!B4#)`<-BS5dQnm+)09E^^XNv?WD5WXN2dra;Og5MPUuajM!7@HgY$D(=Yh3aZgH| z7YklW%e%Jb(4DdFqK3@O*!!An+rWTo=5jZ*tw0UEeAiXZcnB5Tpla*}1-qB^u4=Hev9#6AqGh=}GsYG)uHL@{?gf3-vW}IG;QCLxL)(h~^sdML7yrHgHL>&WckcXq8uQ#Q-cLjeoS-bAzCOg|_VLy&5AFq< zW9BjUFiKcCpLb{1!H1?>QJzYK-l6ngc_$2#D@&Q*!Sh6g&{e;g3835L(EXrkP!C&# zOAJLvZvE}2i|XqO16|(n6|Fb93JWyuw*B{woL=kqul4JnIc9GUudg#W47$4R>oaqW znN=3@p~j*z%n#0jmI|96E7R=Y>uu2wxxub1CPxcRu{NmFPV>J-R@vl$mDB$1?~=i`iV@;T zZ5123sgPuIbAGLUu>aM4(S5J>559UNdIa6ca2fuY;XnBp{*m8}q3|?H&Wf_Ozt})Y zKm5SBtBm8?g2BR)lERXT@nb)`o69w_8HE<(efs?y^NMXoPj6mHp2NI)f2gIbL6@!p z>Ktp8y(L8DCAvdG(7ORlSgki|?tKk@FuF^ZrmtMOB=q=!&%P_#B;wP#k{So{-m$n% zLLrEb95N=le}dQ}0*$=F9Wn3)dPNMiMQr4b5s59LMYhN?wC2$+L;9|5`q4Hhy{~{h z<2Ub$-{*3H-l<{4x({QDveLXCcbYptj+#3LI*=C+>AoxYreoo~LJ2+mgwedwp=fl7 zC|H~~_xbO7iH{+%KfK@r1RhNRasA)wKA6AN{ZH5?0e=+cL~N4r2>4|L>=~u|_&;;+ z2#fErnWlY&ZAtACv(wwafyRMIWFU$^^?V-`DPupiWfI(vINLqV(4X$`^!jqH7T?*KkM68-Qlz7J*=J4AyqrVwl; zCAJ{g0Ff79d`Rnvyp&e6*dX3$4}#Fsh9I=XUl-4Ie#p~Asw+$oYJs48hz}pn8rRiV zxV@U8A&u94jcJ@US1;P?hup3o=qReJu?$#hTFmuLb;Xd)#XK9<3Sq}>4-2`54alxx zW3qM}w|SybkL^V0$YYN^h4YW~w`@VREn9Ahd>A45#qoQLSvc1m?AHk3+j&y&{|$QV6{qU4_utWj4nY0C5Z4Np z(V7Qn_u%3{E=l1a>ioUfonM43+ny*-no1-40CFet4d{P5=eV*k3AAPq&IDo1j% zp(TQ=a*#Db^04DFG;T$p^Ie6)r3%Qb{aY7Ro7r*GH3HjJ)a%Bxj-v?|*EQ9fTQJvE z7Ih5#H{6dkpL^gwa}U1Hac>;)uP(y(Z6))sYNh^FtoN}%;XlIKAG}8d3$_A~WWaX7 z5cO~2Ibzt!{Hsv6my0rHFdEvrckfpIqe0LZ%r7d+55jdH9qk_No}3&gs4e*G*R4`p z=v~pjDp*6fq>EZa8q9#yDxx_yaa+tFx5Y?*Mq-8OahFEH{=z=hc#e`iQ433aOLBeu z4biEp%v2&q9np={Zn8;ys#Bv-GmZ*+XZi~652B(qqej6)EmBb?^ZWYxVzJop{`&@c zh6hTV&XNIKOB`|E{pS7A;swPj0aok0U_}Q9WqR!Sk-ol>U|f&jJRry?D91M+c}*Gu zs1u4-aGOIb4Xvyx;B-ulGjEJf#abI;4XywCLH<8FuQZQDA|uULIyR~ zxl6eJ`ycW73E|h!NMOR93%4;k7r--{$rs?vTQ>DGOZzu%8Ry(lH#e&3K6m)B{O*B) z?)=9NU$;s(Hl|y39jNJ0iM3JK|7B71P!G^Jqybn1NEFusD3g$Mj(f%K*t^BRIVyWf zb4t>6hD-SK7Wy;6S5~xgp~0b5s~7$q`m_>#7q6Q_Hm?cigNjyfd{{hcMJXOru!5f* zDzw|p0drP|&-(W6&^c2mEM*UkdSyU&Hz33fY3BHpzk|EV(i_!CY+(p$qxO zHnu_-5ltITP-d|2X_`F^^)$-u3SeI*^~CxtY&?0FvDrI_D|V_o^jq=dGc<(!u+%4e zh2!669}$8O4BjvI!+j{g_S5TK0|>TOk{JA)xs7b**SbQ_im=0BxTE3D=1)7WGOtSV zg8%=zw*((kwxn_iU-yQcs^}Itzvx%pRNd{hln!6-?5@_jijjBq@IYHjuw8Q&9PkF0 z=jN7I%kIp~!@iez0i=k3ACNyDW#;f0)IkJRE?u#FF^c=ez;F1UdfNLVe?LCaytg?sP4f%p z%*5Tcryd*{70x|ACKuTU-}mLBLOJKZv1N(QW(4QV0Q~D0aD1-M@+WXvi8E1>%FR7d{(BR?M2a!eMB; zAR`75ob02w#XF3RShA476g*)0QQ*hkVYur;^tB5|?HzVF88Aia%za_@mw%w=5*BFFsP?v+Q#^IBqNgX?#F&mdQzt&_4I%|I?jQSOY!@a(VD(1#3pce78+TD zyP~2U1=dh#0!7tGfK7~{P(KHG&reUhe&XwBLyj=yN|?ZX8;G+b9y7@j;&&*HOd@#a z!4LV5V(0M~6Vq2n6i-;cx2a6SBcM|kS-1gk=(Xqo8UUWV;lpPs!D_c2;Pdjd8y0Sih?CB7egmLp}dR-Xl``ZQ>r&{jfL@IYj8 z;f=Q~Ub_0mHD-rKQ@`-B_a0kVZ)n-J)6>}7|lMOIFwLp&0;=b{d zJBlissxnM!R-adR$18Uf+cT{8h6T%t+uJjWpjau=fAN7skLkvAi#&BrY6fTvS}V8Q zcH5T9)&i||lr>vDuVpmlBk`2qMAnjnyoq9@s7s}P*2}!K7La$~_$)VgIs7*+9D20) z%>BxnyGpLvxYF=NA;0f9KC2gt_vR+^c;Z`gles+6rMcm|^B>wKyX631n0psJuOG&- zSei>hJfCo%ntabXM|;Cf=Qf+=Ww@IL~L(^saXunp9fklcxTd3^f|c#LoD z7=QCAuo(~Y&8Oa!jf4HS^7nrHMEf8fY_R?G)Kml-zHm7Hkq92||3Di3Bgx@F`hnAm z5n!Yu{=?C4?msU?f&2P~h9)P4Fz|$+;~77RtH|HSD(1n0P3BE63!d8*w}-<}rV-aI z)$MSN;2sxaw89!;Ie3h(8UgPOO~sc=2qxq8V!jjisS4|b-(&gvdcgzTSQ6rOllXnT zn766@#U+VCqxaiofoM3oG8TOW^NgG7RvUqp=tV|KwoU3th6qW0$r!76T-=U*(ORS* zDN@K(Y6dhqZgzJVAW|(IIS#Eh_P$Zy*@ki2V7#_NZP28NF>=AToiD-@Q4;`S7k8Gm zMQ23FwWJ$0>gY4+y0(P?VON{o*~z4{>>I>p7S0D$m&e^HSRO+yA|)4dh7gO;3ZB*g z>30@?U)iZe*5)j+zZpF{QMv7)eodD!qs4&UG3<;x_edHylQr;iu4DbiL9fR8KkqvE zeZE92@!_XIkzqrvcb)#ZFRWkfwR;D~#6lnNT*K0!FedXgD4(2&qDjU~8lHS|U&K)1 z3G_JB#>e)~8m8>;_E&ig+^)M4gOzC9!+5S_Tqqe+O;&4R$#lWyl2ZBWk{YGJnI|D9hk~Yvv{7gBIj(S=oyHFr6g=944}D06)k3{S&sD@O z(h7z>f#&L7P~~+X>prsV+AmIx_AY2}3?K{ty!oze$4}ze?P2D3wAWF9WRT&b436Jh zScbKC;7qb0Nh|9PArXwS$#7;F@|dV-4EQ*dT{;ntRt854o;0^%KX8D!hPy&YB#+;` z3%P@lgM`qh66Zsi#27-{97ZvNJIv+P*djji(D_(p>3P8>zWvd=C_O5%e^c_lXcnXQ zU1nPWwv2b*aDKGF7Q@Ez^t;=3Qak(#>FtN{kK5sp2)XtSJT98O#1to!C${+QI95cw zL~&ajPW-E8VvVC23+A}vs;Gl*qdEwlt`c<+X92rAWjd%6j=xWt5H&<~P?n&Bd`WZ= zgq7VP)j=KM^DAWWOyP{l{)otE>P^GFuSljKqQ$T69XLNQMzaMmO=ylF%^4(wA@R-z za`vO%GZL9I5$~Lq*=BCT5&kHS_9F_R7(a>kV=7^tA=J0v&U;asL;nBfoezJtJA?n5 z?~VI^r@#2$u6FouU3W%$oxKiu3E}RbcMZ&jzj$TR?DMz>t$gBKm~AAcm^|)0>zu>^ zQ_k%u8Z+7aD5K%JU}6`X!r2Y>WR`)6jwQDXRzujc8U&>Hgr)Yt1u1N~MIgeK*#iX# zw!#pgu~(MlxkG$wp~c_$*>c?lqRpA}&i@QiZqCGa)MFaofkk547gSXktNG`)2?(Xm zapT`Z*rZN$6E0u^Dg*A(afa?VDITQk-$IO=dPswXjnxfM0nIRMbnD)T{=WT%<*u@} zaL@SAfF|?i9o_u|UM-v(D|BIz(pmtB=AxRp^?_))v$SkML+{`e_@@y=na^D~I5ik5 zt*ol3uMPJ{`iC`4W^v8>1$!S5^Z!pI)Xij&zQ!cn)Q*}POH%%gXY7ik_JP&g$|Zcmif`F85`e&4B-W%Y8I{$~hbZ4vtf4B(%%I^DIb?TOtMJIJza2 z6z~M~cw7I1wTeDj^R@t zL#-O5k$eb>>oR!TXh6U1wW$%WhJ^J4{b#Wsy^{GF!3FssP+FkGmu@001Hsc4hq+tg zQGc$`)*Ct>vu8R-MZY@#Nlmb9?GEf)Cw`5jFts)cv&axI2P9thj*5~UyU&lh3!EXr z*B)Nf;lFmPtkw&kFmap!BM9e6D!yL4xn~^X_78h+ei2uF6%B)Ih}TEM{QIQtOU%(m zFp&6l5riNN8_v{zU~5+x;}V9-A7yJ#4xAkvJUcK+??iG#S-wAB@7d7Iz&$}SZG~EN z+G>$rErN?^z{(SkS&E7>^yur_@=T;_U13%bW#UM=;#5w92A<%0K3@OvRxsQy9%ORIoYYPJ?i{cpqr1`bdU z9Cm^!`oNhI`oQ6OaAgxXhgcV`tPPj+e={ZN{{~?xo4;vrE~K>c+dOcB9H+#IBgO8Y z87FkHgAym4*Qr1W%65X&DM244bj+iYOo>^$Xp<+g%L#55BrxR^qcD{i_F&m4q9`Z| z5^q8E%(x~DrLPMo9pR8kPC%s=W1y!CJQ&7TY2UHy_6{W0JNhC4z;C%_bO7$6LRWk3 z_QNBkg+--<{Xw4O>`MCa$@&1Hl@+3(BXC9KjriugUl;WpkZW<&dLa4zxW5Vo!fr4L;M{# z^LMJNspTN|90WCDdab{nc|y)>>bi=27NHc zVtfhucNYIaYo|r>SN%Vr{AY1x3+i%Tt@1&O9#U%mj=vgPugOKFs1e>%E(^U=-!P*qx5RSI6%_57|~&u95|`Fy*m9sn-mjnotxKK6c_}IVz$34AN?*}inw7+w{qh+`|M0F}06TJs*JQMcC##`uc5Co?c zs$a(zwdq272h^oGS!OlcWxhD9(HOY;TyxOeSGg@O-My#t#-_{0RSXyT+GG;QYhS8Tto&og`b{=|$GH%>a2@L37R`-zInClL$Ub=Xd z$z%=ocXtff4Vk&_MXfDfTcyPu&~#G!>v7#R%BTn5g| zZQu`VA^8HF4L$&W!JVr-dC0PDnd`OU{bl3j`-)$4E!%GCJ6Uqturg$xc`Nr7$iUhshU zCH)nt#esI5qW2{7eV<2pb^IetHE4`R`5!TbqoYDT(i+km+lf{Y`8)m2T69u@Z(40| z8MA@b29`y8&X<*E@&6lmvdYWOGtO($bj`txjVmUmhAesEJj>9Ko(nJAZapvdsK;li zJbK3${vh{)p%1U$m*eb3-4kPIna5)M(chPZOB&$EKXkW*TN>cU{k{CJR6?KqW3;bR z+(Ht2%OH^<^q1*whkro*p?mK=6h3#Z2X&e02@M1WKz%yhheV z>Y7`!ZtLv@*+MHRa!V@a3MHHFd}#Rdn@5(}*m{R+zo_spNzOxkP6j!rwu zc!bU9qw6QolxgB$nVJI2rufpIpE~vPQ-h-m*REX{^qBJVO&AmO{CQDa1&D`KFc>^@PQCw4F$O-em z*A=d6tU3AHN2`KCizUQn*Du`Wef)<%eB68E;u^g^)Wv4jE!q*d?U6@r3xpQK1?+VAu%mfFMutP3 zyLiQ3{@=mZum6wh^*?z$GBp*^{DI9VvgAUvatwC3=c+jH;Q5XY(Fb}4+oxGL?+@Cj ztY}*RqP04((b|Fx4UmmA7_wi%W@2;j-=ykHErXSh;@JvlNi~fGcDkT-+-|$wZI_Rd zOa(!!-aYdd_8B@R;$c_9sNz>jOG`R*Df;{uiRmRDocynmS>Y@ioB0kM+n+G*{46FZ z65EqlPsVg?J;~5{HkB!bV{3n5e$BiJzbaI<3V6+83(gbG0gUEZh2aS5=fXJAvYZ2Z zfvy4{RVSfq3QJE)&fT+GeR66hIn59n{(;i4X>J;(Kxr1zHO_%uGRh@AcOe_{FU-3b z*J{-oitGHAH8tja7<2FJHa)mgItf;-oWzC{OU-{X$!ye^?$h)xTobCb5LRD$9!QP$ zFh<)GKLh_hHI|#jGX-xVQbs%>oEAnC<=V3eM+bDb?_Y3VGZyMf zbl;z_qABK_6o-C{gM0Ra{7@3aHYAXM|7HS?Sne|4r8M!tb@C^rO6rr0ltxdcIMv7r zHjg6?j;oxoAK59^c$Qc+PxI->->i`t|4?08coy zXRHeK??5upW`Xvr5qw<})}x2R8t_8@#MIP8Kff+<9C*rtR=nTkhp%bD&-?ji@V)-% zxpRYb5C|b*9P}_wT0$s-56}>!XfQYgc!6IB2P5NLRJF5O<~BSI&|0)EW58+CLeb1T z&HqRPfrHY1O;iIK+d(7zzaNysK{fo3l<&HLi*vzOjarxA1=jXRpKeFrK~Vg!5U9j) zT>)PJz`y#A}J;eWrzS9oh2aP=t7YL63m0s~~0OD)2*Z}LA%^eYfQ*-tQZJ6E%XeJk|%4`BpwR#ZajA1u9`L3*_w>&TALi*rd`(_IX2L{ zY_*z6FD$9IH@B|Ue_{7c4-{r+-FnE|ygnBD$8UEXxCuut*EcU;OU|a`JYW)TnRRJ7 z#f=^1fHbqw;9Ej~6+GA1=?QP&`qq=ZU7m2L>n;8Xuw}`*1CImsfyV>Ax z_?g!iHS``h{>-wiK}^liU}Kj>BoeLV0zSuFBiAwDm6vJTZupK6A;g5BRi z3?2d>kTvOs=jUUjm#Pi-Vsje18D}^DY%g2&>Cc!qru{-*#Fv$C*liF&{v`IB$|4BXb+3ATQ;v@^v2g8w%1@p)8J@U(eIh!c6hPo{93$Ux8R$QO!y z6*iQ;aTwcf>5-50C7c_ z(%TWl%MmQDgrC^EI=tOx?`X3Bf;)Bx0_fwi@CC-NMP&vbFbhzO3HhabC^tajsSA}f z-%;?TH%i7z79A@1;_D^m&PZFN6MrJSf$o(3Gt-3SvkK*2_{;s}7Olnu-sBs=n=`7J zp`r1fvpvIjJkl46mp%_4clk9IFM3`zGXvg)&%=j@dr;sh4dnzAG1wYF#ZSy+O&+-eL*DhPNp?AZoW!G+5 z?{=@>qWHGcRptrimrlWrSmr9t5Arv|Pw-3dB>btX3{s?J<{#K+S!2Q--6f4V6(wmo z8d1A2-{Oyd@(BokGJO^}UVM@N*B8m5wG;j96aM%kFM+a`UgF;=6|{~VwmT8_tnq=w zpBbVWbBy)T)g&mnN;rZCP^Pqpy*i0gBAIkk^3Dik+ zooN(u$BBHLiSj{^J!*yLED?oae2CLX9B8x1CAkkxt-ymzg@rGLER3P?`%}onxGk<6 z)0=wCdCSNM8r?Hw&o!nJlmw4t0aqnnEvfLbeO}p#v5d}NehSN$+G(fcr?S-iEzt1FVG1+3Xo`z?mS~7>7l2#QczjAg=t@D$?7{M#@u=H$L zB}S{2v@1!udb7#qQG`Q#QUOgy76adDit&)0*D@y+KC8^eH2L^5$Zdf@^AX>lbsc^4 zljS1_=a4-7J`0*jA|e`v&9^|c@H5zb);YzzS0Ud1C6&NW6ml}*FXuU1GTUozy#oeQ zVBk{3YvSV>rFA{!sQB89P7pTntN2K-hC+64`#bovFn|v1G26Q?;?D}WRY~DGk-%$a z=yAi>;3I4hALVHbe+Z=a2dyU4s>M)1yN$W_A;pNDnt<6-Y4f(UE^_B)8telq<-8Up z8y|ZHD(ZkAqY%e{oSN{V$o}8Q>BGT%PHXXD4_O?YCkQ%4A&wgu z7+dPsE6CE)asHk54o#?Al|YaB+36t&em@obLSJ}C!n%OG<~7UfbL?uU^M)^)yW%<^ zq-#@9qIFS2tA=Gfo$r{T=+8{kdV|XOllCMLwFHJ)C zi-_WDF!4J?x=Y6{!Lm3$_AK&1$cL~dAkmZ{8NjY2l24Z)AUbE2fd*-1XxVHd^BGBo zVWmJxt?W{K_gT71GhjQ|&OgIH(@n03fbU4xCCQq^ z$FE8vd1ddH%uUxg9p8}AG1n-Ou*qbrbeuZLIG<6T@*C$Cq--h?DqXi+>AIEACNueU ziNZ22CZ+RoDd#m0xyY|00#T!u3q)2e5r}M3Q7D@W?o=+}!4&e3&lV(~+{wTulX=ql zWlO20kaPc6F5$3jQ_E;mEnWAr&mhnE%y~#Mb{;ZpR(h4EmRnZ@iN(q2rJGAu8Ra=z zGHE`GbRzosy<`H3h-#_4KsCb4aW9-~-1mY+Qv%HZRbKkA;})9vX^07MzY~y+^X;P} z4;@K2sx>I>zA<)INVorR$30^jhV3U=_h3&fM&?86xKD^R*0WS`J7=xn=D^m30`A4A zT3D!se_H}ON%b~@ovd>EItW)*xDC>U8f0l*uCglElvt6A?o?1#<4S6oXRF^ytim-w zK*{TO=slXah6vjZiMRL#$gRTi%lz#0j_UZGepmm$=Dq~5&FV_m_m^bJ@}hmSEm@0Y z$-6DbmXj!p5=U7KiE-lKIE!M(AvlZ4juH|G4rBor2v9fRLj7pm35KEqZmAxb6q-WQ zC6B&5D4hr>dOQTrf#CGKL2-Jf ziqhlAh|mLPKze|l-xK@^g3oh21@CfW0?TK-8&#TM@{nY9hR5^9RJcsg9o!_$B4oZ! zfZ&OfdJ?RZlhF2Uye6a+2^rnVfl^*313d+(lsq!(9K}j`2ijuwQ%6c+70L{ha)#Ds z5~!5rbOK65`;aOL=YpbUI3}QQsDzz|fN*e9VJ?z=N0o$e!OKcQ-(Y(U1>ZPM_V=aBAfqJ zwC0nN`8DKhBDwr;WRfeRawlf-UqN^_iAnqz)`ntXy#VV=Q9Uvuia4f#Ch`k7HPeBK z6wxvXmdG3YE|wli#4!nq$nRijDL_Ov3G3mEkNVxj-X5(@1?!;`)}PtFN&BC)9eJUyu_je=k{+ ztp4xd`a9FrUnnD0D+c*uQZ>fOK=Vjk&x^4CH3V-XJtHeVAyToj4#PQ}ikOwlCiE( zFos$>?#pLCeeAJMAB(@(6$o?%CU9TEwE&he?oSs%RB6zR9PQPqqrf_>)>RWcXd8~Z z!Ty4VMt71bmW^HhHXyb5tR>OIJGbtyzs@&4IHK0MicD+v_O>?9sjSCZD>$3FreTpHsf(UB`3 zu`{BKdlk<4YNTmP6Y)?II$o$i6Pr(uULF)6S?+oF9$^ULuOn*V>iR_<&mxa9(#3#T zaPPP8-HV4q#*=qg=OYIhP=Y}Hh4 zHc~(av+Z_LTqcxQTt34cJV{!SfB;AeVE}-=tO1EAC|HKTO{Yyl030@-4jK4&;O1Le+ zw=-9M%ax(c5^t9X{{-(<(v36+nC?}=t{aG5jEXi~DBv&9C?vZr;j5N@jdGx%>~aDx zO-cy2f2zVc#pFN%uA{-qfp!hXs<9MY-bfJ769 zh|-~gZH${)JXDk+m5!0L1eRNqVUAP09SSW*Q0_$ohTfvPcByr32SK2c-gdizAo@C|NF(O{nR8}~C_H^kE} z>gUFZJqwoLo@I+Mp-V>+dXlWiH=tbm#D1L6d#va9PGjOky+!*;rXpj*`YCA;lFD)L zn>&X)>YH>-Sb4yl*j2*Nu<`|ZbW*)a_*x#EtTU$G%eVbJ`?cr?!@vIc0{2TBgw0=o=X(l z75|u1c%=nKv1~G~r_mnfK)v3`kPs5blzapQqitW6y zZLz0pOJhRi!4E~1ht*D9X>nx%k7@6m=!?SO53@N8iwzHc?1Y-LCDc?XJ1fn%H)3Kh zS96I3IU0wdx9x@vdYe&k(Dlt9XHnnubr^%0;Cms<%21Z1CxQiF z?(K;>;`q^{V+r~puO}h=WpuB@-#C;2thFLdo0;W6hc0p|TJrN`%3*S~(}Wd8CA1JM z`FY+ee?cns2(fmN+-9y4$}L8*8w@>^V8Dus&}X{@b7OGK?T|^e0mp{Jhc}eX&dY}- zZCl@S-Lz@fO*=nQ;`G5Oe|+mL_u|q%x^WXY;`1U?%*!w*cW^y;51?gWsxyvKoy`WU z@0Z;9)KhoP*)rd&I43EnDXN=4zpl8(z;y%9Y2`D=jz;TS7ujX%qQyHG8x8V)$MG?S z`3G(HGuU=_VoGl`ao!q@s&%1Z&Jma66(hlR)m~T+;>Sh$J4K&h+O`fkJ%168KlK#g zo{FDZKY#xE`9a{sMMsYw%ZBAH-WlI;^k^349YeaYB1?g+G^!OJ4H8B8>iHN|e(T6s zf^Fac<_<2zC}E9>gUP#CVl6k`*kY;7QOIPyy)s$xycwmXGfKg>aO=`;m0V$oKWnk| zdS-h(v$L|qcLQjSlw_PG==D<)9m%bmdg{jvkzHInIpbFw-bq zQ6Z|FrztHE5S8~^TwZPF@@Se^6Q3uRZAe?Ve`Z6`bhS#~H?yJ8uT$w$)?)vHg*I&- ze#^=d;lYzWc4e*&b6TgXfe-nsQvVoa;j453#~VGT7C*^AL)p`BBIs`6X(PoR zWp{s*&uM%On0Bnes+0{q&V0t=IqjaTmzj$*U1_fhw_qt}wuo~Z`RUO4VJCKW<21C< zddN;MszEm5H#20hc;NiOLQQs#5FkM75Ukcy_d~3=urgmlLj*_#@+(|nu#BBAgoEWI z222cK=#Q$5}2R+8zE>tIfe3}+o!mFVNxA(;l} zRoSGc9_@n+hhO|G?SPbrAa`0QSLpky$@*SI=AOCxmk56S3iGao<-=weOL?C{wSyPGs)wb*OZ4U%MvjYhoIw@8zGLNYlH|;fA8V&87PMqfiTv4R(E=p?|rhLqmW2AhQ~c`!}2F z(UbPC9JM(^|9*zro3e*5qqcHbr(Om35haTC6lv9nbb@HsSK*P&v}+nBhcO*;sV<)Q zoLqPg7R{df1Qtfh`elk|gtA&i&mcNMS{8inU5aaz1u*Ri|19vt;(x=>0_)kc@!tt{ z06=yE*hcx$p>-*5^lJ!mB7XAo(bm>zYb&xH`fS5CV0{z5A0MCdp=M|1i z^t4FOSl|}TbgmmQPAHCF&!SkC7J|+t<(cSNle{7^#kxMo-}Cg4N}-<*ob7 z-fE(3UD=e%92D`iB|kptvfHE+-}}nMpW;V;5q*`xxAcfKs=dM;%YteYI`d13WC7QwP zTqEs)`#eAHxJ7~-p>2!!!;-d=dz{x#1wLxRpPkSG%tTEFv~_ThAJatcX)SwB)Ki#t zns!zw5~2M?DX@9o($3Unf`1H>jtfR2tncD$Fpo2vcY55{2L4IxS;NyW&{hrW+ZKqO z@94nw4TbP56vovcJWFa!-EwevT|pPDGklL84FqtVySm;Zetlt_lgl=-vJaGn+_9KD zR2Ca)X+iZa>|!p4dGlBw;8jol72=st8M(^5%a5R3BPJWKVxcD+Rtr~H@$3L zMra>5vObwl@#4tu8u|Gd`G038{a4%F0>2jCZ}}Y$*_gtv_dMKi`K^-h=Y6St z`Z`I4P##{ds61h%ejlu2OY}KvxAMGO)V>zgl-k#PZGVUiY`(gTM{>&c`Nfp>xoDr^ z%cnIb&x>jg1+eO$=6Sw^^C_wRlsHSMf0S`%38xdL%aSDcCe%sZi3YKU;vy4~aOPtf zY1=_%2<`YD6~gb9j0=qC?8+y;%cT9DXX_$M8PjwL2Vp9HF3MAue9rLHTy$w;RxbX~ z;dxd_eHvk#GS`QmL-FgO6yY%9hbX=82NC74p6x|cD2cuwE@5mFM^+fsWyonH9?>uC zYvK~dFmVKh(YpaYC>pt}tgp_;+^)Qbkxi3OG(>YI^kh zY4m9Ec&HM`13fPc$;t`UYD@}&fTqB%Q6UWCU(xjvc%Ftr z(ED!#zIRUk{r~y%YK{{oSmn6O(UcMZK7OAxp|*_pt?{ zJ|t4PIYHqTR_L|Fy5jp+%Y?d$?p`@bF!|P1R4GwqKvp&OmEimIL?0A7B#b1wR-f%U z8pU3b*`?uK(aI&2(QWIIr>PqD5c3nn}5Q34%`xWh(rQ%~?g5(HJ$x9`j z+$T`^zs2%PCn9S_VEz-bBRCr@mt2_QDh_uk{qjP;Mi|HkYi z#`)QxVXT!taOcIbF-|emdK7b`UCo8I=Bq4L0uyVKbO*t0h-0w*4FwVvp!|}$YOmL( zjXaW=m4aM{C?iGcoV{h9##Fm__9x*$b%p8Wot!C%d+yW+uJ| z&&6@QRiw8P>cH{bi%E63h^w%E&v0aZ-;5IJxUAA$F=IxByHYkTwbb0upk?Kk>rV9U zd8EE|zSZD?r?tmmnc32qQ%(wJ+za4cXqP4QQvjU06pL}4BdtSk(mHPCdQg^M$~sC# z?$ri_abq-Lh{($ou$sCpN1nQK{`UDE*;ydf7)lMk;QTt1+agz-%{g$B3P|QHL{+V< z)Qv`tM*Z#WPKnZ@Q@8r3FIG8n)e9fjmjhs*Q9FyrHU$T;j+teu;n3|BEKzMVoiAzf z`aXp;@INl}*S3Bf|INp(h-dKrW{Qnj3zDzY40@vw#E*AU zj=7oMedVLi&;In&U19I+yj+<~Kfi4G_U+4yYL&S%Wq+Zr*!`V*2gV*he&KS!>kWAQ zzXQD=|I3qGZnNa&LoICgbGtP;IkvYfHlJ_T(02|T92x*iD&|#G%)|8$!FJya+f6WI zaRx^SI3tmM$rB@29v`9nNe_a#^gE3>!cb$2M1Zq@gzFjk+dBR`sNabVJ_}59HTvLt z6FrPcR6JZZ@FViHT9w?D1X7btX|z2}vf zk|ph10t>B04xin${s5PcGWOw~2XtJWFrQsqKAX?vZy$;O_6y-}k*o{g6!^~+yoVPk z?;*;A83gNaI}!ZrfMHJtvzQ_Z*!cxNQwn;GRp#`-3>e{{zao759B( z4Pc8TNizX*L!@Tz+?uw^CPi3UtSz)!3$?}4u%fwKRj*jyzg$sYub5k<(z^mKy{a;( zsK;kAd_(gN^MU$^S;083sa;Iy+T?nh%Qa{EthwRhqHq{4>k&zZO&{{koE7rByta@& zSH*FJOA+1Ut$<@;6tR{O!~sU_I?&qMTDCkK3WeL<@S``V500HUF$RAjp3^KkmazZo z{V3)X&M>$K%>;6n6V(JUO1t#7LaB0wx;b0v^9A%alZ*?fXXYt=jU9S_o2|jqVNtdP zr&S_9GOEw-p}Z+`_jyFzePQkVMn$z<6IRu$!|HlfSYxkNG|ty<(KYJoa}<^?iz25B z{tkz{p$`0lAHw1As29C(f5_L7^TgBGfb%00+4zQMEs#s4HsBmpx$KHAdFWO4p^^MWR$r=ANw`2jrm|S7edp+J z`v0=|-UR!ZaPUz+GGxOaPnX{nblKofz*>6U62|cj=0bS}bT=ttCKmDT5f$9;gRc|KbLTeu)~!S0t3#I7 z6SI0|SwhushG-7&Prw3r*4b!&UJ~CPJlyavxc4BsJrCOK>7HK+I$W++ORL4@RBOsy zT1~571#OOMR8AlOZVm(jAw{jr=7npq##2%)mj?rZqb_gA=E8ow-P~_ku8SOJx)4@G zL>Mh=9Rbpjwpje3j(0N$Cl9)T7#Y@(V`Gd=gwn?w{5zMz@Mnftmb16L5;aG;H|7E zol&B^Yttv&4)18UH#^Mm>;llF~Ogq?N%hZivxXzdYN^9bglH4sS|qvBPR`u5?YR9Ov{Y&N;Upj+Q%1JeBUC zzs*@#;)x!)W#bWjagkDq_5Z^ck8g5UdP<$;gP}rax%H+)dv8Q3wkV@E+v1pAwq`H$ z0e;HWaSF5t$godBgM1R`H+G0;U0uBsyCO(Js&eOgFgN}O{w4={KRp1)2~Y481e1gMF{ZGASMua%SQ=qVdrrTvB6Bf$Fq#ef%Y0MzzG3 zuN5zlHnxt+0J&d_V8u-AR0>qg#5PQYO{S_paU+EpI7D*ZfHKj&9V+?#fCxFpKZ| zKa~_gc@A<)AwfRjofUvh_)Bel(SG<3HbbhOS&CPTOt*xL-rFH)N|Sdog0Lxay@Sp>Ni)7FROINX%S2lnw9j2|JOM`)7KBRFje%m|DWaVCCJ1QkK}P55sjfZ*!?Ry&R z67A|8JqSGT?aH@f!NKa-%4Our`EPM_>Wx=}f4=g^Wh+U10>XFO1p7k5Zf(&ll9Rkc zD9pv~qJ2?Wt47mBn z#Qg=WHQkr6>4AAtqaNk#ECWlpmx)cU&0y1mWeJF|#J{p4>jLpz>k{qyL=H3fJx~wJ zFr)8H(Y8-TWQlE4SYHp6(Q{T@ z&o5--r(nXPInYpcJ;o5x<1w4m&Iij<82YG=D}Wiwfp&fo5ZU=Fc)WcHZbuXBZ))pn zznrZPmQ8N#qxO;cg4?H?f~^l$q_Fm(?F_AjdhQo^48D-94;G{`_MzSf>xkx|4avSn zTOX`QW$mN-t^h0Gco^w=n$*?@D-x`I)aEQYro~xV8e;FGON=xYKbI|&Of>po_4Iy) zqc~S=`D5E3%Kkl+&D;LEzk}@$R;021q3uuSqMNL#FKhdQWoeCn#0QSn@5{vY*L{_? zKUkL8_$Twf3EPXg0vEa1_6I9?>z|w#WREh_5t*|64^||30LZ*XWupwvDck>W{iXH* zko|Kml!xtq-L=^NU`3(_0NVfXTwDOpMQZ=+z8d=UC^14_oG$r_U>xw`XYaP(OUZUwiiK0lAA=9LeLHgNtEYS-7v^!v!k{c&B8W z9J!b)0kmt*FJC2zNv>}-JGxi5Ex&%Oy-O04wHDhO!{PR>j-DPv|FS??z9w6zUA}PM zMs4?c?JeyAow`t|)pjggv`H1tqWrj@a{tQp(A;mV2_cq8X;SKBY0`i11GhdBKMwXE z>Fhkx`Q1_@Jl5j>Ecy?mYbm=>p19FmW;T~SBl^t8e%%VoMPpw>y>WAZr_4ju2o*44 zp9GMbT$q^AgK~Z-cA5V3n|Cjtw$)Tz0hA_lkw$3|^uQjX`|_Ng%%q?so)x=K zp4`3jkw1+bwt(GNGaYJh*BVm{+&pe9 z!!h&7AOC+~dE&c~Y=damLHg4t7PG-r6qDYu`KH?&>X$}`UomJ4^cH>J!QD4DE%MBk zMd{r66w7jB941c!!Y%Qwh~>gxVE3=@l#W|1X6rOxZJnj6a9nz5)5iU!TFp}Bts9NS zg?i6`y+Ef|t=_r$diV5PrL1;&8(A}YYj2ZDiFV9;T9tEr7 z|1cT`(&Kj!oe}$@Ix$uPMo(f26dYlMz$PeAAg|$uI#+@8g`9Gewb-mSE4UY2;J%OK zGF@f&j)O{D#q>&p$y`yGtCWI0BTee<9qab7yb181IELFIc)ZXJJl3TIRX%UaVN-6N zv(NJKZ7a9kbr^9t+LgDjFAZrl(;oA@khANCo&%%K8mF^HxIg(dluPeV90tjZbMJs3 zUI5>Ujl>u6v5c^NJvb-1VNzT+Tn`?V?bf#rs!b+^LXu z^SN9;S6NkYaaFN$YrCboSdUQIvJI9BQ)^#etEr+;CzZ;$Y-3TirM<#hWwlnlVRRae zPQu*^`=f{S2SduFQvr56yWhVF^+F%)le}EmC)0d(yU#&7f$tO0O1cBj0q{$_U)adL z=|Nl6Y>?y{gF1;7oQOT^tuE3Sbp}gx-_5N zraWxx3ifI)wOFCf;fMMLwpxpRQq;1uR*!a;-rmp0C?@#~I^GV7NiH%Jc%a)x`a1yp zD>vCxs@x*`rW=p;X^lp0Ul_fIxz3*JU4guOncN(3F6|yN*$qPmyMf|BY{lc6n7x)5 zQz9jKYpKIXB$m2h7$>exh#o1Lvv9gQi{X5C;{-h19zPoOUAM4A zrY>62JbiI#L4W)QHkF^%gj1#$uyf=Yqnm#S^u(w1!`#Wi_Cml7V2B#t2FoTSbdK}nU3l>OhiL$GW$_7HlqiSHg24>wbjyGB|I8cYKF z1^A`k>$(%yCai7I#iKx)lanLi4(Cbp^ssJCp%AU!{axlR)1QDeFGnen=OWap z*gQ^Bso?6u`V_BD0rwrpr7_8}#g@{wtJ?;4?msGx$rjI^(Y5}DL&|*{kRm15>TX!H z;+DNP1zd%>8qLauH}+@IySg~>u@S$VOY`(e@WCVC)cAOO4fT~{j8~Fd#Jqz1lm+u| zI}xicb63Y6R6ck+XpcYXsjKsV8{^O1{vf&Uh3k>Xr^#MiGf1n0|fa4Sxj z!s|iOP3I-~9{w(xnZrZpW@%@gg+J$pe%_*QfzSN`n9&cJoqWDuC&|mh_a+T^mB9U1 zf9{0~9qNt?FPyv3t?oty;KRf5e~w=m8{?l_PWO~X`kdk1o3CtAZF=R+7r|q1z8T-} zB8dm#wCsE25QV$XrT4+_|M<}pfE)jI{OY(^FHeqtG9QCl`MGHH96WSm_-^YI>~}Bb zj%ZBKlYQ75K=x+1^}`8dcs{spbxU+nYtHw%e7nKstt`^p^S&>$F7mDE)$i$8zQ@?R zp|F3N!BAf6sx<02+q@;(4gA^96_a_?K%+&OXTapbvtRHHI1DydzG}{#y&JmsapQWs z{^ZGQsRZc8RC$g8UswOFt0XGDQR^LWDe^fkvHwho@c<1XC&9Uafq|+!agW|v1wVJ} za_{=fn{WOF{_4Q{g!k%Bt_0T6moG-X_O=$))sry5K#?U#OR=uz{8~$ z-T#@4zUQ z{|VcRBdAP?abpF51|ibOvCCLxc$(-CMssxEKgN_yVMx#-z>|1RRKo%mL)lie7O@vs zZXl%KjZ7)_WAgRt?qy)jy8fGQ+!oSXszaTd^m_2r((uFGGXK_&n}$sKiv4Q~b@44( z5~7pf*h9)2B4{OO2NY{22#<(qC!bT^luXZ~5VVBnk@q{(^V$i|qeyG92nK~KNFf*- ziE>I3<)i`$5(^zo!UbZ|fiw{dD5w%P;@%g1opU1Bss@S+%Pc^9iejt7J z|EpMIj_4||zje7ZOXrl3e{aWZUz1P~-_#?^yg}I+CCW&kl0T3lf72&8cx$cy5Q^BVt z6%t5BO-n58`zi3!gubz)ER|)VCYOY=w3DQZxI_L0>g#)%56?u8eRSDCLjFB+i60-Z zB>-FNwP4Q7HBTQd^sApmzYPJkadX`~iDUr1akjqRgU6mx+WI!!j(!2d%hIJY^Nou2 z&DOQ2ECo-n6q*<;*wb#Sn2W4U<3H#>AuEv ziHcM@4Z773pU8+xjUDg>EJt6gCm1H2?ldw7GS0c1dV6^UtW51ak*Q3Eg*El| zQa-lHiakIX*)q15a1XNc6#3drDZFisl*&+iEK?#w-t|U!48PJd9_mIFDRi<%!J8M~ zkCqcrik{>70jW-9NPJE}hGS3PCt5{RF%x)b((8m`t?X^FPDb-6pL)jGSKo)k!(uYr^ zEmc_4+0=JDl`;w3TgEcaBgD5frBckd06ohDeiZ6SW;`F*p}dQ!^Te;rV=9pK`#7Ax zB3=p}=N!vH;6tHc5Q;%>qod%|Ai8B9jIVj^HL$Oep5xFis1WQ8!qyl6j2wOxoQ4zG z5BcPKqBR=RB%E;xm{ZED3$YJeBV;Wg1AwfpuZI>`^7CTumU>OBsm@%Ulf#ZPURUVB z{&U9bAo7+6_z=0o&^Q|B|BrmXik@K%JjBs{55=lr&(fZS<-)l@eC`DaUUxY*#fay< z8V;|>`~Eq@$YblD_yDmN9$Tg{xa8MhBQCyXoY|yTv#GzH|E`RE_Xk)${O;%cT*3E) zqVG!Rch9k}BEKzP2NoZIar#I-nqu+m;rD^_?AiF+qI&@u=8Wqknw$`0;V`s@La~8? ztgAn_L@n<;S|6=nJ)UUOsS<5E%*#t{I2CEEV7KvYruPU*&&oy5x=4%oXN5kX*!Zk|%=SN|O3=B08$bQcS|zn#^uv5vfV?eH0*R zq8&>0c_>?Q2i!MV9IBe;NHhv3pQBNX`!{#Tv(N5u&r=mh*h#oZA~P&#CD%Ytg2E?_ z%3KFN6dkex^EkN>;(45r9fR&z3e(-PG~z=x`KAR=#-z)ZOFTa`oRjSCCaqjaO1Wek zND_QyIl(U#*Vmf1zDzGj7R@H{gj!QxXp+b55S^2g-4tP!Kf`U&&ylvD_Qr^ssQCUO z7vCG1MSesVy+D#vpW**PK10JIbe*K;eS@{+W~rKY1d==XIM!zigj*=8H{;8wCrCe# zC+RX4-Ce1Ky9YMop5DG4J-B1X`-8smao^zkf?Zva&PK=dt77XHuGR_0?-|PJD%jIS zsbqSyDH>;A?coL`p13}LlYc@IZnn$sYO={~>ULMVC4c3@+e(_8*_w{h<;~Xn{$Zr} z$o!F8=Po*7yI0Hg z;5k5e#8TXZpt6&1L;S*}0KO6VBiYZS#QzB85&xrSY92_mnP&1rq7k3W6A5Nvok$P$ zz&MK?Vtb&RZGf-CBZlmLM9F0HwIbQ~hW0SQ`vq3<6ZJU}|24944Cs`@8J!M%->d?B zoSnz#cNxLFJHHJE^Bdi!GKJz-u7_^AW8TaDn#y4^o5V!F}%P(Q3Gj%o#ceUqf?le{j6c)6(LpyY_f0 zq8rYF-_K*d1eT;F3Pb|GjlXlKtG~bN&?c1Pu!-v&3T$9M`G-x5@?G)_)<)Bg6#EhpEu*g zM6vB)NPI#owqx77Ee`pd*}{a3{> zPAL|O#}!vmuY!l}yl^O&cIBzKH!uk1C#KN!v3xmr^u71uirRX(ujsG-yM*Jm!sl@2 zfvgc@S0NN5U_RG*7|(4`mQ6d9|?uvTi501 zYgg3OtswYWSO0_hZzse(sWQerL3`&IcZ|#pS)&8z$YRgWYw>NdM-Ub_@(qxD%mh4a zEsC7Z=E1ZvW)MQdu3x-yORU9MmZOM`+itn#UoQmnvgMzk;4U(UD@5y*{tLuqT;SuiS$l5!H^?ki#42AzX%zjh^7gMl-Ozix1~#8 zRHW~MgF3qJ$C0t(GAp&yBzbgx8br5J{PTd+7)j{40ZF(4tb>(jGUA`%z|O1`b3Iv7 zAhruh+0}!j^avvOeRC6>>vo1dz!)aPRJkbPZ{~uX^W4%{>4Ksfz3l$`6%C${D<-wn z&j~ldWuTnCa%st;LR0RbwjxwGdqJ}sF4$+V@8wfT2aBmrW}O$9?z!fkN6dAxSVPgn z-I3AJCkQP6Z8yCMw+(pk$3KpaFWkFu;a;8_x00P#4Nd+U3C=;qG|%X`LQ~tatxR8D zYu&#V)W`A;d3%w4dtbf0u)4LTD{|#DGHm;7RV7~{F67I`XW;eEJxpvCy8aCmWg?p% zTv$;uj17SghPgt6*RcBL9o;@#^$c~1LXLKcAW+KX-K%fx8+O?hQpszAKk!|_ABe46 zZ0?`BRE_mfV$}|b_d2_1ual7E85B@w<8ZE*gzR-q;W_mdY0MS!G$`)BU#71qT2LC3 zy65f0JDt0E!R*3Ng?2F4RJf>Q>B{N6+Q7y(k$)HKSO#QQBxqTF5}PA8`hETy02ex1h=SG? zT{1h@Y_KjVvZ?dGo$W0MESZ(3E3__QdKjp2Dzpms5k*;bjZ2{=eTfG7HdvmPdPw|u zgD(LnF;z}Nuv<*m&qax+`G!*cZWN6Df0oV9mP*Ezv*QnuIReMyIXKR0Y%BV)-brUl zgFzy4as9Gzq2|QPXwr0by~GbQqSuCVhIm}ol*K=n7=`=VuX6m@d8TWA+!xV}Me z$B-7S3H<3Sy4I5tK+|(tGws9!2&V)R}6@ zJ|b%*R+`9eGeOO1uMOI2KVz0#pcf7eHP!?JZUA$xSm2l=yUk1E(lZ-wIFs|`w$4{qDH#SQ#Oe29IhUj-g$wtuGQmTNmEN?xOl=fbN}o z(N0WNUjs363R-iz&khWo@5^n4#?hr<@DegoFNH#v7^crAqR)zFAKgJ)+uC9_8`(j@ z^G9vAqss+*4nKPv_A}6Zn?4op1y=UVs9*r{?R=Q&wWxwFYLUT){&Nr+WJs;OcoCUp zMCm4avzyK-{A~xl(+$KP0pEB#_6Yhi)EyW;Dx6o6yW#gON%IOLu^VXW9CveVW4*^w z6uTz|{x+>_>(OtpS%q|IuJaSs@xA*V<$Xne!Q5wFrj7UwB*Lrcd(wc+6zP+|o>$_{^g3z>LbE9%>@b2&AzL3{a+q_Ix+&gWR#v`WH@mE&El6~rV^{|&<#Qua8ho7GaB0nsbQYhia$O*D!1}GNDNH_3*E-#8 z>>lG7w)yCpLvIi?9|n=(2S)@Qx|hkEn`*8I|2=juLBEP~A?|B^7=Qoa>+T;N0#^9Q z?}kQ^9S7UImgh*q5n%K&v=mP1p$&I|S#&q-j6|TqOij8w@VZsP=idT8<5}6@w{)sxWv3t6o@s?Bh^vlz>Uzh{g&U6 z%22$aPq{zgylfY>Z=ARmMiV{&t-wLi7#a$IqybJ?aN(9yYRk}3y^U-<{v& zFnbk>U%4N=@H5AP7Ug~Y_xK#z0?GDcu>rr$t5xJ|*bR1uD7Mj`u&$0#sA@p^!eEb( z(zY$_%S%<-%wsR3(jo_oZeNjH-s4rbfpnSY{9&S>u8_ZbO69U+ zY8W}2I?25rzlUH11H3PjL}buZ-%#t-dz3k7f4={Q_TiE7`f^WQes=cLXqWzz^-$Yw zk5N5@?t2ERK|mcGpC!Zz2Gt1gOyNT8`4#A`#&Por_oL04gAb|=c3*`q)FXYxDfmBV$`FDdw}}!h+mJd zPddBmtBXnfs`6mIy4b$I&z*&ok1;dgY%XU;*?drYx-zz+Sm_DnfH>vV64ougXPvlm zb*x_#)~yvvO}SuCQ$B6%XNPi2;Tc^uMPEB>N;;o={GU-<(mCE){5QgyM0nzz?B1B- zg5gL2WIhq}6QDbAEEZbibR6n?4gB)x;`YPJTh~`lGZx;obY!4MmcM4vs#|z_v|@ri znoDBQh&{R%xdKGyD8AH^B-paLPE4IHsHa2 zJyA^0>#pequNO3jUN4yLDsAR%Jg?2>J#Ryx!!l7_kxY#2l1}c#Iw#6sE~-0U2GnAC z?lSC;gk{mIr>eYKgY9B7 z<=ydZZ_pVqzglHUd|-fvRoN(Q`0@z!u;3;N|lnODHN_pa+DIcA;e_MMS~l|A~B#7Wl0ibnNmdZ&>5F3v76t+MG{$# zWxAXs*h)Sp3AQdNCrOU>RB3YpjughhnF^Lv)-_^FNtR6!XevHWj5;L|Vb>53G)AvU zK&*2hIM1ex zpDz(%=soYcvq!bOspMrttNBTDtKsF6#&%WrA=gVqtwn3}O3%ZtyxjZrO7GA>V1T{? z18~;^<^X!J`-sIP9~t8+F%Uf|@2$irb_7R{BRF~m9QgY5o;lOZIoHphe|?U5+MJ&0 z)4FuJ;M%fTdAZO2<~PquHFL_!m+5r!qIF7pRlsZLs_8P62P*A)WzN?f24yb%Z&`s! zqfmTZnr~!rl$99Ms$RfeLTZeIK{23oXsC=3lL(PT7f@pLj(ueS^bUX?^ z_-th4Gw{3LE2)<$`;HzZEeT{9oPfF-cYmkb73lys+e}6qj^8~6_sbZZ#~#EtHUW#D1@Ooj7GFXX&m(KsM!b<(^GG1i zqF}^ZEDq&avxe*J?(V)>cjIh6q9?dY-Mwbb$1JLc;Sj$_k3j+4;0od&A3Zv@dTwyls)4S4 zi`Q)ST8M6ju_y|#7MOrWf%$x90qm@!P!w=0c>3;rw|L-oFbCmKkevW-3XeAw;_U$B zViF&zD7KyGml?E6M_SEtsSxWfD3%_mmT?I>)C3JmH&J^s!XqR}bK+yc=LmV#6QfG} zEUsD{Tq0vZ)qIS}Ws+C-CG$eRcn+Lk!&Oerf@3*_=?u6tG?y@~33r;tf_}j~2LJyL zc?@W7I?~V0ROKYj(6I^S2@_REeIZmot`O|PblR~ag=YK_8tNp)STyFFpjuCN0~#yo zH}N@-nF!!m6#+6_4mxBq5)?f=42P`v$Hnp2;oE}kb!zY_e1m;n2Y!V{4vyXF)2G?E zC1KHE+kTV}zoGFi|0K%O!|V-gmIPuWgQTp4`^hf+Gojw`UwfMQ4P2;|%mM12kWH3HPsm*B%M#;@MzDe>Si(Q762rZI5m!K@fZJygDD8_)FJLwoDW8H5;fco)G}xic;rTppWxWM8UOsrCqed; z@z{;k3L= z>IX=Y`468y_SmP7#sAQ}MDo0``}<~F^AhDFA3XBN2l-2yiyv0@4k?#33%XMy_7$sW zhMKyG;W#%kfe6U?k@ruY+~sM!S3mDweUYaTXH1R_e(lMz#q;)SzP4Ypn8@_B2%w`L8og_%Q)d= zk8^dfJna8;HzmOg6Rd0>aJt+MXL;;_-Ma>7&fW4p_rxdGk|IaBbN2&BHqYv4>XJQ+ zWO?!Yc=cMjz3TVhAFFhidMe#MU&!sSxnf`2x#KwDW-D+O8V8kkZz=IOifpbEev{o% zwBhirD@&%87!kLZ+)^^#*%cA+vzIdNkqP+O%i^!R2XvuO{I!U_H!=#o6~AdL0-R@K zSwh_rSA&REjfSWo{A`DSpIwEi)M7GEiMz7KS!;U13to)>TaL`)+j{(dW%a_4Q)~B> zI4WG=nXxw2-8XHy8|B2|9E&qq`PgHMEnywKu4X9ch*)XYknh}up$*4Q?seDIxt%pN z$_F-gw1TR)6vx(YeqgMor3U_w#TS(kE|LZvvfU+p@5es`A6()T<4<1s5U&Rp9gi=9 znSJW=38;N4bESDDX5D@Na-T39@rz3;E0=YJ%NeCVAEFzgOUDix5uh|!^3&maxizy8yIZewEy;P!C-JOkgu0W z$FjiH|Bd6c&Lm)yQVd##O?q);1e)R(4k`~`7zz##5BEj<5m1HiSYL-9`zXg}Go1JD zAbZ1v@+EbmbV*F9;*w;$WUfGY&dhbLbtURmZ3Ff>a9=cUbZuNz@{RK&)u3SH#Pv&0 z7|i`6@gJN(`I5iE`3h&gnBTRnXzh}MKzZSqdb(ynJ6$zq@dol2uPqui&e4ZdIrbeT zxypW(!ahFGH86%>@Wc4{IA}r?8C_X4Cuu3{??${HrL`cV<5em+UYpj;}D3IqWLa z>fnD%l!bb=R*^F-Eih93s0G%?NSP*+{jUk$*CgL-{nk5V63)+6#$Qp{O+MwgVt;%; zn8`l(!#{Hpzp}4YU6h^O+pDq}i+w9LgFD$L`UgHAcLw7+VOc=P1iu*NOwssB&IFpG z(fH4~V#M4{Fk5F}4lds3>JWUcnC%2{K!Eya$GK8>e*E8nCEs0oj?2F;TQNJ3+dhB% z;-JACF&hR4m9EI#b;dV&-cxu^otcF9lsH{S;cL<2*t0KuWQka&!Jo0-KqTOUKLaxx z?)?TIZ@V1xo``*}nv_1*wpPUDW8QM;(3WsC+G>NUaBFzLKLDJ2f4J8_5C)HWZH@|} z6I8?fp#`4dZrBrzNq$#}K*ofE^)RXp^_ei=$zV7fsg4*beI;;KNb;;Et)sEV6sflL zTWmH9@69%rFRDQkO~xs#jwWkuYq?RjtG^!<*lX+~@Q3tW+11Cn zN-oCq3I-=ECM1E{Zc~|h_*)egCqe zlCpfQT&wSxH@{cg-R*wL;&2ryHO|4%Mjgd{V)|A_9**j%@o=C-cN2>z7Ffy~4B@SEQii zpk7Eq%0c)LE7NjvG-u@G2;n=}DRFYteo;=2AS|PJ3Y;9ZjDKQ^oE#mPi$u)Hv10-! zN07f`zIOfM(Z3TXM*z>uNjW+0nmQ*3!iqQrZPO`Yq5t}eW1;)7PS=C;x>I1GCu$i! zd%h|V*t?;7C-zP!i@XZ6@MJ)*ynEYXa06qVJ^@aa$3V+0sm2a zw;QDU`-HpQU)>LWeP8^C;Ma+Fy8(>jk8?(ZyWNHaJpw6m>F#rl z3D>#I4kjP&U%ouzjs$`wYSp5}vm^g4I5hF<5^T|akCe$SdU9~(7l z!;l`nQG1v7@2CWC2cH#B{W>pp1p-|G;^B}(vu5^set)mSoWcD0P5lHNLqofC>apRG zgTrcFb^PRrXneI~|8P^DutYaO07xx9Yf1F*&aM0Fuk(!$j;M96BGa0^y{*l2YHYG$ z5|^|P^Ox7A@)0zpcM??XeQ3PS?Wm}92Lgy2+FCw-Z0GJHf?0Y`?D!^Ujn`J<8MK>< z9B$M413TL7e!G#kP1o+ndi+Xg$0^8pE=xYom&9Lt3rItu_}jbm;jyt1a5HTG7+5k& zbZJ<3#Is#2cqmhQ2%7c`Dx#_ovR(c9s24mR|ASIy3*2<*SnRg$05WL3;M7Q$YM^iP zoiu)U2IeO<(o>ZA21*42_b2}Sr0?AM6Pu63U+MBSwKwsOfxCJ)tLrKdLwf~2t5(82 zT7k}O6Y&f5{yzQ@_~6nQCy9<+`H1;1>#$z{>%^0s+q42A&%mI;YK4jj@(kSb?mch_ za+Jg~@OoQybz61)B9CX0M;Ymw=ox^+A$VpDM^3`7IJ>aD$=Br@yNKFP90PBI4X_XDU|&E9y;)Dn9tXdKYr~LL3TNBA ztVLOES?jX4Wks^?&4QbjAI*6<@Fu6H(Km-;6J^7-V4|bwa(WDYzFZ?ge7yx!TTj>M zUAz=4P~2LyxVyDzi#x%By99UF;>F#apvB#ay9Afu?(UcW^WN{d&->oHzOzntc7FS0 za?Z(2GHdqC-ob%BF^26jGG+_Df4<1-CS(`k3N;k#FtO79*=+=uF3*8)lN}t@Je=xQ zO3SBk@RD%wbgJfNM#^Cu>~UpdrV=*I_u<8Fx=+0)k=9<#e-6r<`Wdu6XtlUxIkkh? zAJPl1gN?yQ5~4ptyUdtNn4KVV{HlaCeP;WU$r1b&_o1&S11(RL@7}pCmaFiCh$KIf zUVM%Dli{n9*9qxV(1-a%b}*t+PX+MT(_(o~bsXC;!|3Yb(WrIdC`Z=#lZdBedaDTD zE{k0CMRe#>3JFtm{2)G4caL31T1J0E4LjUBz&$Bl)!j4D?bH@Q6XJrdo}>2MtB=uM z`m%3}cGDJue%gt*8r0J=anbJ3h#s;^hR&U>_&BvL*e!{^7J$J|M%<2p)Y8HG^bY-i zF(hrG*yq?5<>p|GuNC9*+U0Ml(h|j#Fq>#N{aNt=zjlzswP}dpo~n;iBVq`ogJjh& zWVO1d0=4g$Lw zEw%YUi6x)GsxoP^f!rgMpQ9Ds{uCMM%hwD!_vyyw6{X{4ajZAVg{iE-Ba%f&pXJZ_ z_~*ffh?5^Lbt$KmqyY*EfFt4&%p&XZ5Dv4=gyi9m}HjV7*#kd!WVkLNl zk4J}tI)m)z&h^U8^i#@Um@+VUVwUdGCo>4!Ph}-c8>n$m(B~?yKG7~*oJ^}Omy5Y5}=P06_X3?;eR<7R;YaLSfe1B_9 zIYoPbJomervo>Xl%{XhhdFfmTGx=kTBMRo$>T){8t039$?H5zI(Br|W5zCZxab4P? z>ff?}{t3Fq4?Xt6yWS-1ap{cPeMcI?=Mm4$-YM3bWCvkc{_p1^-*_Aa+u9>+}NJ!d$p}5Dt5d=ClMb$Rd*O`mf=K2 z6)@sk7>dJ+V3VYU{!%gPxcn9gH)2ee6kv?$nrJto9mOM(FG&~wHI_2}i&3%oA1-PR z?-2tJTtb|my2$b8+68=3MpkTmkt9*USnR$`g1o{Re`?k0GL=%B^KE3<_M9&TPLqv; zifT??cXv1K={->EId5sw9xe+*1rLl<(VYynO3T~nK zbiQnAVLDyC0ewt?iozT%`@c4a$Dxsk)3*A`WuJ8m)9usoTjTMz8keyq2N!2&3AN&7 zpI56&nd{dlcD5UYPq1`KWvGx`O$ZO9a8WhnKt-oi>$(`o36xd9hG1Fztc%ywWXwxc z9WL31e6SF&i`CR9&s9?emp~wd7P57Tnp)*~v#N(B*APStQO=*%`PAUw<)mtfVZ}l? zs;oHbL}1yOLJRh~rbQ9!5-hqBCAxApI&fie`HV_k-J*o7@>1!(Mew4Wb)`wEh6NI* z)^YJpamLJ;1zsI^QTDjvM!^A+Nr#lrq#9Dv19?6`y)NJr$;ggU-35ao#+?WF*Y$iV z86Oa9i#KyrNeW0+PkVTr{F)`6%e<93ilt|+56rLgo4=1LKOazXW@oCpUB|miv=wJ6 zkAv}M)GgZG#oO|MYTTvYuk-jc+A4sml_g)UBOl~Q%5jVRHH(8)6%%H}OUrJQ!d1jo zRdpaUs}^EjZ-JgU05LIWTh9#uqZXK;XAD44Q|%~S)8hn?%=&WbGXjWa{hag# z0c2dhX!=-I`QGZFPNVmJ$of^EVZA;E;s4b)oQLNM8lCAsz@8_r_htM7^Yh1dLbl&Q zv{g7VLFM{H$%Qsb+&%RyBqp8CRe~dd8dY#3!6Xo}1NrNc2h^QTxhnP}*p_aaD*huF z2Yva0E`hxen1LQo{J<|?J7Jxe83M^sMSpr#wEO;PhQ;jyD)`aOWTSiZyg!1=tHQqt zde-ydd55k7Dvh4uKhU|(d{AgB5b_Ox>hZv%2lYU0d0?Xg^Ppxth*3eCP*)xpxxhH6 z5f6e~&@|MM2W~8|25QBFG!}FT^=y2{6c`RQXoP198iv|8!d3)=p%#sZ6+wqk_eL0- zz%-~yBZ5uP64bd7?j*1UYSV~x67&f5c6~=07y{LIg(nT_huXQqW(F2P&0P^QgLa{A zt}wvBB&hKnUMp;6x8)gMD-5Vx>x`=v?zG$EOvv*cS-0dFt0!z$x9J(LCyZ{l`WdGu z+*-HGnc&kq;qH!g;WOPkWba^IsLvVw6JkdX*%{3ff=`d|UTf*EzzD>8J^H8~E~|d@ zqrXf?eWXXd=R7bp8(58$OhM9{Y>f;|A;X&=8p$gH={M;b=_-Q3n}m(j6+xDpe2q*M zA%~lYjbG%tBW>A&C;qZ%;&%x}7@%%8vfB8kZJ0Fj+W2W1z{VwabC|G%E8EhihiPqc z@t`&I?(@Jk^c>q!k9CvSa-IBgu%%fhaOu*q<6b7Y?QCkK*|8Cf!uX+v+PO*sd`AUpD=cBdYNjUU7@PBIW~yc5m{Vz{sO6}d zb82SHW^%sT7LW;;9x(ah6W6a)>-md}S>3G8bOVLeGZV|IC++ zo+G-?!j~SEO|k!#FEuL1aG!@SQ!X2OpOP;{E=PHvgD+z&n|+^wFKsNxVPAkRizypr zpS(GlDMxyrtvS6Sn|`0JIkh6ka-Xj`(W)|BHJvX^!MRt9yE8HqAb*dunEm={~P}CNLXspUOSuMZ9tXd_~wM2bxH`!flg1 zooKltZIdUP2)V-alF6DVx+3tB)164V!ts(_n`pQq@sbywh`s_nU_2*z=VtBWJ&-+1 zbxfXKAwB=}nOwWVphuLTHsNXv(sCniu;X{JrE#(2zO`kJ-bEIONrFcXAU1W^qH&+^|4@<9Pp4hb(NozqN}uB;Jj5 z!(NT&-W+v9S&hX`-}+R<+oO@$t#z)ln%b~#-pVMyOVCOJ8fSOIJ?*Sv@(lb$ z*f9n=OMAlY7(G2}c_Qr?Cp!yy!t@!*IxBi2@EOxROM1fb8C^STcp~u`7e0%A0=;0o zCi!e+-Q&HGy$*GppFSbI4*Hz0Jw@lBvP^sqK0NSm~A#LtjOnzX*cZ|ie4 z^kX5}+S22N(ye&3DdE zBpX>7$#97Tn+Ku||HAi@nE6FuXJ}}MZK!O>Zs=f$Vkk|aPZ=2>!^I{sn`~fZEW=sP zRnJ;TutYc=Nzmtruc z@;>#H9t)ZkbF0!GVzwG}n~ynQR(Uw=xeUMe;(MrD$t3(bXaD?suT)op4qiGl}-Z}J_9SHyHKyu@bKL$thI{` z_K2B&r+G@^VddNfqc|R45yEDetQ}A=jOmQSqe{D$nQ^-9g{t!PO7B&v>eaE*XAo>k z+fb}st~wMRd=XEJrqs?c&nGGYOpF;nlK?vBSoSHjaU_FhLc*Ke%fZA~nkMeNDy_sO zhz`Z@4$1r53g>!>bKnD!{<3e|rHeOoOEfqqC=EwYrwTWx`7Spb&fTWb#G}L5XDjB% zW|Y1osT>ZonGDOhqr#Y(0N~+KpF={DzT9VUjBlGmCn>~vm}xZ`w`HO|JToKwTE;~Z zYq*{!-Kw=S9aK-vM!ItW)G<*BZ#wvGnpC&KOApw#=(M{ZRS;p>&lyoO@)tXbrt@h4}Q}h zxhr2!iiI&5bHp7V>-qOyh?p?t!Z+azSD3v=9F% zYsG2*rTm-xOVOXi2yY*Omp1Xo!F{gU3lSG)c=(5T1Fm{<$^MNJ_q1|Bq3u);Y}ky_ zSB#SlVry7l|3eWbf54oK{^c!pMUhK}`TT27QtS;~6axM`9bPoP4h3dioyNT=HjyPf zDh|@n-T$Q&qCRJx3=aj$Tp8rzC7t&fx7w{oqxfjwZFZCW*GmGBb{`SmbK_pWxG>X$ z-DGsQx5yQCZq~==z&#o<;V%!!lk&g*6X|jZsr~DSPtW;#!+is>kueFMycRxd{G5~z z^H#cw%gy@7LL)9r`|vL$b)}q}h4mc2S0pA(^*}o*AL{-8K$=(6xmmc+wR=V4%#7~l zlhWZp&8w;0B;2;zy@G!b!Q}8Spu`nhZc@y-iBT)ThHaNnr;JE_nRL#Mp852*AW! z+(CUyN$y#vU`=EWkn2#O&NN?GIAl5SpSF;@QB!xYcl`5D4LZV(_@@Su_T}p`P?~)R zMhIf2_GDcTN7j|OGw4qGWprN|_8)w>_n;}LcGAdLYZivyv@KT%JtN}h#o9vX^54Ik%E+f}P4 zG2y|JIKo0t_nXBXnL`_V-{^GO+P4 zTzGX!ZmC)N)7dt=d|15V0pkW7y_V2I+MV6KiB>B%c;tMP8*Q4_j8j|UDwG+*vFy7! z;oJoxWgPO{@|7%tKIynr2J;`_3vLf}K0VpysC?Zf=@8syn$O&?xUf-?%fe{WsuQv6 zY=b0NGCW7k?PS8P{8sF6nKNU_7xnHwcU{gapF1K^*J+|1wHFK+eF^QgFPKP`ar|Sx} za@~p@MLE>EX|FuU94G#e>u9&m!mhJMTpTT~?}@9kZem@5S&{?Th{HAH$~KS_cn#M) zYYo0d;X`GiOe2V`{ss{Ehzt54P2~ijE9ene!jKMe1n|8jYsn@|DLXk?@V>Rm{=D!T z{nCoW1hE^|X_#w{(GsDX+^_0~p-{vFNF{_}5r%(dZ?YhMmRZ zILceGmFNZ&G$pWvO+##p6`CFAn31VBziiXxAW+U+@d$^j&FY_KV!s?^X?UppB*oz@*eFU;2$9eKWK`1BXw$4Y3y zWQ{N`i&J8aFwZn{vZilOqlDGFa3MOM=&gQjHt2RB8!FYOxfeA{_b!*O)bCGEwINjM zf6vq3m`8kVt4|UxGM^nWGOSNL=)nQi^Cj|Y>GY(OM}4V*pgEX=O?hh|kq#zcqTNxo zxvRJ5y)m^pdsDFC{=xxPgj8jpm6MfEeY4=oLTTFZ0->$JcN$VhtnV}=j(pFT%64B(!OP@DerQVV< z^YQMf47Q)n478uB4sf+T%){6?9i7S;M*guL<4tlKD_CpdaX*4T_uOUM(+*CT?rLJN zWkSi2o^0X?&`;|9`3sLXsVzM`4qZJXJa5=(_fLZZfgO{av%rBiES8gIc>D8G$h^;);-J)$E% z-f&nyAKQq^u4rZm|K-C%$Pn)$v*?<$PU>jpLq$o&cgN*Isl_1@-=~dgn;vL`2&(Wf zzU0y0w@DegClI!2UYr-WX?a?oHZQwx zn#~t+V$=c+M1hBiX0*ui)_}K`bAnSURVR}>NDYl&*6JF~XBlp=#rNB+!bz{soXhk*#DO(af?gk{vV=% z%tcCgGv2$3_L)QZ0kiPRL3%$VVXDQ`eTHJpF_|eJRxElhhg?IcPe0X`f=5jyz}-uHlgJ`8$TOu^3^= zULu>tU~ph#0!T~knn5izVSz*SMrVDcSby3p8x7=owqbRlwHX|Bx=GLpf^t_B>~Ynr z5@j_`?--8 zB%7G-dsi*3k~YJ-bu~2Uod4V#nYh2we>z9;eZS32H?Jzv@cQq_ zhvybY#q_L6SSLFk!h&;<8)VX^Y$1}nCDep_<;bM>*jlBSwZ=VPrk%OY;%@WVpHtJF zNOc-bi7!j^VAtVJ{7UOd>VY!So{KO&M47NS0{3CK;1$kUoOk5izc!fH*LSe4izE}g za{e$MgV#ZcYeQ0QeTtzF7-M}IMb6w&Q7eY7b%*`^ zlu2u`_50O)AEPmmEC0<;am+V<-+R_L$4@39P!svG9n#gPld$}9puXx3Ob znM;Gsz|M*gHC3l%Rn*RPpO;)-?$8Dy|Nz?AC$E=6% zJ7^D48GR?GCASs2AM&$1l}P{JyVajtD7ksIw@h2gf#3A}@bck6KQJ^tzk~fk5C5I( zeelso*qq;R_99FJX!^gAE)Lk&Kf>JSyb}}o{uMqb=aG(?jDYsY zR1VYt)kvaQ)Sm1@S;8Rw-5pt?y*;lM>S)KgDf>&Ww>JwLczZ`DXT7d%sdMBQ*~30i z{iV6-%MQHTl1rxelH(yiVyS1;|JSrxuR7|dV6&m)F7O-~@ST67+QdL9>jWTNEh$3Y zM>yZz$%W|5QU&(=iFm_R)gd&45^`0QMY52m-cLyknYQW3uo@Toa-Whv#zWiWAtIk1 zlTNG(yZezOI)0^*t&iY)md$Ktm}7u z+SWoALgi11)JlNhR-8ntwJjmRINGX)p>JQF`L7SY>r2!C5qDT7*d~@buMmUrlU2X8 zxb(k}Sm;&xrJ#=J=}EwOp8@%4CztJ$sisJqIV@vg+!&9?+g#YL zReoaG2SomP;gIPps_^gtZQ_%SSSHf#jg&D5jYR$$1b76M4|^Za!q^E}dV6bhTBo}F zx|rk~ZD;A2NRKuW(C4!I(9v(4)NqyscBw+h*M8vX+S$4y;8i-Gn*B4u>+-V7M0&hY&6K&b;fBrx-4sy&ysY8l_kvLP37`s%U?-#?V&?E>GhMfC_s1i!% zO7%JbzM}z-3SSZehYwu^LD0z7Q0`0I)uAO<{!?g1t8~`+l$QD0@V7Ra_ZitWtBn;8 zNyuP5oU;PclB88;F7CJWqcHIkG>W^6#@=~jFKdeP<*Wk3ZHHfO6&I!?zcHru4V+1G z6l5=Wapc!*UoSt@rcVew?$&bkuT96Uu74vPVZQk29U5}MLOMp_O4%{;ukl65&Yat) zjxNxJmcXJwg5q&}hh})G0?Fcc0mnefJ&XYTuQGY=Dp{$DC>B309V^ug_+NrtE=gC1 zA6IEVe*%ip26c6yh7u6cjUSo!=6Ev7o6>5+;`M#8%yck z9p@zN;C`f_K=Hp=%s**XHz)`w^Ng@`2N}FKm^CQB!BTk>FyS9+8B65#bnT zN~PE=9eedV^CJB!)@Yy6E5A~JnfZtCJ~NzAHfn6wMJ{Sgl2I{g+&Y_x#L)0XSS!ux zFcO=hQ2b9ejluzy6zXZ?U{6YRIiEeJI zdHHvmy|j&V*b}u2Llp!1I=Obbw7q#n536?gp#w$KHVto7)<#hioXfhOz>o2;hV-r8 z*1#mN@p<({31ev0g%(2D4{)~X0G$<1b(m&v*@jxYYB!yE!k^soDK!Fcnd%r_Vkcc{)irjT%#e##?1!E@fDpuvObM?KwtJZ9eWWJtv!WPt(a z;>7;U>k7n$FW&G`f0NE}JaKe;<{*z9exMDlMq>5iQ{5o2U8KZ2B3o>mNgh3?41#I_@)!EC03U|+cs()bIj}Vl!1|Fp6hwO1R+D8$NJsuF zscAdcz(*8AMTRJJAyf^Y0}$@jZ#wCIxnNh(k8* zVB14ZH<41WJ%7d7vdMLo@Sqi0qF-T-{ng+h67J)@3pEkT+AzK29qVDX74j~*pHy%B z*wF_%(|SV3Gq10OPugI6BK7GcJEwUfnA>n){*`7&Z_}S~hjP;8<@yd|9aA8f=Iql; znCZCy@b9oIPUWU-Yh*_ZAtu%ZIcT(wO^JF)=H=5j4O$}OB88!!O|%%>dGG1IA(XJ- z6l|#YODUSuNpF{ss}`23WEd3VLJFK}{kSG^7bW=3@Qw@HYO~O%^!cRv_bD?9i5$a~ z_R!&w9Gua&)6>&>crbRiJ&DIK)3Z=)De<uJUz7#vbo zcyCE`?I!9dPjm3Xpe|pBH(o;OHM6;;grWRh@F$8zrMDqZBn3bHCqDQjv|p5R1GV`#x`W!$X{U4#P*(1a{}S2$CPu4pK|y`(mk? zhK0MoLT+*8Ez^qXdMF^}97+ZFCoKXhX;VL(`Xj8*e$>6LMxdG;=@Amh5fR80?(^EX zN4dL_acMgA-6U7nnHS%tW;&Vg>=})B9V_qd267l?i~?E-9Ne3eGto0ppu2kwxid7PXp$OJg!hOKdufp@x#UX7HbKs(*P!8_aCb5DY)J zJ`DIBV3PRU1;71QJa(zGkJww}ZDzw!is<6!R)f7L62XEiCi&;RrJeIwON7g_kWywf z2M=&lTQg#TZJi%PupD0=oLZ#XGhMpBK%-l+Wy>+YU2Hwx=xE!A=G?!%T0!lqNwShy zIS4*>eS+Im$1M^3u&YdsK4hp){dH(8-!9@}{C%07rZ8!-V432+Ny@>^jrt+mz*Xj7 z<%^P=#K6zkgMtyGlj*$``R1(xZ`q#A1$&IHk=S*FS=4WBLi@PeGU&o2w(2;kE9o;B zs*NE4p@zPT`}`(xjPHKUML?+p(aY4YqZ$^e2OGx9v{hQ}i}BsFXHzpmz{EwGW@XR8 zD@GSBU@9i(l(q?}ZoF0D!Ozs6LCI42I%!|cEwCiAEYve@$P{}EbHbh}=+N6F3yy_z zn`46y!WO0_ZXx0#ogtb%-}d0V_Gv9dt@q6>B#%$??z6zcysiCbOHb(0+b(XzZ~poJ z@+FJC{H8fK%IsElBYg9xKVgn)kQzZKujUAzbTt4@|0sn(YQRLd>YoWQ|2mvi>! z4W*KNuc8A#O!reOd@xS^1X{D?zX37N{mHSI&lR-M2kd@LSq-2JnX6@y4rvjTp(=DW z=|3Ft^2y`vk?Z6RI|fxuagyA6PY}wGtR59+X!%7SQ2scrSij8n)Ky=<{@k_zQjHkN zzI3H0)5=8UGmKa_Ib53Ia@xiyB~P|^r*`k9R&wa`4Kh;h-TT@ec9HG0bf}!7x>o8% z_Ic$Rzh(8;bSD|PKBdgedy_iN%W3z@Q!@rFvNUl2UZuue8V}4AxVC8q{Zjy&g81fT z--JJ6G1~hHPWUN>?N9H%aLhxGI3}Q09LrGRnKfwo%s5ox`Xd**KOP#ovlV;Wo{->x zn`RF8-FKEq+a?})R*ri=t{Cy>Khb`7m%=}(`SZ*nNB`Z?9<_k2{Z22{& zu#d+E@fB5Aj|a+j6N&6!D=O#vp*;1dQO1uawc9cuHs>q;^brDIoGa;FJ|Z;={2|nA znI$wWewt~J;V$o-Ni^g5TXd9&keP@;X}0>X%uka;=M?Y0^pFbITf&@N$h(Xqr&!v& z^X6-I`lQ^V=r>K|M&z-D8$eX;g-gUATXT#;SgnIs0b&dis|I6ZYmMJT*{T0cf;*oAvuC5tN zox9GgQt=jcAQHqT_J$owMu`( z#;C`eHaHhn-GsWJ?J8JlOSzQ%U28rj2nlnU2T;YOjRykJSKo-iR1SJ(pfubgY zWSrN5Gt=x-3i3a(1;1De>f+5it&X^xn(T*ml$yRYXWEeoc2ODqdIVb4DDz4=>D%M zcC;DwsMH%`55-L;@UFj~GN4!?r&w{3Xa+?(uHo;ub4@XFOnDhQ&n21-!cl5U4exts zeixpQdWcb*l&^gd3Vmr)oqs(hw7e390P@I6`v{*EIA`~eW&n|qD)bx!FR#OOveFd3 z@3F5J?Ldr|2s-S~hL@um<(q6Dyf`fez>|LEy)9CcYpwLYGq_3e)<2JD5)a7Lj>Ah} zHX~+|AF}KpF-ZK2Gv0>V-o~IZkdtwG>-xdX9=J>`f5I5Z|Dd5Z`u;lN5AzS}!#~5k z9PyyT;ta-*6xhVYj#M<%-o=c3>}$#W8;PGp{}9onNERzc8d}K7ni`9rgows>s$K*> zJ?3Z6ZimmfZ+G;1tpJvS!ib@~GxC?5P=!(>e!{dbwXIq0xmy+7;hLkw<_VMI( z3QdZATS}`H7nE8`^%jprk&1PSNVr0tuXsahgpVf7kED%*OAksUM~Sn8<+7T8KYzd6 ztrm7&MZF0b6aMwwEs9~JOIIn+a}#e|I!lTSyB+gbDq0l$ATG5lLhq-J$AFxOu#3oz zWEs%!U({*X6%0*)K0~WZZBzrmeX6o>D5X)wd?glc5GvwIM0r)~V)p`f&+FU+V~DAr zswzt9X~GqY~ zZCpgCR+;@PLM*DBCgpXLY5sB>CyGT>NqPD4cezp$75CsqeQ zT~n$o{#mnvDX9j+r1N>4Ql3h_!Vv^us-rfw+?FFBSK1i4>-#|h!QVfPRWAWXiq}aS zDrOBO3=`!+|MvZ!pULp>YGn8GRLq;)rP{uhjigq%&~`al#M&gfoKf4di2>9*7{WaY z#h;b%=I#t+P;nAM)H5qg)-q~HJ7*0l)xZ@;E*oV;ZWvG_3euB6m4ydb3)kN+A+1Yy zWN$ev3AQ+=L<}pn24nh2Rl&aQ&LXqC@qz+wQID9n{li|5Cl_rKuk3dUj*7Br%~H}R z@htHyDOd=aakJKInX@rJViIqqrxtFVcSC<1$WYQ1i*}0G^6tNaA-{VAsugI{bBtxy^}pvIX5rZyt=LA zxmWp2Hy{MJ?6p+ifO$^jNoZylmNut(Uz*m5V2&9{-4Fa7?hdmNeHq>cvB7_-lLRVP z^9-B#u;rT@hkxxlZ|GcrYTJ_XWF7t}_1xlA z16qj1RUq7qu!`rm$AevZ2B**c*sCSwi#7-9MjMstWyKd&vSkteE!61$kB+dYoV^2k zI=qzpx$ZG{*g-tDiXqO7lRig~A4g}=BMEFnS;gw16!8^4U8C{8M z9ek(O$1ie=S&){@^;2nkPuU06a^3Whj3F`7p;%jt|?au=%}d~>RF|K=qpD5hCP zs`%RDz+8+6p*^_GXNL^u;~Ze*zbg2UXA}TwyriwwPD^SaYd>~NFjVsRbZbgZ_r4(= zv@~m~gHfEyZHkztj;}vDRj*3RtGMh9KD=|v$}HL~>Az-u0H{v%?-v}PU z&|gbk(|BdHYqS-DrW?TA*BR~!?$X!+Gc6CKZ4%bA7<)*NNB43dxc(sIy5|7y`Ionx zSMl5-T5~x1^^&`3TM&m-N2TyYo>N}Yl~Cp>f_s_zCgi{l=usI!dg^q+w~q|Os|?TF zJ3czjrEzO<_qt{PWphi7pWj^E%u2c%wuL_Y0EKZ&GM{^&2o^mFc*cN^yL9!Q=uA3R=ceHz<`$o8XW)F1Pn zCm#rXKt3R^a^W$uBBH0CFGkN=_t7?&N{ptGdA{yqL@!)KrA*KM-X ztTOe-qWiTOjE9DYhW)I2mphj|$>)|Q)d$vBn^zm1v$;FVE9@7qmjL&Sj@j1|??s;( z9~I%vj-8G|pLNg$_kQkc{cF@qK}YXeZq{YPgVy!sOUO&egO9Dgd?kkcchhIEo~=TW z50MOt$zf7JP#5Myot|BlSQ<1LivMh4YjtM)JO^!r@;p0NaR5r!klV35#XE>RIqyoN z0zmL<6KMhL{-6WKt}wkIZ($x99?4fm9%deS5E>II(-awe5`yqdWLH90LME9uiS{PK zchL!`DiVDFQEMu+5UN&bXe((OGRFzPSw24gRE9k3D(L?h@Yt5wMbVW9tb~R_kwHn> z_yUL5$e9?Kw+{%10cgNiJ;SO=BK%fTe9$el4Jrt-t>TEQsZ*KoH{l^BbdAGS^<}xf( z{ptYXF~ww&gyZFY?FJs#YKdCqJn*pSc<{cx_2_sFe9={tp5Za*dC?=kg9V^gz(xD^ z|1yT_`pklVgm8oS=*z0dkc_tD57)^Gg|9+QM;n7#E3ABEy+?!MMuBL2@D)0MNSW~R z0drkhelQ?kStv7<@r=tg#B>dTb$%IewdhBTzl!hXqz8|We9-lkCDp^jpO#o2VY<_i z1#Sd>;FwkI+#4ng3U~HJu@;wQ=bFZEP*2Kma#zC#@t)y=cgvy6Pmh!be*`JS4)lcw z))U>6l;zJYo*jr4(0z{ARSg}6QrZghWoF;#U1^UG91~wSbh$x2&#Y}>?$B>ZX?$P> zkXNm(CgS!Vi(+5-@9oTXDKFRuDiJUyu*fEe+%gEsuA1LzZcY-O@ z{OYdggb^zJ8uqB{vjz9w@%hVTv+uzM`>kiA%OLdoGW%`5%WHrt*%G*Z6zHy#{60|q z#Rp?v=s-qO>+fmUownka6uwm889%WuQduOD|93r>d87!0$EiPdS6PoS?A~*CWhSW6 zxM6pCm}CqaKH^W6zg}^!?a#Z&g#a_^3Mc(g=d<^_vz27Kt|a~P0oUmro%e^tuPMvF zL9ihE=gRbs-t(ocNHYaa!svzQxwKYh{HW;2ceR57(yQLuhHAUV@=@A%>X>VE8|vau zL(n%-y0;lFB=l?T({oQRDC{L>gIk=8bc0#^75_Xb_Z76k+}DA!i~$YoNbm;2VRVk_ zeeE2F^JK|xept`*;ES>IPg`FD5NPgnvdz;{HQ67P!Ow#rJo`%tP~SH)9X!#QR*+{k5Pei)W# z_8+*FWsZxb3dHoee1nvFgNe!+Qhj z+^x{BT&bTzsoltbW`2!z!OY(y|1l~4n;et!G*@y`Jeqt}LB%qZIEN6{<=~s($YPo& z>O9`a)~Xc?9`Sp0xGXqH|M3831rye)W&RKci9xZ<-viiKZX)FJ?`cqm#*n<kxTCx)6@-7#?Va^E@3m%Eopxy{sA>u-bk^DwZF@$T&bKX8BnJ8+1BIBvlkH5>!Ci zcS8(!=6bjv3^4db5xm;tUfJb=wrY6>47eq1oxAxOd}kmy02M?WX9diDG)1-RdlToq zAGJ{1!jCnqDh(L*Ja9x?S^p9d^_Hul32<3lOWHE|J7A`VL$&kKw+YLFUEcH;7|(F1 z7W^9d9d1{{`*!g&Mt>0If1n>L!w!}MKCmZn&Lds#VaCM&UB*nHL(Hc377rfpn0VCx zR=b+s|Hl#VyX5^%19nXI>EXn? z3qCRh`8@~Qx($ZY3(iBk5HYx1tFHgK*Y0QmNNGxy)n3e;t|f{$OMRnITwD4^G#1z% zX*8h4nNmO!Ve5>0ahE6>sl}N>KnS703^WH3gVXg5$qtI5=#jX7pahzys}t@f^tlyu z(W3#)&!hs>30tS235b;(WAu@iQk$()Je4wn6Fe9kV{nm|oim3KmwcP8V?34YSFpjr z&IuHJfeEM|A_j+Rb{Cv%30_quH-ZdYXg|TOUGOhZ`W!*)7)SWsfJiO3*kc(UVe14m z2$A`pdWwPuG&>Us_-`okQe?Arf`^%7>_4m@kI^PqKORROM7aEQm6VAHuU3gDFwn%G z?#9pTJ!1$qWi1ai7nh+o(8Tn(IkZzhukJM_Qs1+Y(ZCp8Oi$3qD=DDj{Zm&*sjTF| z-c)~IXl_dtOwXzvx-NyPZr~~0cWY?evA6R7`xEftpPMCsmI?FrrMmvp$iKb!hU4;I z^Go^$4}|f_HzxURI)QV9TIg%8hnOsh;^DWNEsX!p84j-NMDHU$Y_Pw6Hu_9}V3&}d zzjed3nDNZrlcdFFoP>Y!kB`{M2ISwBk10o(y;1u*wvu1M3|%Grk1C=jYq1jhsyKoR zSQ$nw7>xDP^#QFU!M!XD6?%XS!`VzKiQXth)`&33x0Dhr(Q8JVKB*%^bVDPP2gEy+ zsv1T9#069`dtc3zBK)1NeR(E3WWh}#F$6TMy^{7jM1B1tLX2m-0YSd=Cri4=P?AfO zp5^WVnQ04e+8TzuXgHd8o>{zgle$QzzBMou%qBW9FQiQwhPw2ZM-xBE!mcnHaXT%n zH8Mt8;&Ao3cUC2KR*8&WthET(M5Q&G2kUNRe~uV3uQ~l#82vHUioh0Z-ui zK8`@iYq8kmx!L4tq&!-h?;jLWYG??F)@#*HIk;1I@t4@5xot>6Mx^6krwvsGMwliU zF5It(N6y5Cfq7jWmQG1Kl}HM4qnFIi07G5=QycOK7#|33h{ZdKSQeYPnOrjqp_!u+k+YHe_y}_@IX2BP>FXT1P3B6@F_eZHUE9;FXO6kY^nuV{9XaRy!((dbRdU@gS(*nHen|Us zlBl>&9i5YGYfPgxdpgT{Ro0zqQZB~`zKqHU5Q}Y8a=a^YqLHPxe=2$&0D%v+7hpN{ zorpIEH8R;oERaF+Y?j9ltq)oQ4n`=r=}{rET9kn*_*T5)LDTH>qTt_wmsFxsqcZ+4 z{q|V05jR111BE<2muxN$4*t`8!V{D3iGiR=(8wZqs;%gEQ)${+cncby8*zby6haK|HP5iW)+)6?AB>+$kfSnXB){I%u)2ESy365{H39= zBi^<6bGyeI8TWK>U>B2X8kCCQyj`pl;Pq8@w*rNmbVK@Ot~~8r6>e#AJoN74 zcJnjHnu^1(=8plc`%x#?cz3PO)GHIEdyWr4qBSanNIY-ONoUxx#b)mkK(DtsX>X5h|ATNfKiP+;o^8Dg8SVPd(c z_|ePqz&KCmxS)=wLHum+&^p+B+PvJQQM(<48Wrz&ubGg=X;+aHoqb-l5|3(B3M9p} z4RK>RF>}7gUI5?gtOh54<9Z^@$|M;g0y1`)PB^j{VPgEK z&58B$K%E?$ahv*HUV=6~J~DPK$O1$&3lk8U59&ARzS@kFoNp&ND)QRJOeU5$f_M~< z=G{Qw=?1~PZ+JxhcRZpu&sa#{S;VJo4+z!WKYAaI2Ub%s>ADu^<_E3 zkJXLh5od-K)uDUe=dY0=;TCxn(1FF}Uz|T%>js=6?oIyg-gw%3|GYuvV9-uE&us5i z%g>e!de%VqUYH)YVfI1^b@}m;2D|G?^p1Qla@O1aH9((jW#L=gN(!aaQY_}b4Au!G zu7=hh9KftEM0ZJ51$0e4D(|+U=1%Q%La&6PQTiyF>~Z1p(#>v#&B?xd%vII05&!1G z(L!ja$8-p#%4sLgPeape=N043AKT>3EZ^pDzhMyAcL6_glCg99g=%+a^p7Zpu8XHx z7_)~<>p$ZyOn0w52)8xhGQ6 zY`mYl_i@&EM8U~2?0@7d23ckDyo4JCrynJWHO z^xN+)A(FALiq>C=1SE?9(NDmp^(5?Z(rmZ4;T)FRM0&7yQ~r(aj+qi~Q>o%w)1l_> zw_!5TFk{v24Jn}K*m9oHde*MXg9V&Mrq4JxFEGUmJ%I%f;2bN`Z^Y_CO{KY5KXlA_+zV{o(vs9=+3Zp)p25TfRDb2(NIuZJ;y z%~&kPfMFfQK0sSNP`Fq8v^ISLD(fvbfdz%xc>KYp+sscpw~i+i?jOsctTnHouXxVP z_8KyY6ZbnUR>t29n+2XLo?-Xa18QUUQ8I z>8OBm)pm;VzQqT+eI;C-Fj^K!-*epCDvMa|w2<#d29%Z*0KOWxZ%vwx26xhmm*sF+ za_q7UxZwq@Z2FW%G%t_$+?2IoDn96%2&x}O+A!3Ug9l#wD81Djk5W?@m6Rhb9U(B} ze>}Z?!f{n`{d7sCLRt3vaue-!R{sV(-iTx4HXKipBrg*7rA zH*|{<22z-nPuz77-V(EOy0dT4%C2G&XJ?cAQ|!b)|2dFb^C1hU8oj;Vl8pSiL5T}( zXQ-rT3h7_5O^XceO;xBX&-KF#5^PpeQ%ibw9x`11O_7p;%!|(D-sjvRz7(zng>FCg z%)d@JDjOf-hU;W;)qOq; z0a810{c`taS~QAEON%|Q136rB!6;s2%Xa6!%%g%%J)f-p?;|lvd2<_G)p-Yrln44+ zp#;ozGL#pt<6dMsM4@yM=;JAI6KCvVTDdU}b(m=}C-@m?me7J_wtHTlekKsCYCp#D zI66;6=w#f^pRje)pfa8ScJBVd5b)9UL$%t&7Y%?lW&x|qP8u5HKt zWlJx^`Gqus?t9--W>>CRldIaWs%q{s5Ccipg@S`u3*{tm*CQAHD5l`AfEXq-`K5}K zx&UtH&j*4H+c?zgJ5vry0}>!ti%2Wja6ge)(&jMocV?L$r!6%J_-!WD6{?RB)ZgVN zHjEgbmAcxoQ$Jua*gf`ZgYu!Z%r!)%r)^(bA%hb!PjX2A=oqsY=&I3Tm}4xWNxZdR zSAOn;N zm<+$@V7KKT zZRK_q-L5YJmZJSi!*+IkiiR!RA{wj;yME|`FCGb*@x+RWZJrC;>XjA$LXdH!t4*iX z`M|MxKKmozTel+ z@4RBAc-uAb3P{dD#1Gz*f;noccJXlyvd zjg0rK$S&Md3P@rO787b4k`1(Qgp^4U)}ZAcZ2&oQ2AoeLBZ||w2TK!eDiYck0_AeUszv%9BV&rq z?aSXU3Iz(~hCUL?WmO;TvqMHK9^mufyT$jE?=UjLy|kjm|6N5(&3(o-l||K9#S^hBhDT+SwO@mxPzS>?>DB)Prj!N$=m1aThtRy3on8v?aM^`D%^6 zP(FRWK8`V0b~AdfY_C4plCgl4NB`0(?HgPyDR#BWb?3-&MK(=&heC^j;qZCfUHJUi zzm=n!WsWS+ru}kwfWzVlcGIiLh-a?%?`RnW{{2ruMeDCJhstUAIt$1>G-(T;W+|7i zv#;_g@y!HmaF?a48ND>V`$)7qrGl(Fy+f_DkWyG#xum?ZqWDV#Z_81rruCNciSY>{ z9Tye(?(tpW!}HtP__iN~=9s#M=5+tXF9Y~ds*9_wm%16?L)Z*h&;)of%DTe6WPuFxMD~TvXsUirzo%wz64W z-CG%9*y7S;0cZTlsi^q%jt`GS$`eMOIHdBu33#%1ZDpzP`pVL-!bioAcFjJc0|VH@ z2m!>3S)hajFU{%E4-}7Re;3>=ULm-lO#v(|ZL^tL+|UNEgefn?mx1^eu2WO-3r7KQ zU10fwWmlONx!TJdgjAQ8FJGulv+~#8l?r+>LI3|ZMSt^=LiuBVFbVkA1qmZaCH*1*b_BEeOa9u^X}DoVu^Qh(2f$@r@cKN zOxC+YG$`CU@}WmWWI+gDzle4CD;qn2}0$ga6$V~6r9`Z zj2sL1Z7>*pCe)w==2%En-E6vt|7_PiowKQPB^#tdWMP}1Vc(sBR4aY`@jz|eNJe{e zR`mK?tZdP00O|D@OZlBnk@p*?!cUFLApTkS&PekdTrw>5;V{Tqo^SX-c3c5X8BT9!2Oxy4Esl1$Ra1f zP+!Zbk<=&h70Q%X7_mt9Q&d-|c=gTa(kV#m0S$BV2|$4;<2-~SW$mCn_d)v2r>FK; z(=*qhkdQ!Oaxn37{z)WiE?AWN3b72+kDL2v^D9BR@J&du5B>ph2#$*KAT%k4I08@g zE&z(6d@n>rX092DPgCJcmZI9YD5=_*NP9=M=2+G0sAXH_vv+CJVye}mvUSvvLc`qae^sgze4>p{P|wYt%t)mCoqH0Dhm|`dgd6kYyFJxrp_7twgwX>zPil8=MsxfJ13?FHp>NksBhZ1$IYp8tmT|ZQvp=$C zNfC((_P_e{Jd)mi!?$HJa1I`{48YAHH$k-&GbS;$0931>t1mq-0d#7xmiE#dg7bQy z1C2r$2LUPdWk&r3f5hco5#6q{3k>U_}Sw6MWGawmI^eqbuqdV*KLEYpBYRvleD zUrM?}uuPAP&d#06T3RO43*U`DG3OLT?+$SH0 zVjOyI)pyM~Wwcn5&P4k=xEfjN3jR96SDlkAf8ZFhBCN>=_EiDbow}owTJQnmHuYZz zAWZ`(i46SyY>j85-vv_wESXwy0wV#(ymH0sJ|o_gS_=&cEhA_<-FC{YM^lo;^DN-b6rjaO1n5XfqbB~-FzYvY7dMw*TUa5@Ym?)Ib&8oWO_A%20->zX z)6(zxgouJ*>V!z9p~$RFKYwg2Hh<)3n(~P8j$$~t?7}5WVar`rr+a^VV0ODO0f78i zK?jWfP#^{V`TWP0zn*?@|o5`J*Ykznz?%4Hmy=nAicPnX4fS2dcfl?^c*G50gdh+m z2M2$5GiMra*Uw3+Jp6zWhs~lM|7y0>Rq07!Nd^^u_rGPdsbGD7vtbDl}(ah}Mka z!ob{_dIJyXGjA`d^hKu5-H~SDt149=@AD;Eg)>zzG zk9@s`gcgVNMsh?iJpQTs-dK-(p5C<_EKnRG zPWyvjW_<|WqTvD%3>ZgVk&bRdpS=q^ontV~cdlWKD2NYI6huSxm9^a6>X15DZQ`oCD^sZ{O*w zZ`rHeeojqIv{~f@NZ1qv1S^amUQ>>&PuId0GS9{Mss z&ovsGmFH@JatX0fF9UV=w@E-*SckzV}yT3G@Ltf%>o80?M}fnVy{vHh!BQ z&@IpG^oxK>XdyWG9bf?z_M`dlGBAfTxS*dqb^sn`c0fK?Ed{jO;Eu(-f`8oO67sG= zU#EHmeR~M6@v{VTXCX+UpTI)pcnJn3(J|C9#Ot+SqDd?G4Mi;VtE&M?R-F+sTv+za zx#{RV#sDRLO#qqBK?pFtS^BkIE#tL4;N>T6LC(%&3cr1n*vss&PNpdxDjSIeQJ~t( zNvKLtv@;!9i2#9tICFQUMmEk)CHmiMYhO+4e-AR@aM`;+%K!PV>`y;MK9n*^1-mb4 z2vY7kt1!UZ6}D*z<~SIwZhUEIODv_KoL)#hF}|dPe0*LSCY8oDmu)A|y@c@61qBK+a(|8_FG`Xd1DzhE6>l zemgs263i1e+sc0L%x>#jD~>L((j|5z3mmGfJ=%P$=2m}m`01onewNh{WE8=M36Xpe zGmvKOJ+hSTX*gJcpRp?AH{F-q&0lM6GS^*C@n~`azs>D?Zioi-k@OB0_E&Vg*8QEO z{f@=BYZ6bYGtsWI7gsm(zhG)wS#GSvJk}Uiy`KCbw$bja>quN$9pFpW(3jU7&5d<9 z$V$bsD{{TL_Y=?hZhwo;+@11Zv%$ZBv)f9rlE>cTO-HY9a9)Ad!FYfK%_hwydK;HP zV=L3oAjjRyh%nJy`|pd7p@)u6ZJzQn4n%miFDTt|LWhiCsfN1J80zh{D4S<&EmaCm zIlRnVBD9TlYyu;3fIFTDqq1dkMnfC6>V~UC29Ct3qAg*zv{xf45_5<>!GXds!Y(lz zIZ8j7`h*nQh*U9ISh5Q|iQcFPp9UW@juF}wXN?3pz-r(#sbuFBS#8Tdjw6q{>fRZMb~Vz+jo>gr}8Xldjc-m5;=wZE348u(qv1`x@)yf=sQXv z|GnSVyZbr1e+5iyF)TVQ=aiWyoOp2e%$0VFsk`}v=v=;h(<15Sc8=ckonhD z;E#v#oOrY5_B%X^YeR>(SZup1a!wn7OSNSta-e$MAHV{R{YYvQL;e_} zbL#|-<2(+N#W9w{d`DYG)9qBMo1^0vv$EE_HaKGyWt~>TiP@yJA$7~omo$Jw6b6aX zWf`2QR(CR6YTkV@>nqi4#G%W6WaqGxa+PC1xVJwnxjYOA$>d~WeTw6@*xg2x%rsNy ztZKDZs4W%?9R$dCTz|cOWrlC`P5Z{M>o$faeJ&G>8%DU>_NKz!w7EgDW3aVfami@XQf)@yplexz>8FSJ{l30^dVGdvlhqje2+6+o}~O@dP?{gW+z#Gn8jxVxZf!;BMyyha`1L+$nG z{l>9omYLc6^iNEz)PjO;8oDKcw(z4~ts4L`v7kf}Ju)bWLW(sJr-S10xPVHTLJ={7 zqGWO;F`)>%UMvw3*_{k`n9U|LekRKir-^?UQt`C=sZys<{V}D#wu@-YH<8^z?sOTr zgz3ui?00qhd+vU?C%wkAmmqjwM34|lmxiSrOqU3@98#Bx)?BVPy|II$)?iyD*n?Ti z6qj~&Q#wrM(%mu&mz#K%zplr1=`Mc@Gv1stsB!uZn%_g@z< zyKyV2(}wT1sOxPibi1#Q&EoJE%s|nS9X1^kung2`aUdf-O$bhPG~7Uiu0uUX%NB2z z%Pwv>;_B~X#H}>H(Wj}Lif1*^l4oTS%elFhb*{AwZ|_R`tme)T-R9a+&HB=F&E~?j z&F01y-dakrCeEsQp==`YqxvB9=336NE7Ni^7lra#CS$P|*kaueBpG6Cb*}o{mmdCV z1*nBB%GyWs=sYc}jL-KsRZy@-SVT)ovNa*iuBv=lR-`0|tlZ8?pgA-4c?PWYa6mBEt9JV)?yjOMyATt+^myD z%xj4n^=2+VwVYYeX1?G%IaKzn`9@xZ$~ulp3A6DH2x?bXzr-CJdzEJtcDvI#qsjPZ8?`H~t_ng5q4lk?*QQ8@SNkA^n=+JKf$C1oeh#LLM zcYCT{%F=B2MB0%qW@}o-aV2|3$1#l%2@Vttl4Ou1!rF1XB$doL9iu{G1{ z=C^=mhcSA|J#_IA-nv3&`?u3!LD^c!|RL_P7{!I(-1k&$% zj(f)n$RN09KB_W3vc|YXyK<4POZ93GY)}4Qj|p?HiIzl?rp43o;JEUm9vxv?NQ&xN zYFGF5?Foj6!xI65>-l@(4|V7`Q50>%B30H+)1CmOV{)Z@$tEI;Z0BL6Rc+Tnu4+!F zGd8`rWjVzv@Ap{v?gZ6jaIkPVYh-YEupnU~l*@)btWGcm5@I6wW7leEDn&xLx5h#+ z5n@41K_-b}42+usEDsSPrf_Wdw@_!+UsX6w zW*JDuV)0b`2NiV_v@zzSO1bjM{kO6Gq=i|w%d7{ww)2FE+P3o+uG@~&o)A*Y9^(2yO}>EN;2UjeFQ~< z2|Sk{NNT)Gg$kqoI7Q4FGog%tX+mnf;S5+z4afC*QC1e58%`2pcDwz_D34EoftGdB zOeQV`Xo&hMwG4#XeXabQ+rZXm^2}h|$)hi?KN+>PYEw{(R zK~&Vg_B?J5kh%a>s{1@cvt9p4Z{rt=1FE%fL23j0Cso)2*O}U+m3|b zq%8AC^#2Ma{=yXXOqx&xD#k^Tq6i^AgI7U_plyc9O}xM$Oke;2f?xyy)6=!zIq8i5 zVTFUy3vT-j^gaB(FcHaQQl%OBh#3&npEDbv_B8Am)vX57SQwzW0G;<42t1HF(OAM1 z4e$u09uvF{QcGEM9c1>TS#e_R?3l86C49?m_o?X& z$8B)Z=|;~<@)8=2=UCUQ1`x-ckec>W1UBzW2a8_kiPhfcuHW{99$(s*k_*BG)9?{_ zzOXcti0MQm3pR7>^}@`_cm(y^Rr(3m;KJ}blmo$RCXlHs3C7A&iBCXfW#uX)3lkSa zmDK*_V$0ITslzdIXDOv=9Jqs&iz9nijVbQNt?#?ZF#eH=BPY=RxZ#3!!i>ZR7#JK$ zuh*>pAe^dcBGusri@m(sx$2n}e$yz|nA{WXy3{@H$npIwEb;;J_SKoARTP66_kvAt zPT{xxevxKCESiO@y(jn~Gd2`Fp`6fvkY6!@XTX+2$=DRoSVg|WZqE!KgnZ`QaWL7n zaX>Bu*PeU9yq}{nsSD~MinD!O0mw@zG(MkjU@VM~&L_-4AX?4-@Z$HR=-fEG=ukra zb^XU?+hnWT!gZl*(E~eGGquWYI+c33TCws~Tix1WFfKPVMDd(#bQlQrDCm3vTPijA&qNh{v&#+sm94p;!!N00mTyg@1x$6%A~gAq?MC) z;j*Ky_v2TDF*TkoBC~mV`?*f(xrvdI$R|`h8iUDT_J2yU80+Q9>r9q`8hRa8%JF=` ze%^-u*E6|B^fDl(^jk}%9v0>&{8_jl@nd8l%r=p=tTp|1~fu2^2QfMyVM&RU?Yt_0F7S%FMNhl1d&qt+Ryj!OA< zZ%j+r=AEH>T;E2rEjiL~yoF>p77yf?*%5yw$orxPW$I5Wa^fx+j2wOa(X|;>5VV2o~Z4Na=_06knqZia!@Ekm(0s0?-daefWYM5=#NS0l|}Y7G^05(}~STQWC_8(|*n0IWyTet;8UQx|QNo*o>H}m#Ta4 z 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 literal 0 HcmV?d00001 diff --git a/static/css/TTHoves/TTHoves-Light.woff b/static/css/TTHoves/TTHoves-Light.woff new file mode 100644 index 0000000000000000000000000000000000000000..be30da280dddc5af8c1b70f8b0495aafc3587c38 GIT binary patch literal 70596 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

`, - ``, + `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Contract type{{$addr.ContractInfo.Type}}
Created in Block{{$addr.ContractInfo.CreatedInBlock}}
Destructed in Block{{$addr.ContractInfo.DestructedInBlock}}
Balance {{formatAmount $addr.BalanceSat}} {{$cs}}H*y-Pq@$kBhI#?_EFtpGBT&o>iVVo{z2w;Bg?& z-)uzx3JEy)>ApChIbTNaI)8!J?-wGMdV_jLhj&nb@Bri+

Bd&v?#lYRAlNz{i~U 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⪼vB_d-Xx!0u{x#Ge5;#HguWTg~3UWS*w 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 literal 0 HcmV?d00001 diff --git a/static/css/TTHoves/TTHoves-Medium.woff b/static/css/TTHoves/TTHoves-Medium.woff new file mode 100644 index 0000000000000000000000000000000000000000..3277fb8ac84c607e67e417858378054d81c1b652 GIT binary patch literal 70020 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

+-W5q#^uG@XZw`Mf9^~A0^)7N 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 YP0`iii zP!MMZ0s#F-4URwv|4qPMy8rQT|10}H^8YUhh>NSr;bI5z;{;wC;~qGI8v;dmio&GA0s+{8NkOVmK?%VUT_LUjIzTc>u#3mHeIIS>gtW5lL;)dfwab@1ofN2e zSMWltm$=z?$4Vn%<{bl^>abzkV3&`p=kDS!zyC{lro<+hy60qeFaeZBJ$I+opEne$ z@*Z6fYNk~Bs6K%D!K>H^UCC1-a~SboRxH5wV$bhe2M94>=zKgGcMHH09&8@mm>Jn#S4~biN zDaj%Q4`lcU+QO;@GZhB6H>TRRunjAO@mUT(CIKiKlnS)I_yk#p&^=CKE#ag{UT{Ze z6})gEND^Ttt=;w5iAqqXi`U7=`McLXfm7{I(u-Lc=z+SL1U)((!6vhC!fI+&nrZ0! zVA9R;eit^DyIw?PsX#)yojTBt9KtoU;~>=a8bTW5m$NbsL6<9Z*$n0;dlIvS!=s8E z$)19dzv6%(z@})mrf6#cA&58}=@gR}ZbPghd0fS!NJ@6B@NuGqWXQemLG%|?!ae9j zcLHcqsOckZLa1pVt%7F|DX$VfSn7HZG=GaUjbWQv zmB(T3W_2k3k3PS}-1eu2YoK}oY2lJ3)2K9AjbX~!Cw$?KX28R1R>^FFz_cf4v^Er? zU_hxZ!T^EoKfHA_6iA8MfavvSs!RWl*{6vd^9S)R7bM5+Ul%vmLD$L#hLYeqgYU~1 z2z}7lbg@p>Kyh3ptCqd?*HTM?Ki6Ceqy7kC)^I`VvTf9y+74%teaK?~re znP-3QB$R^oT|v6kzPqfBdGLP`I<;oE_1KcXqe4`YvDRTetDZGz=&bz2NDz(B1Z`&z zC!*8oZQ;MB62ZVB{?wE@WMi1)Dk3EkVjH6;_ud$^P2ap3RFN~wY4JXPdob{P zv$i~dnvBR(D8SW+fQ1ACg;3!&fGT)dc9(Ioc)^SS`t8@f=+__v+g0=8$7B-KTp-!l zSp~}D-x$k_O>v~!zzX6e3b_55iI)2VfQ0R$TASqh=RkoyLt_Bln1)131@o642=iTf zWb8{syn{L5>-Sv$??>7gY58Ndp^LXWibA1=H#BoV+4|7#!0=dXD~J*>NXYpLDvzG; zrGxYPyT(ct7@3VQX+ArckH?*Scj|YYWsa$ybbWu>*BemCJ(MsgB_wifqpXex^eSc& zErOZX+{pMihNLxbNK!6^Z5#@LsN907caPAE?bINKGOfsN#uQ9vWm^yG)m0tkB~8@F z?cQ?BjsoH#RoGmm^HmYClPV>ivKsQEg}Z&DW)1v)uBfmop#qWUZ>E4hf@V<9muz^# zWde6PJx}-fb<xEG)*0pWo-ti?rYou_b4K zmu_J}=|!9C-<7{gt8ivf7J+T%$+>O-O3<(*ARy!bOV798%D;SG>ED`vNmP3azm3SF zd{^Dwa7m{KNy+I`$uCB1X(5lDZp;xBks3_9ZG_YVHm>?8X%$fW*Dj_j2vX&QlM^j_ zXr32UvP4FSqsP2ihl*H-hC${}1m~X;6l_`l*c=Uk#Kgh4futXv4#ar}f(lX{zs{=S z)|x$Y8$wYVHV1DDn*P3LJJH%bW$CYxv2aa5QxAYepcT1W)oKd5x63W44sLzo@FM)7 z0M--n6Z^(I-mVKA4D;l?-oFyf`!N3=StJ&(Md^%~SlLY2y|cvG`c0M)<`m{6ji8+- zj;19RifgI8^Zoov+_6Il?lf&n2wR?qBnd%;Ad%huG1!AE<#;)Lo9>rMM3&~hSEnRK z_@|!MJ8eK17^UPbj7DkI&i9w$pN%csCdQ(nGBr%}mraZnY4{RTutTMP+{W*Be>#Ar#`MrYf4N8s)#Xevg>WUw^FUHh;QSz6KyN@iLFM-}RDlv6~**U}YmB zV>x!Gk@2arIpHeOniP1=b%K>e<23Sd8*0lNs` z` zcyyya>B+&))@PRHH%yBNd-c;FhCiHM;Rd^|LM&<@;VHb2@TZ=Q z^G{=p^(A49^id^6Ef;p)hLfWtunCGeaxa_j<^7iN53HzhQr7k|B=afLKum+5MA?6= zz-WLWqmsepA26nZ(}8#S24|*~Fnp!f$wV~wJG9;ep5PZ#i+XM1CVbUBw zouFMJ;-_cc{Lu%-<4pOb3kiYTL>SaLb8#buf(kUiC_G{5zyLp;IZCQ$o=H|*+#xto zQ~)6;WY^?&p8bh$SO84-`$pSIO`*;V68CI#HT?{8qq)-xAS};=#_RgfK7+jaa?f$0 zPhcDb`cV3#*8t#PB>2=qwBsgJx82DlRBId3_>zC=u7*l}BYVa{Ju?wVrGui6MDuOH z2T(yd>D~}MYk9_r1*s5b$r)de8*-4&1kA^UB(Q4FJv9R{cDx^+hW&E@fmrm4sDC$% zLZ*oOh0O_BrB%`tKk25-wx>Hc`9 z$4S%MS)W%-GRF*>>a`n9wLB&?+nfQ?kl0M#P=#yD7Y7?(E36?$(J^%%9!bdKncxC| zkbk~{7Sz_f1VWX@hgafxAg0trh`Z3N?jPqBUg;k;WOh%Y6xMFF9C}Szly*1*?H(MQ zJJdQm*7ysIOQr~jGy6iTzM5o<%B7PjBpQjvrBkXEs^7vpzFj{v9el6b8}H2oLYY1f ziVYH8MZ6RYNaVVvMUt$+nVvA3_@U~(-Y8ynx}@94@=nLSs%PA1lQUksF%i8H7qvEp z_2XJLwXLh#TcMZg?H!-hYn2BKxb=5@`jDAc1N%(^l~h|0P7$ijeN*1}no;L}-`5kxpVoR!der zc8z!jM%2idQe~9RspTb-k!BChxNSzLIp$f3vYCXj7%yhcW=(wFY1L+59L?PbiWkcC{xW*#`{MI{xJV#4pJV@dgC|s@>IAAhN40+hF ze_WCNj7-|t4fNu97Bci1^MP;LgM-^mX~j?rBa@(;GnJq=2flghW1~He80pB?tSKW{ z^RD{Y1Gc-L_~QIoGrCK1XBthjrykC9=2T{B%~{gbS_-vpwkOH4*CiYE*P(=g!7UqO zMiCricM0*%J@UI_P!s$wmE9iU1b$X6`|zvKvxOCYy@KqAop0D~7-*YmQ0D zOU^BoHhiZoy}YZiu2>#kX*Vw8x2t{bhrk{(mpfYu%(sg_M2~ql{h_Yjg zM452oqqD?*$<9+{Wm#2LRwYK>n^xJ0_Fd-Y8Hrk%@3_?HuOwlU!F?qlZcnC{qfwk4Q1$d6l*j&w#BnKka=EW z-4+I8J(d^!G{EmK$5&eWDg6X|JHLgcRzOOpK4SCw#;H|b96*aGtXJ9!_5BnwGe7Iq zYrhq`i@X2oSY@Ip0%cT;5w?=&-;zjasRgy2#9hkGMg3IpkS zeS#SD1B~#4;mOiPf#}R5N2*1t zrK&okh>Pg27)0HOFBlk2Dsma<8swV%q(4FJf_Go0(>?f<0r-sfYS0xo%^eVghh7L` zpA&?zFaqP98^-WJ3~&@B6n{_(K&Fzwi6AV*_%=L5Nc)FDTVsfavz|h?k%pHmBXK!} zJFV71t#NdzE3&p6+X=;SV!oZD<49va8~(;9a4az*6=W(YZdxNdsjHRLIz)rxCbU82 zb0R74nwc}1x;>%iMd|QRXycj-TnUvNV3HZW=_|@fjHeW5DG@`migdPka_;oR-NqZ6 zL!QTx+uk8~ra~WVBaS(QbTW2j9wE+o+IA$|A+2XdAGBgkr**|%a#M1X61x@F^l#nM zuH0^ly=Hdg)TZ0G+svn+ca7iDfUT%@n7xv2%iT^Q49V-PwIPGU~C zBkzafV2}mLgcylTJ7!(s@RS*b6lzkrL^|0?o3S}#6uDBiPO)kkySA_~vNE%?G_^JM zhKhJ3a?UmM_9>u5!=SJB*@6%bo7r~6atsO!HMvZ2*3>;7og4FXMyKKJVpUhciTG6? z)${&vSRsHDDyc*=86Yf?HX*WTrCGIXCH_guT?QkOFBD_wYt5{oV$OtsPHR$|lMXZ6 zc4Nm9EY{4Aa>)4)g%I_Ci&zxZxP%x45j%{Q>j9Ng*_nH4&#*Xa*>a)eM)s*-J|G>) z156J-8Lvg4HxPOLuV?1#iKCT7K2Q?JyS6J9BPx+{iY1frsFs(D+r{EmTiTLnL{d;F zon9F(Z)Y*)K8_F%+nIrI?_NQ$9Q_n-v3twLpK`(|372bwX;~|+bZ`rFf70MpdY1A`nnOEEfQ%gJdVf)UTIFh6+d!bAQ=)pz;9d zGHycrSc?iDBm)izJSOwSoJOzV7H<5;kl+vTezq_;=>MwV2JP4ix(yolZ}z+wwat}j zy;uk>-kW2v7>W^V{)5KvL#OL=UOl0HC0X!Fvz2~`W{fLZ%1KaDY^wBP-6`^+D zaF78^qgtXFscbf-R-szDNfFEcStQkrz9aeZgu`mP%*h-jrBY$Oz&O)BTDt9dk7PKL z;?>K^I;oUF5OtX zg<=j(Bp9KX<8fohi|Lbzh%>1(yb(8&zv1IJ6Kyo+L+$8qGpXHg?C z|MvUdTKg~63aVUrq2*||@2JY|j}G=KkIz=&CNB@P#IV&y<;mIZaiV+CQT8NPRH#-g z7Wm-E^P0GC9;h{{H^sqSlS>G7IOq3(DRxC7`maR(DVu)u;tA`xQ>`Pbl%znNp^@U= z{MR;Y=W)e#DCE3#@(q7-Xi5xUW~UK;*P7&RC*5(9Wg78#!)NAj5f!)%*2B-WVB2o;#A`vF!o7F{!mAaS&4`b(7$P z2y14pAt!(#5pjH|JwVSEjB?_ffeuLB6(a{dr}>mwrK|Z&7939;z01kmR%@(ydcc#a zTlFt8HdY>+NU+N({2J?PODKIWxwnO7c&p9u3z6gYAXegz6$Gu?WwE)1gNHXG^LAXA zkZx~9m0IrRq>CRK)+ygrXUwyHQw!T2BhSP+cXFVZ+H;Y2M-_&IP*+h7xT#5Jk@AL_ zb)zXt^0CQ@Ex5Rpr77tjcJ<7<@p><*NqmQ!d;Wt#4k*G{NUaCd+_FhS+>zxXa=F4e zR@z=wQaX?f#{Q}obe?7iV|YWc=b7L{onO#CI&yf89bzNpcbWJjIFsu2GMPKh#4|j= zkv0gY!W~DzrwoEYe1=WQ-b_qZt8`bzquk?Ven?L5&&lcs)Y`z?U9gydSOhd^5-4Pd zeK9pEX7OFzYdzZ5^k;c>x$}zK-L5!%q9%Zo+ck?FWM!58MZ^%~C?B2(0zMEyz>M4R zNvmt$Kz_SpoIwIc4D{hG5u z#Nq3i$JGu=qw;>}(_}|{riGup*guDyH0G2LqA>L-^KBoMpNMzre*Rao?XF#evMS%c zeRqVzQ3Vzfce^wayG(BuL{2Ep&#Lbttpk-G3vY@_ zfyJH2os$fyuK)4%D;^d~w?w7?-YhUj*KSwJJUjOv0&*A(7)%so+47i<` z&1K6Cjw^m;O6$!(-dr9*cX};>Kptn%4O~DTU1ePO&?RFw!+`x-S|^RjivY7nwxC}b zEjYi&Dmi=X%eS1c%)T@gD_1CgKF8N4+I9iX7KHr)8N)|fe1=Unn%|Is!#kg!C%mlo z`&jcB>}w~7N`!Ns{unSi?OaE5@O?Ss$}>i06)nr(vUbzskayYW-B+aDF8QGWu74Jh z?Q{X!?ieUzZFdL?$?HXLG&i>u2mJBwyhxuEek=ZemZV6#02aTI0y5_^iNzL;F2J>> zKTbc#&x*Lo(&?+58^c1kD0^k&`Iz1PB1YQ)0NCFO$D7UZt7^46)D2 zJKd%wzl$QE-$|R%Rgc2yriDCt2qSP9X>`Wa*P=wI} zN6;l7=%^nqOj`JPh`@8uGXy5tv{3R0k~oWDQv{<3_FQ2IY){v4c3T9Xgx?Ln5&(3; zP@w$qKc+EInqWNK<8o``W6}E^NN}KuIZk1i!%`VEY&|%we6X`6!3-1`ZJ>oy`2PUI z1l;c+r>5omc{lwE)HQ&OAPhqe9>}nTO{u#<@b2*|L{<)Nr zSF2vZd<$hk3xoF?DQCPfej~xE>z;bf%wkpXrbox_caWCZz$s2gAkgPFa;u(F$UeD% zgP_)^xD)U&HHad^pC(0w=tg`4<@(a%Zy;n)zU(p`$b5F@TvP}T`b(ou&O`^Ub~BpL?PX+$-71xcD98h@b?dAJn0ZZR*9(rz^3)%pPr3F zLVW`ID705;cYsZ?obE)yp78jt$P{&0YJ=}Xwx^vE?49@HDj-Do zWk`SM+2)}#vY*0+^5mHgM?Ffr6CA82f*dN-MP8j~aswzm^7VQcvf!Pk$;+)c;Z+b` zg+&380Z6#c{eIUk#mj0xf|)tRT{L{iK5SnRR0|||0Piw}NSb;wM$0_QJ(`^LaZ&`4 z(9;9M4ZXW1JvW?0gYWG~r3*_HoQE@F82DCpmUsXaY}#dIAlPs>aFhp7WqEFW&pw^z z%1=nEX{l+)IWP(zOBvDUs9$hX|JnzgyFU|t0G6B>w#dlDIn%op)7cjzTPGcy?a{{I z4`e7(UV6D{SLj~5DAy0TyvK!YejCeaO3VM3rG1h^{V0$u`Uyg@Um=2C$mk{Jg}N;o={s~)a|=m=x*_(%>=nh{B~q@n>e z0_l9|bv{fgG5f%Z`Zw&Wk0G3-BzE4EAb=#3P|Q(0MyBFkf45xkP{Q6~ud07b0iOnz z7{@2e7CPMyba9W2oooB^n6m#m-;u1GoY$E>P}g=3?)hhFsn~|0vF{Q6mQBv4?Hi(b zZ;<&N_|Cap$G&?wz8^?vTVzo9a#0@26WI(8xKfD=bg9T4vP8lJ+I0Ov^_1SgsweY9 zw$HNiMT*e)_DZm7*Di!#B=0ELE8Rm}oJw5oD=n!9^&@dg^*wWQIZNITd$xoN6cz

p(;OmO(tg9C(y4LPsT)}Qdi!xeg~9dG!efC(pKU)_ zbkg~lM!8f!c+B@gA*;o5t?}}DmR&BrGVWGQfxzYM;66Z>sJDc{0G8!=<#)JOUWAOr zd=Y0@a$GmNeWCGO;~SqmY~$pPk*&$fnd)54tthUx$z<{{_}s6dy_QX?EMMr`vgp6M^?^c97rivoj_6jD{(-4v z?{1vfm%0a)fAHq|87R?8W+lV4Bm2WXliL#qBk_zOTU#!tB*HB3nZi{?-}tBkLMLmw z>GhvOS*0E&a+{izs(u4L2l-#yVhlA_krr$9;jMtdG?Av&7zp_NE<*aTM)plN|K|R{rW2|ZO2a{R<+?7)A@n{i`Uj6Fj*eE;q;blnYLZgh z*Uq|kjIWVrSrw+TOE!Fh$NMg4_n&O_fFz=F|1%OK+}Od3tj2jwG*>e_b^h{EeZ|8u z>JPV;;dhRkJ^%;vHPdljS4+CI{}Ro>rK@j`0(eT~SlW6R0sH_4fqrhUy2{u)o#B>m zCwXD|t~)Wq`tGdP!P*OJ{PN1bST*4ipX)Oujvqmp62K#!wxJ;R?oc!mb^iFFz=#h6 zJdqE@FXxt|0UpjW1Uw&+hZV$9PVdRi@mxUVKg0Py*np3E?a%u77Re#cjH07uX?yIWqJjv(Pwe}Aj%Z9=*9IKYbu}}QYNXlvPz)tgB{2*YTc^?arXT&+vXI@E zxS!Y5ToFhnQ{)09IZJto59Wlz2N;1#R28}2`2ucFeZMtTfnxR@+2+Nfam%`QbnR=6 zex0a|{IpM;>Qr?do8DOVE$i;w^_#Y}KM;8CDY8~8XQ0+EE%-y z;nPPjA;gWM)X6j{)UE2}9GG(E(JSU2oJ5#HOgV&9>HU8_-#|k|2|FJmq$E#h2uUDl zR7D$cS8KM3RF_#>6i1~~iarNYDwXBufehmCsj+S(iWdI+IVcb&O`uZ8-s=6I1Wd-V z%|b<_R&%blre>@4-@4#g`ffMi076Dk=-~gq72?%@|8g+0GP5(aE`sp;y}ul-<@*yt z!UhiQLy|(r4j$fv5=Y7wE}g@YN6-EPls~)y7fRA5P8~y+O4lx4K7kic+BR-p!_4kW5-3^0qlep_OLTYnM5vdhqhKLfC66LZV(pz1Ld&&<`?DVRREn*4{>O*^cQr}` zDRZbbXY8C|BbP3|!9Ju&0V5}Hq_9Z?CvTu6u}TFicQBL+J?fm7cX>sZ5f}Z0p_3|ye z*9z;>9Ak0Jv`iMQSXE1^s=CFj#-X%+C5@jMz2_nZ$ci)`>dOqw>WTtWsYzZ=7PZF8qNk*2hih5Oe|4Cf zYwOC?iSQEjXqi=b+rP}YA?XZ!A1YoEf1S7$qHL{F*Hf2@i}K~6Ac7qm#*G~0yc$n# zd@c`-X7eS=zpr<9-7iuE=(rB;gF6GUvWHLpfvvMglGV!Az4_Nl+g%J6Q_So4ct76u z|KHc-2@WSmCCbdRYt9S8+wFfcgf%*T0_8RF3OeqOX?JwNlCcL%BV8*Cq*6HY`s_eF_ zXNciNPv+cNjOi8c<{gs%eN|aTT>6M)T1sp?jC5O0y#n<$q=@7=5Yf@$@&C8#=9SwJ zr5sl;$cW?4JchLDXY=kYg1GX>lMkxukEgqr@S{mrb^}_Ct66vdF8(4;spcL^f%nkD z!}k*y3^jF$^szLK5e<9mUEi~`wAw8Nb+^C&^%)s*Vk?w2zZ-Y%K;UU!4JzlSxt1{u zlTE0_1sqqM{Q(c8A5DF%8AI19nv@sFuxcH_5x8swwG`VdDQDfDDc9b}YO|P}1`Ar_ zqkIo6IdmghAX`c3le$4RpVk+kzuT*YOqC`nGr(I)a4x%8SHsh=_$@3|aIH45 zCdxmW9T&;%5k`6nY%PbsOupl+in|=XV|0-Mxsz0x@%XAmzj_?Gzt#Wqq5(mS*2+E6 z?|ReslJ^1f8Krxd5`VEAn{`KKf>rpFt~ur6{mz`^{HqzM=r7yPO<3k!_WYkVb&emR z7mBV}yw+TYe@o`q^fvtZ*yG>|hWO^%y%=g)vprKK+>%LSIXSu8s3pQ8V>r_A*ih46 z@OaEP?cxE}`MAvZ@fIY`3||_%F#tpsMMl>Rr-NyQ2E~8#VIrd66Tt{0Xc00{#usM+k$j!sS)>*K?u z#`2i<7E5Qk6Tz4`(W;VuToy1&p4DEPT96Uvm$9_9=6Z*l4Q&MnE44udQFMxIjo!eM zDRn{7w}BVGL9gX>lfF?OAj$$;OTM3Stb!CdEh3j$9<;T^-r2G87)-PHzdO8`VxbUJ zs0>b|iQ@DTp9Q2L6XR9F%^tr#%6DAmrg(dIE4{4Uq*+Nq%U05Z%$yAa) z?u~nO87EQIb)C0i+4h~iG}O}u2u6`1W-E;aHE{!T2ilhucabRVbS5B9odN4%356+z zZDcoDMtj!qHu8|nBnZG`Y~2Mp4O;`UKn7shK&WeE!=*dEP{Z(@3cDGD}svVYZ5hQ`kriMzAv#rCSNO5hBhC}z)qp#Ypc`M@d zO{?7$tzs`7>{)}YNU%{77tStl*q!@aI$=|~qI6M#I8HR%um|p6ZwW9)E!@mi+`wMo z)e=PY&`e20?6`QSiCarhS;qlXVM54ej3NM)Y;ZfUvO~QaZ5Rgbj znc@&nA!i>^;G@Nt4r&MbASWy>^eVQBl*B!?d2Mt;aa^7Sf(Oj^!Z`9YQsoLsIcuNw zue8nU07`_Ubf7)lWcg^BDs@UMF86HoWAKuDXS?jcvI%lkC9u{g$Sw_#t5c`rk^b}G zG~$N%e_l`2v;=J8E2r}eU=n9?vPCFz*y@i3+_m{oVPWU*;lr3pZfq;+>6IVY)}Uf4 zY4C2aebkNgWAFhl!8qx7F;M0v@@32#zEGr^y4S@*6Ct`p2eE#jWCVOvBYCUS1;iVB ztdwF?#MM?}!&SQaFzWP%enHMOclYNOstNKSFPj8-^EOT6z*}X!*Q#aQt(EiMH1Iy> zi$UD;%`Po#PwmrA`B33;VC3{ zP}=yU;H}NbY3ZC+?~_Ry%2pqU&H zC2!d~bJwZVID5;k({ZxSju-BcX?)Ip#S!fIg%vpB=;-f0cUnyo)`1Uor%hMqYAEwn zhx%qk>Wy)_@7WQlWWJ?xG%JJT&ExLaMN114{7~DXmysOG)@L6f6do@_R2^g z{~lJG%k{n4YO0Z(X|!BPUgIyq^V~FT;Y+=2p7GNHuOs;NX#XZ(`YV4cXb(#D@SC3x z1o(7ms-QWO2)!G-lM*aA*qh@n8&)?GdEwM&{q4c47%0)C2pS!!&#e*k{rIKEJX8k2-!?G>k}W zFaDgrukDioy;p+*g%1Sshh+i$ry#zBJbh{JepB95DM3*h%nBBNj^I4rs|a}OzRUR@ zt=j!Vp1JW-oXA;1H?Z(j0!)skI#}@I?vCC}GF($!+n&VFVWWbV2*Fi>*aUGcfwF?( z3~4$DXhCQO!S89-q1%eV7KkljwE?wZFTw=k@3Q|&vE5um!%T(WStW5H!|ralRGh4clt$+&Dv@knmTY>6~4Zd#ISBhe+7@!;?!vwYii z<$gS!3H1dmiP|0R?p7@^=2S(tWAzR>!Sgi*t+sa9wRvhi1~!x5rh@ zuZugfLmrZFYy`ts^7{+gT7+ZvM{@L=KO5Is$5@=bh)K>8niX|7!Ly!BI=NUekeDng zm0F{+NUp3+U9M*EY#n3P*~VzP>m%LX@hU&LO=TgxG{~|a-FwlVWMr~J75}=`%qe*n zM|TMsaEbPX1>~s^?Wl3lQjAUSM*wBGpTcUAVOe>gVfZFRp6$DI(%idwF|*NG=U!F0 zHG`>EdIt@TU=+eZOy;?Q9Jwpp!nz--K5?e~AGSv?tO!S|E-0i*8ws9*76hE|2COs# zRgVMk`%!#-o3oGjU6;sJ!>vd)xnyN3jnm*I*oqHtD~y#=BDcJeT$JMyM_d=exVw%$ zQTH8AF%g|ybno92-q~g6NY4FP9O=-)Bt;@`R5Z}*XpzJt`V%*|1Bz?5ZwB95tiGTpus4D;bX zt{YX}k8!EN#bN=7)E6rC7SpcA>i24eH-{G#EWj>J zF}R*P+B7z<0(Dne>D1e+#IWoG>RYf+!+dTm5`srXKBjFp*)|wvTr?Uv!9%>|`5y*b zmJ*Uh+%I`GUKvQ{nvAOFAzY4ZMQaIwXnhLCQ&=W>2+3}`Ja8~#D9^BNqZp?VJFK8~ z_Kds)Hsp&$M9e-1Hsdf%2(5Oe--RdQkc7hRkG>N+3HPKPS#iur)*8+sXpLcg%nJqp zLare(DeV+fvKc1I2J4b3XrgY{QX=dmJ@>*47Ksv906=DdM$FF8yi|j5PA}osFcUV| z9n931xq=-WDNwj4(`cNB1S!zNZ7~}}WoO&aAP-yHaa362rJx>~NqKo9%WwQ<06I>7?|wsh)1Op?Zxr4LhPn8a*fiu%DIXKC3>qqE?3P zcn(+UWN8QsX$|ENhYbk~X;Nr{uL)!u?e=u=Nu&`h;us@)iM$c4!w_%P0MN+)B3|gvA}^T;IRRs&rgoi_vX9J4(4-FwA<@gYA?BT9;pp> z5?<5TuUd)NK1nZ2v@5bq2)S3091Cd_G!2NbcLTVKrt+{&wmK!&md>siLh`Izt_8e7 zy+prtdmkBT>QEyON7a5xK7NZ%?O?UU%RT2qcAPQsOh$j^ZL;djTV1g-sQ~u4Q+o`r zBiE`OYY>Oefjx1Cwx){Js&oFj&lq*~AHSckLkTj+R0N?R%U#{;c%TE@i?E!hcIY4K zUR&p{{VnaneF-E}uRyx` zV2V36_Xo3!uXy{a8)d%{r}cFnnqw9X>G<7P$z6j|G>z1HGBQE(Qdr7@0BRmQH+JJ( zys%xN;2Mv6J)V8$nM9!@ag1$dtZO;s=LKZ3aj3U(5$^EG{+uePL3tE9c4`Pb!?G6c z6qo%_wtsU#KmJzZ7;d^bumom4XU?N1*J&)~mg9~SguQNM{k^VfpBl>s|Oq zCc@tNxUKTakOI`g)4T}Yew(JzF?}}_Il5HBxxS{t*&t!j`Eh%$OpL#ykr5IR&*4#5 z+_{heqLis&*>?Nc<~8`S!>L$D4eGqi1A$K5UpNhMBu+775CsNoViUrtA)Mec))6)( zutuI5te4?#0X1iy;5WqQcfDavp~);AXh9)IM)B>=F`%9IVLbS-n5mItryrO+Hnlg3 zVXvPpn@N_4mxMLUv_|ME{!y!W+skLwRPLxFx}OwKk4+=XeixDDy#MaWiQW)Ei7+}1 z7^zMAXXU@(>#O2EPf^?V(o58HIP=5)YeYA47B)=4(VZ{$z;o;G$P@YHR({(Lery=j zvhXuBVuU0;2DUX6F+`j_xD*h(((Y!ef3Iq^p}|Iz4_y=#2hOtjfOezcl|fT&w=97|RtEJU)j!sC1X9JJm}))VHX3oV>E%mz;p zpc>-VFjV`lm{={49}8pm$9bo$ZJ_=9b}}>CLYYKyZ=)_9=SzXbQ8_dCE5({HUxuv#jPv%JnSLbkx@9--MmyK+08A*N6_ z5+aM96e*eqJ-@Rvz`8b!ew#s`0IQ^`f0=fyq16d{W8SH_f9;Sy)oc67yts%vN%90rE&V=;Uxo940DYqwQ=N zh(2eGf(@i-y{Q_9?c@_m3YfZCKV9do;fo?Rlj^Ua+b2&GuLxUM?(U=tEy-f#j#lG*j$S6AOxz-Je-xbR*(j3)eT*CI+ z2H5XI=P)@x3Uc9X9_n0yTN;7_BAdEU@(sGePM9Lvlx-FNJ8ao5D0hgIb<{hjoz`Xy z?5bJ9Y0H5H?bJ@L9{#zS|o*psa7}ujTnmjQp#|( z6-#7J#hxVo!ccGL`JI)liP~4@`pH$%^Uouq8k8Iuvv7{@9oKA1^=aXWy=YDIm=s|j zNU-!9A*rqaur)%wh{zj?|51PmD!zD(-J@}@lJ`c{OM5N*9vj2H6aWPjssQ#7`~jfF z3ky{uEol@+6wg3RAw-Q8yq&jR46b!BqrBY`Ddx|b7i5wiCD-D-Y3eVC}>!EJdL7Lv2a*%>w_(UB;N!UpYTC|;zvZCp3T1BY z6DCY%F8^!#qzA;(GFJa^_BloCQLUKk;6`94d?eXp2EFkP5N(}Xu72D{78a@&9n`JB zPypmaz4wmTh7f^SaQGa7jz^Srh=jxe((t0Ig!F9#^tU;!#Jb0EOh;zW@RwujNXQyy z&bln*6OJ1rbFpJE6;%wfQWQiD1k15l8gwZ(X8MK_D>i*njb9 zdgoDpHV_7n^@HI_mr3kT{HtU6f)O_N0rsd*n|%RO=D}i>s0=g{uJqFHK7+B}Z)Tag z7Mgz(-YmS-lGE7zT|`<_qLHo``EgBu(-THg$NJB+P~IQ&fYB+L_$r7Vw51^hSrACa z-f>RC0qquQAXoIJPPaO)r$YGT%*$}23KCk~SL8?)wdsPY!z1x%SKr&ILc+aDm3ckB z84L!6n>+{Imgl9CpYOeYjz9DGhW}{Y7|OAx{j67s2IG5U))gHog4J0yWJE;1$=Et< zxACP{fHJ7Fq`-9xpj5lAHe%L(gk{*0p{Ukmm)aCEsk6+|s#vtlcrq%d-g?)L=rK6q zoHuD>hjP3);E(PrZw0gukR63wmd|humCo1sic&O+0zwZc2n9n1L`J+v`;WlEck__? z`UrV-H;-ZGUZjjzsT31mRMn_4-Ma|JV?;}F#Ck*zU7@!8Bb`+I7VPEKf?s`8xQm;(b*pvI)TT1vykW7nULl(8c4cs;X>*xzawve1t3Wt(gz|vs}4oS<*IRP&Zpw zO;AQyS-Lp?$=!`V&_vOuYfaj{RdT;?gE~`*xR4Rcr#KE5FJ6NxSuN`p@Xc)OZ(J4* zu>0)M2220=Vx_!?M46OWYa)mwOp?;in5**QVT&1rQmRm8V~YOYIU0#rDa)s|C(Em zz>Mh^LCof6QA96}Y`fb9gj$3y%2=|yNhmA z-!=qa-nNtj4hKWT;=k{*KK;?nHBM}hpGboFH^Ts;DmTj1a+;k-3{xguc}wIL22Xe5 z>3Y2&KAtoT)#0T}N5j@*%q=h1bU{;6t>;_Ja#~0PtSRK)R3G3vVV`)LP%hb~zvdQW zsH)W)LODOXNCXH86L$58pGrtT8nH@j`PKyeYR3sR>JRny_}1&+5Bbpi`$*R92fJ;k znGaKZ;L9$)GZ2S{t<@>n^)zUu_7o_BJd_1gF{lRxU2Govuo7j0Z?W}33nhNx)LyzJ zRp`%th{;pJE`%vE>ls3`{JIQ`IT5Z^_jDwrc^t#V3)&(15GN}y|k`xTq;{K;ORMK)t?FKf4LpV;BszLNaAF1g3+ ze$BPMCI~b0ry_KO1fh5oH*#}MkTlP4mY~M}f}Lc)bCUb4up?CWMVlplVmPr9qal7S zir?8uWH^yK4fTfiODeZ=(TDe{ zmK>cC2WiE;j$LH&q|+*B<79O(1zN+{)73mq{&_@`Yck2bRjQN^Z~>0C-+4lAo|hN| zHv5ZWQpgA;$ysdJ(qeyQ&V96_e2lrOI2v<_P$=5o1~W6Mr=guR3KsQRU{53$ahNP> ziI<&d{zx7aMKm|sY-+yAKOF~KqvWT|teV42na*-t*o??z7Uxwytu=ie7~?+hW)|;p zV15Y~YUXx#xK_ONMnSU3T}x2TcqTDk^#2>V=z+J-s{X zS3SS!2ey*mbrtNK_6`(?IS(xJ+1SuwcRhD&Q;+Q%+*bD4bki0MjNZOXhp)c|fJ|Ha z*ZgOpk_}Q40YeRrn4q~g548HAq(D&B`dg)JQw%1{FxQ-@_1?}^13(*MzGxc0E8uf7 zHS`>b>}ZsEJDlLHv5O%L`6u?#Yp>xTREIR_>!hge_!u1A=I; zGeqvSFTKCB>xCo(`ocl!Z*PPubG@x$C%?4PgDE>I-dzxJZ%C~chv7`ekp=VXa9gb9 zdBWuy5Yq@y$ZZ$=GqT}=1{F0O4XwFRT5}7)!0HE!4wd4d2?s6IMD%R{18Q#y1o1Xy zN#^t{6`;zPb{(2m7Pck8w!hQ1e=gff^nR}h8N!|MlW=j)-Wxyc(T9+U^6aHacol4% zIK2K(7tJwi5DohVZp;xi*${sDJK?aGVeaRfSS-M@4{)wt8}kbPpy*M5BjR+DA4O?- z%S(T|>WAIM&D}5oRWcbJ1zCG*85o-A)%UObzTw7Tc?>h6w=s*iOVgRYP3Voy zE>Gy;6#tw9by<|oig3kMC(X(I(&9HJjNfCaWSLL`2KF4NJd2SozAs8eVT2-23JLSw zO~O$30kYqcViA7MU4R~qRGiZlg^s%nFe(8Xz7iF4tCIO5ghChK+PbL*p-)o6LfzoD zzq^wTspqfh@+WsNE=J~=W(bG0t#G^9RQ~8gPhRv=V3+HKFS7ZlJXz<%(Xsa@$xgiM z>Zf5I)`S3BfY&pWM#or}SK(q28VyvtS278OGwTpSzb`7ZRjAFKLN6=j9@Qpn*Qr>U zq|gI33QGW0%Aa;L$|A@fSFU_M`jqw_GWtzI!U52SC$G%mK4$o_{h`@-H$4V1F!w9n zyukVcp*a4k#4yT&?xhzvX-a5I2$CMof_}peCsZ-S>5RINWrBhZvXh!xnt8xP=Hn?# z51?E|RQG_(M^vZ4GAo1#G2&)x_mP7~$62!M_v&GQ>y?1_=Rvj%(^xH_&Rd#i;i#gQ z2NXE6!0fuP8%7?G5l4L4m-~T}o_IoNTQDC=F(*91tV@bpCe<*XhX(sl%-P1aB{jMc zuO6*ydF92F$v(Qn&8r7v;lNueW#UKB8m&_Dg4F)dgA4Z_;x**b4y#*&|P^{b1=L$nC#ipu@$W=^^;Gz~7Lu(VVC zW%&)ss!6b7fsIBR_4=Xh*S|~Nf?l5K1q^w7t=GK>m$6^+#g}RQ806fmco4S#RqZzJHI=LA|&WOeQ{OEr3Y`u$zH43=#fzSgd)T$`c+Et|t(=st}-y8{r?r*a4 zb-Jhm_V(}*A?yh1+1XA8HMN=I)zocyUGBhrm*rk`ad({S=r7ZIvcC>G#-2&@k;Do=}1`y<%Jl$Jt_pz)(znwh>Cd&a2 zQm1U@4UuEUO1f5VC}0dU{EL~(z*MPh7K2iZ@n;@igMp0X(m6W}`1KQ@Ko+NrTKf1< z+hZ}S^vyExS3vOE-rue+?H!zIv-Xh|?!}#FEISDS-cwZ`H?hsXLXX{V*j%Sj1nv># zkfWk9LZaaSC|=21E5HPPDlWAOrb_toLq&IYMeFakoV#_~c=t^@y-9d?%>LoVZ1=mb zHf@+55o4SMU}d0j(sz9G4$JPpL#pZqCzz*t>7yoTIR-t^F!*Q7pN5J?a?rnx3dA>! ztEQM)F#D*ox0Fbi9-ffex;7J}6Z093&kG*I&cj9}Yi@F0Y37VH$u;#&>nke*M@-x8IkbnH=n@CHwE01`z zo(*vzZ8i8H+QvKBgq(+V3V!gDJde12Y8Hg(tnM6;=MNm?5ik51e_fAc8LQ&Rty6XG zHs>q9ztz6hy2ZHWWi_jXHUmJ#I`G#6BG#sy6Z9(kV2CsyAM83u91l8UljB3@X}F5$ zXVa8fiE8|`R;PE8#7|$vSz9xAoFqzE`B{Za?o25TcdNO!<~vw#rh%?D8_;&en>6DU z&p``Sd}J2#y=oynKjOz;MMF8Rn+e)QlYPD*Klp|lYDNz20$jABnL zVQGj}l5GjvdYQBQ?~y|>z7!3Zq|5!kxG{)*5-?+-qe$^no)r8lPQ9CQx%e7bG!x&M zbUMlF4F`k7PUxPSvL211YT|rHf$962*!RKaO=q?%Ew67{xt^poQZe9GDx$56)KdX+%Y39NzBkFkb8j$j`xV2QZ>c#{R!BGst2UbXTE8W&l5~_e3a_*rA9LsWGZd`AXTa!}682Aa2s)u-KtNH(A1e&6I z7+ZLbtFqy*hoESkC#?pplfsHhx-_ zKV&iAO&Kbe6=rNvAl(GY+t<9$QaG>sley;p{B}uaW|p$ic7Q4g5?Ke7opIOl*C`og zJLv@{T`*26o2Q8*?f~c@t-Y4>xpvp(97q*N0$0nGS8FgneF_jEZ4l&S)i$nY!X!0X zXQ~Azq&$NlR4uu2ams>C(KjzfymVFaZKu+pJUQGyyHLu>Ov zsx@3fI#?NX5$9xqCnR>`U-tx_m=p&coEA^jO3jDmo!vW_AtSGU8>o#z9cc_zK(gi% zCSgMs{wkPYaL{3Czj|zvrRFf4IRs}jAd2Y#iZ!4T1sovoIFTeTAx#}z+#>}KdF7Nm zJF=QL_DI$`e%TTze`bF~ALL#xz`AiP#PpI$t5c!`9H4ztJ<7iB!DP^SLki46Oa-JeyZ?frnvCFTc!sN~+12{VHEC)W0kG*#8 zgbz(o{NfAIeHo~BkGdKt4IEfTB^$EjrX<@r`(U9oF*#U=Wtu~Uy7@;=MU-Svmv zGq1;%E-p^_QM`Z4Uj109IPg*Bq?kj!MS)fN{_swm8%5CH>dpmgOKprbg{|R@>x&y? zxm!?4CG(?_h%dJPE(1EtZ!EdNIiS~aFwIu1QVaf!<4y!9FS98609r>ROY5ax%lzN3AYNE*hB`O3~Iet2J-}h4Kt>hBaS%#&`>a)0} z!v!(-Y9dD;mbo5Jad~jRrjJ-wfXn@xXa`uGAw?g|_r~YE+CzP6^TauW$cKfmW%4rvEE=#f)?L_g3`#qh)zP(kbX-Wj$&A1DYWHd%XJ++G zx7}^8YIoa0ylcSuKaRfZuI+IRXBl*0P~xP8b0;S}S~=3=Xmzi@1f zOTLb0!!>Exwu^ch3F|kG!QHUNuyNK1%RFsC4iCd16TiqDs67%B++C2oY^PzBQ{ie; zWj^NbHhZ_-*?B%M?sX#XrWJ($1jhV=Gd!eDMbjL?U#w<8{et%5M`Y}_- zP^=c=lws(4wlGgOppxg?govXfF#&S2L;4?im2nNbMi(n~dP03@Go*r-C^K&0&bOa# z>=&Gng#p;Y8$zTcMkCZQqEyV|pEA#+?)L znaRZjW>b3k7H1nPO3tn$bV|k-6Q+Z!<8L-HL`fbgoH}VI;eP@`;XvRL*NW-AEVyET zL?4>|YuDxJ)c7h$)iSuUC+88yRtb_fWog$O*%3DSl}#_k?=D$q%H(x26Nz}IrXF{{ z-6{+SyTcK=iX3UwVjw&ueACscR<7eR1|F>Q=nL9E*bKn>0XC#OwQooDgSBDQKj=*u z7_i5pEXZm_CuHihNXwZ|;btugFAUyv!eYXWJ{m@p|6t1nmx3x$xg%7Cr&&<%_9ud& zeLD!33V#{)0P`@?Ds^7W*54GGO>n@5uOKG%7(TVQnE+`eUG-yp3s@?gxqZ4lgXC!M zjBxt=^-CB=j8@bmTNyV~5`oFqkW^qQA(!WK^GTzt>g$m30j0A8X+BlsE=z(^q=LVg z{2$;!7&hPCfq^auTxb-Y&#i%a&^$GKcMpHr)mQm97G=Ze(p1v#Xa*<~>U8quR##WL z+JY`UNT`e-J5QApu+xub1rg_Lx-j5UkF)EsDdWot6 za|SsyMlZuB3t={#JsKYsq2~)B!mLdVw=FTl*rM}W3u5D7E0s#O)2O1HZ_k4^w)ryl ziK0xYY|crV*Jhlupuvr&D)ELP`SkVzhPPIgj<8jF)-)aIg>l6hLHsbNMh(fs@hWCq zjtf&eufFa6ve2uOkb+}nc+=jOR71Llsw%+mS@T75Iz(zo7SxEIh1F@WPurk}xl-N8 zZT}<@VWQ<#duZvMq?5wN``vY87 z=K*AaDJ00tQNf5|V*(_z%P4h+&W^u*g=Eo?yURV$($Q=CNG~qWdW)2fg((RIiF%Af z7vQFPH}_Q6jAp-RSg_zCn>K#+{Rrf%=|Vh+kr_DFtRM0P z5Z6?Xd~i9Q6z#^>d#!jXU}3;Obgd1G^+&?}99@1U*UFAw6Q}=JSng`u6^ed;^v&W%6sO;ms%=KF5i%@sE8rDA ztQIwZOyejwfp4QL6`=Yr!$Fn99pO(3x{z1B*H#|ftnut!Rka?pWZm@I=_N-$zIsh} zefQ_w+E7$gUeQz|O!&xe2lLg!k!vdv(OSHKNLGW@JhHrea;W<=Lo$(sE=LEH zfv>b=B4xCGD#Q*bcel8ho5;gWQQO4RcBc!hB4l#F;Wo2bH;rN*cuT73kM=#)i|UAB zAmFI!SI74+e%b@HQwP!#`y;2ZsA+o?cNPIR2|Kl4ItVMx{ovq*#SV^y5%*}cB1Cej zIKQYMEvdlb(@TXt6lG#rOlg@4DOB-uO5_!kw+G^iW0LGeGpuKbR~( zh?|3h}EnFO)BtFofKRZkBQ=l_27Dv5yuZ&dGX)ey1y{ETct zCNnWFJ3AhW&){by#B$S2qVyO7+0|5Ku$h%1!jGuK{ zV90RN##IqzItK(IvpBLgh|eIbP(~V8D&=zWB{|YYQ*>u99!H)aF~$0xy%og1UJ!*F z{rfBPcdv@ACmxYf{B+&>^p#1GR=FHK5kOPAT8=NF&&$DneBa%oqG`W+Fg8wNy#HWV zWJpn4!}VY<3W*pP-g9IedNcoxYMLs>w?)d+1K!x=f10=%C4i^|jFLC|_m^495*GxU zHQCfTG&Z-M`OSx}2YoM0_kH{kiqEu|{1z5_Rz8Ns>yC7fM$* zG|e z?o&|b@S-V)0;Dl4Rb#y(=vTO^qL~zS@$-705Jf}1@;lv)m=Sa&<_5a_oV5(vaq#26 zaWLn>jx(?LR+#)mbrKQYb?~c-T7^95%L=hR?-Ji;Gx902#)1>P6goq`#x<*-4$INUbMT^Z` zA$%#*KK3NV*R#n;U??Sw_^}Q+!~Y!MA$_4@xpatFPFJR;xno(2Yi_*It;A-DOqUV6 z0@U@-<TdC8s+C&S`P9#1%y_}aHH(wG`E(n`B z`~?ViZ27YZf0g=!n6LZw1s>1Ugl;EFolT&!g zNq|e|vG^H1xVxyS3Akf0!JHO3;#bg-;zvlla8>T_CwN`;MN&=-Z^vb#rcw8=*yxS| zab~tO4-k7`3`XNG$K$9pI9N(x#1N3Yn~8?gCj#*lVM;njl9vvXybklX$6+0NZra>f zbrP2}jIJBnt?!E4WJSFJ?w?J-G6sJ^^0J6t)Cn!~vd#Zy1XR z69M8B3I~Vh;0ngk6ria4nLSi?YW1G=&c^yn>+MI6U*s$7X9PM(QVIB>5rKa`5VBY# zZ|g1E$xu07i*WGFBqPerjL-bVzmOE1eg)rC0e(+sq;@wVL~X!W(X2gLR7J;QtCCBL ze4DSdO0|%Rw^|-E_StD-A+M2MfB9-4io%4E2{w?X3HwFwiwAtjPJ#SWC0n%~1HP>} zm4)TWMIm>ad~pBbWCqPIUg8n>BXJkkh=5z&jxyq447THTGiDA74n|Pmj7!k)`elI` zzb8cRUWz25SYU9Sv*{Ws<&C|RD6B*DJ{iBr|Li4ra?N1&0Xq z6pH&D{Qq?yVxXPU)lS^5{KLos!Hy0Ye|>c2PdXP2JXai%ApJfYGC}P7=^_D#gV6mg z{`aZmh6A0Q6o>adDGwPgxe_x1`%R37?gF{EcM!Y!)UQ$>h zezyaq3i4Q&sx7E4Pu7Gz5`@;XZeL|Z-Tu${H{m8NoE^#6GeTvTwM2nC0m6-7HrtLM zh^^StxNJ|!Vj^MPxSn8JL>BFob+e%h2fgwl)|Qmmmdd@D_EXbOX%|zuZHY;3*h%LJ zl*>hS@M6^@G6_|)SG^~0en&_-D{Q_XHu`3m>;@sqf?EtR<*NqBxzM=JswJ2*5ueLr z(4_{lfjl3%pZa+mONk49?+KDwE9x41EGUsI5%RMB-71t^qa#;R3Sn_n5dZhz1&lzngzzH{32_r|0DISSy@E zq_`!9u7R|=(KK;xY9d!?ZnB`N zlwzGheE;3;-!%~si?N9lM~Dd_WLesE^dwt z#R#*L+1#|)fHPDQm7M^B#v2AqmI2nrvd&?u1ftNW5X0QueQ;Bs%`2~Xt-K=7BNV#^ zUjvzU``nW+=n;>;z0oZ=-jB|aa!yasqDzy3CHw`#V+RL&LPoG<@jexLM1_9uH&21C zYObn@!3O<0ctMw8wU8AVzgGwq&^V{}9)6d}V?sl{aJQ?dBSC6a(FkArq%X21oemFK zQ%n^2N~0>T)cGzLD@K4d{k5WoZ~;~ZZGO^o4}~;$Me+&0gu=o^%C}q81*C1 zAO}Afq$gx}6mU1EhrX7rzofHFov~N3IBk}I_r>>g>J-h2KSwbaqwHoHq{ zt7S%um%7*=J0|hDXm64uFv*_U=25Q+jXX8pbC|^YhWMyGT(h&fhK>ej=Vj~gemFZE z3r|@u5#$&JJjQeN)ChZa!PHa1iql3V$g`%RoN*%9sqauGVRGEMgKoORqQ7{Ay^)H=H1(WY+ z@2E87m(KN`*xd5Jwt$+0p%a{y@Xi+sQRz)Vp74`4515|lz94${R1#fXgs6k$2Z_ZU z+*kvvHb@ekk?QvUrp#!rfiWaF^ho*oc^jTGY$PbPK~cTwT&QSO-E> z;xSw=GK^_+8Ly7%GD(?!{$mpU?DD4_>fDb`pdSvMYyp%_V&rOZ&w$!8<~0$vqo6pi z_i&|&uR{)R9rH^e^OPk8c|+wt?sJLlcz{StQ4amnFq^j=WgJv zb}+jYbS@TZ4{p?9jXw9Rd%3wM6nyCYw-sHwVnc^~(zuWc5nt^rlw`v0+y+fEoSA3v zm1kK;lwV-i#{}1Iv6#jp3PN%A}1G`d!^$>ImyAlDMS7QDjt#OJM{KM2AU9+(9vlQFHah&|dCc?CaY$ z;W;wy>S}Q6%ic)@KlvrO6QV0Uz?s60qMn4B!JlKp-;smB0y+?fUx4Vcau=(;ra7m< zp$g@nI~KPmGjD!Q60b>!twcvsi-~F4n*(l7ScJZJCQmt-eQq#I1pX(cAhF#wu;~}9;kbq3C{Ex8 z%Q|I)%sW41zzhJc38SzSmyV*NeYZDxfxkd63{JzXhk+r;(wEBLU_cPejZ7{k=q~uI zb}jD;FaDCC4oMnD&yU<8*P3WE#kOV z@UrbJ85>*Jz|~svAY6601oc!VF0U0`SQAMQ2H+9-XCaAMpK>-=ff&B3TE!WYN!6Vp z=|PQ)#ug`vEKgi0q)<*JI&PHkTi{xFP-5t4HNy$x%8nr_ATOcX@^BY_hOIY zI3X`m>_cwl=7!T>el#+UojLUx9CZMs(l4{*!ZATaz2H1&0PA1358MxHT&-=IiVQ*8 z>l?OE9R~i1eC{U~i!%NdRH5(8R2M27rlZjfVn>cfYvg7vVcxaqtHDdmjc$G4z~!A= zio{Df3zyOqTG@#ai_d&vjxchOEw~2{zO@i31r=E;sOz4Z98`MvbwS@L0waE-D?~9NfQ5!q}%ali@?C2wV@HR76@=Su&9CJDyJFFo@OF^n= z68j)@)*3}?JL>Zh#^$%uQFh1esO?XtA){0wx~TKp_^gg2@<~COyK+c%HW<_2dBcLl zZKsFFgrolnkzw5^jxG=jI;<`0GTl9f3=Z(%bnChi;0uj8uF<8FwaQwVH=PX-$ZLj~ zrKl9cu@{zgiA+(WZ_$c})0tnGnF0|AQql9Irq{e7$r9KYB|P~h``v@_@f=X?7e_6w zu-GXcCNuD zH(k@w=!UUzwp&?8gJ!cG{b9=+qL_tIhc@_R0p?!e!Y%R`r}sV1>#Wu*R;!MxSr5P9 zF)YaN%AbI z9L`Eg$j``uFernc78@_&LLzB)BB9-9ZEW7#q6&rn{^{W{68E0v$Nk}|gDn)o?Sfms zq6xN<#g>&mfuVWcqV5|coh}sOCn;%lZ!|j1N=YgfU6B*IcXAfcS2Yb!3_VuoFiN0r zA*Q+|l97ZpRi1#l@-!)ow5{=}VJ+=1uMirEhR{eKgY{Z1i>0BAr-gTEZFD%qu3xOE z%w&hO=Beg&^D987k=^d}iO&)?I%NcRd@)ztzWb_ri)RfpjLo$=V39tg!?`aZe@EG*2TI)ykPrm zUFph;m4qCT$!M>|d@_ZhX8(|l@^U+DcKS5(w4!TI!(!5G8rp00Z1X;z8|S7avAMcc z+S!%;`^U6arD_-9{x?wop5+0_#aUrr|MLMUw5KU*-7j5Llryay4c^Jx89WM)1c$Oh zgGYdgCC45Pfk*dTxg<%D%y0s_fm`KP#_BEZ`1a@3Z>TnDhJo0E=?(MPz5Dq6i)8MA zx0$Clp5jmWTn6hXuPM*N!ls$2`+`DL$xN6Tn;H-(Ol10p;{4juV$7)E``klJ`X!Eug^Smp{rlc7I~1^uz{a=)8}Q$LTN!B;929A zT8gE1d;_nOvT3H&W$IXdxQzep-O9ri2M5cLrqh&9JY0|IePir}GP7qKo5IG2Ryx`< z)N+KJwT!=I*M+%qU1cieFw`qglajfvpv)d1YySp*;_iujpwJCqiM#}>OUlbf z@ezXH`L5v`fVUHwVZqt}7k4IFPWi-PeS2)!-O)Z-nQmBRWW7pT)bIJfN7>Rek9LQI zbt$cpFUP%%v^KsBXd}30Fg>8`!+xst0ZycR5 zhleQjEIDs-8CiZuE?}{FtZrPlo~sH2v$=L==gI=NHOwMA*mW~jS+RORowBtPud~IL z7qq6xv%<5HCKA@d+CuKKNl?{3dz+Try|s2LM`@PYrDh8}mI+lB1u%@O25ppZANb?A z3c%v}dt@E&$5*m3Y6bT4cFYbF@B#*{EwlNGzPIS+4Ra~SjmJ~wzLpL(^kSD7^T&1J9+;<{>gklke+2ctn@z755I&7 z2pH0c>+&mpS;m)txjk^*2Vq-!JA9;aWwHvbt$C|{CL`&MxL9toD{gD~-uD{Wx_+j; z*)7m1SPmdDwN?Qgcuo(%$N^i+H(yOk`fokq7cg<(_?3c3XW(=Yk}pf$Hda_fJfKkj zb!*ORh4m5Fd2i=lPTJNdU?RsU+u8fO8i7Dk`=v_Bd5>stPstsR#bZr+j~^4+0=39_ z44yzeMOP;;V~ifHd8Oov@uqxU0#_8BBv=Z#@j~~v_^FqfM@eG?6o)9CwWT93FHyja zj^i#eMXGJUH()YVHrS8|{mCoftw1jhKpLa96OXkTz_b3cqPLz7fm`h`9E+R9<;#os zVjMuO!YF)0jy*I_qLa%YV+8$idO;I4iMPTfSySh=v5N}xT^hkp((V=_mJmv)^5eNO zM^2^Oe)Je0rE8ClDQ<`ZAmZpI*c9?^?4H}2NBTVrv5|9w9w!}t8iXS}#J6?HdSYYj zrFH*pFO-LduK~Osj^YpjNm1!2#I?coU=)UC&tXQ*9uoPUty8S{T`+vl#6bdGk=i55 zrCrJ}FbMUR1AQaDAuF`D;k3j-W1BpB3KwY-BZrkc0&B}6?Cuc0vbF-gTZv1x z+To6VKpK{YxZ*5-V9qrOvUv4EVSaMJw%NB{!%r-qz&8e-dh>5+-UEVh@XGS36PH(Z z7azd@Ew4+H)pn_lHQO&_1M%Sf?f>?%yX<@|H7cLOX-gd9i zj|aB-&PG=5eU;eO3s%Vdr2~?6b>bHeHZg+?X_u<@TWv@mgXd(0AjfVqp&Qp5gwTqy{_ z8kl0H^;*{?9%V3k$8`hC3|Kt6V4KhU=bz~rLqj5;PG9>sku= zRYTufe?{M=8TY*bf}>nIO7XY&ySewxUD#`jG~`F@k8*MGeLiSJ#lAMzitTCdW>H{Q z|Nm5Vax?T<49fLI^^WXq@)|z3)lyJbpt^r2I$PrB<6Sg4WG-~C-sz%#*oLV__-^K< z<9leSAT^|eKH=srsRmFW6s(^%btSG)X;cDPb>UEt!~f$ODMs7D#=V0F0nEfiQbi#o zmS<)BJjt!fUR^D5s<&3}+PxPjPjP}Coru@0xoJ)Z7bKgg_9y7N04LwJ0hdg|6P8MN z%yoQOj;M7Y!^AwJ{u*wHneMRBQDFV}4sOH|j(qI!ID+^7q`WU(&}`4A)DEz;EG6Lg zwvZJhHL_FtrFwsYum$jCRF#N&N&?qi__E;q{0H}%JmN1njMnDm52|%jv|Rt*ZLgH_ zGGjlgk9VRPeDQ?i$#gN;1xXo6Q-b`_vSqbz3y;i!zHzRtuVj7JhoQTxwwQ<)vkD~$ zKb0-x=G)PG({lVCx;{&y)aQ z^qbJ)?A(1n$V_dL=bnr{>AXL31QxN=mlhAWynO=exr(>3Dzqhih zdvI!-ewh(*PooLC%F8xp_q4kJMbQ}u_w+k&I_wrtA-eEUDE*J4^mNEO&7Voz0f&GVju|bg-103j=KgN{Q!$LuSqQA}eH* z%}c=rlc4aJ(Ok4^R{i>(%q_{|6-f#}l8I3uCOuOtX;Q=Ql_r`OA1&?!QJ$lri1o_EvoQV&+XIGq@e_V{r~1NH7zHfdQ$pOqiLh3JKbmn(}iHCTu7J$0c~7#)+Soat1@fXz#R?*Y1OMdAokN$F!woKRLMWCLY*N7Oj)A9SsSjWEVWQBCy=!W#n1&vkk}Zq zf<$>UCPiRGiZv(7Wd$~dq(>Hq8yJMPQBx8vWx*_IbV;K|OBfiqEDq#A-&&RHb4Ub9 z;aIq-CqCkv)LMTn;UsIvcOCe&Hl53`&`zy5ftdzR{Y?JWDWUor4vg6^A?&VVyIg`v z>bVtnu?-AMJHy4eo`pn-Jb1+htd+Gi^^JiTw9X7MFu^NkM2u=4H|%vqYU03~J)Z&7 zDg;F!1V>nLXkGmW;inhywbO_Hdc&7wWakwXVEWhL9o8^B#QmbdAWhJgU18S= zV;@lCl|Oydea$WZgq5l<9FI;uWd-YqEoQo+kc#& z`j$mG!{0&B$a}|bspT7L`IYvjgJO`ogiqdGF?{h!eNn$B+~Uao_{7uFsbO-|mu~D6 ze=orHsmcjoiE$Jfaq+dpmU=J$jHVKy`u7)1HR&)ni(B(^`|A~^`qQsz@r^O*S`ByP z=`cl_tfrIhR^)*%GS#EuwO{?>wXZvJk!oX3qU=ZsU+4GF(7NF1KdvNR4QZL|IElBk zl4tDWBwQNQ24n|kXDP}yx%T#w0?|NHdka_4U9-b9Kko@H@a(=I=3pc;;hSLjC4s$E z09tJAEy6jWMB;MXMIsk22z@t*wQ;!tW}!+hOZM8q1KT}(6tLUDrY#Bv35j3yfIBq1 zI?Kx2cNZt-2fh47a_B^D(kP1yJYANTTfg_x2fB5MpvYY3z5j1d5(Bu>LLt9U0nWm# z&Gy5KYl;x<;E!*UmLYlyT9`zl;FzoHwEz^ydc`7 z=jju9$GjW3DPp{1knfG(1FnGXRo%;h*5Qsjsdq{{V8E~(=TUG+$Y0vvIng@B(;)@% z>Ny6ONlTZOrqFQ7(|qVsi+GtzC_McOwVGhu>CAv3pkS#2FM{&q;qFKwcp;Q42ho~C zeqoU)xbpuk-WgjY_|tuw?j$iRMa&7hdPEq?(e5zq82V|X0hgsmdz>|nUYp7W?1|Gz z_k|&x77?b4+^KP)>s=;%bAW|Gu!I8@N%A;$PF@=habO8oP@u?V_px)s*aa~+lrIXY zrL#GaXK4XDr&jyyeZ3+Mn_e3dyfc7ycJVY)B7Lc-(-?9vcL;cEjB#V*e|0jaqCUGG z2lp-FAOQ7Gvg%3S3tB0X(M-n?+B`tUJb^xF9woz^VkvDxAfue@aoYSy#$b8}O%Y_} z(|we-awX(7>A;}X02z&MTERuPngnE2OWiqxgT2Iem2k|Q<=Hd!4qbQD9N#RbBDZwN zGtCs|9k1DBbu2YM?av)G%Xghx!*tAROVj=z$Hm*TI<~*9=Z#%Hb!@+X>Ee`LqY++i z6l8=vgM-;T-q^+mWBV)G6l$M9&4{dl4^V(fg?Q zCFLCN^TCwxOid{M56!a3Z%U2A*=K^36VAvu<;r{h;g^%vdf^d0-N<5g=rtgBa3T`A{@r3?L-D#lM{LZv=M^AO2J)H+^3*b_DL99iYhGjuM{9o^*Tz&>h^T@mSgq zNogOZ;Q1CkZZH*#Q{n`ddbOmdC~#0UyX!UhUv2+)Moz9MK1GtvVWP~8?-JvFOJ|F+ zVsT}MX}P0XK~sNu|MVD%d*AxyfpB$q1JJ}Sh!E$K<0lXlSxEI=iKfhk0dHm4ML1|M zTW$tBy$H_Ck>zJe+iN2BcA;_PSrSuL*4bID@7)Cs;@gA!EA!$6H^rt?8>E!r^8J0f zN={;L5!@-@?wJUJV;`#}=sdhp4qEX=j|B;re6)1>2#Im=;qIuALR;fCTVr8JBH+jj}M2tXT|9L$yfrD5gsTVoi8#lqjhV%Pna-qt*@kA4gD`u|p#oKYB zXD@vc*r+R1_0*eP;SxqB4iw@aEO&$@y9mwwA`o0eA|>ug;+jOtYfT-%*0cQD+C&~W zdT|ipd0A$k)7eVnnMNPmBJR|E%7Fj*Wsrz z23ya!{X1(&kVmwWhS9J*RgtVOC;gPo9G3C7UEw)lAr9Ec3e)e3Y&LAD{m4)Y%Zk-8 zq*26$-g@jLsOfBBDDa%{U?~a$CF+6_UB9zHFI}5EiVx97Fsw4hCZ9uoei^hb=At?~ zs^bPM?%z*3#Nl!dk-~rYCR3FEv*zc@>&d81pqrkF7(=}+e#cqWD{iqfA>CeSG_nhv zN3wAj`y#rrmoc@u=Y9xVw4a>Kp-t#6GYXJLk^(;&SUzv~L~JEjV{?;WdCH#yU_&cK zxZxFWbufa)qJ%G*l1s;k!q#cHknauGEQ}d5>CTybnUw6@jjOmu!2EuHanXbLOIzXG zsw1w9V`?q_%nP!H4&J_O9!etjCpBy<0G31JQh7n%B0dupK_C1pY=Bd7bCcr_iMu<( z;wR$>8FkQ6aYuuzD+nDcfnP>(h${x6;97(S`B$H_*~?mo*1-CAD*+S1%srlF*lex! z0}44LCSz9wrt2R0Nfxe(z=1nT)&+T)csQPz*87AYY2OH}|BW8xW3UnJ14% z=gh{g51NW6&dFO5@!q1H?MuA!UJp9$bl5LVU1N->!IGVsJG^7twr$(CZQHhO-mz`l zwrv|bZ(p+6lTN3Tud9>(epFYgyUv+!RWjqEF`Us<(5gLZ+^8MDC{>Kr3?8?=zSS&i znh#f7;qfVa9%z^E9ZiN`0gu?VK?Dbz5Gen9L4aShCd{-oI(x{mkI>z=%ClvLkn{4s z_jwCy9@p3Mcayr=J{O5_FEbCfNy0d4OY#;Ic28|8zW85<7VL^Rve?y85~8 zNeMA0Ua;YA8l;-lqS>(w@_4)-sU4v(MpR7aqvXfHV5MOzC}g46jTbx-W!xBeO4H}e zaT`K&vm2viyTLD%U$?V|b#p2a0=W-!0jw|j`9j++wYBx@Bh?*H`&Sf}MNNbpxE~Xw z4kpXX3|e;s0OopUSTxPx#m-6scUhgJncGDXP*YoLw4t4>z{70=SRJ)>0O!}04voYqwS6hO`9(d zN%MODaa~Ov@SR+5c9gHer)sNFo9%Z@IWfy?tm(X|_uxJh9i>@K!oKi~>xFhf`QQq^ zNe%3o%?|?XXmqw)-3y9rcY3_#yyWtf5t(}Hk}L=^SDwHC+!H_G;Xec$Y0$nu1B3@> zSRCAjIJuJkqZ+mxnK5>8uT@7<$84~VPl4&RxRXW}|BCJv?-doUkQklu=)mGA?7jjd zxQQ;~ANvBe#hskNa~vU37U7#ypL=tJ{FLY%Rwp;zT!Q|=j|0Fx3kJ;624Lq-7)g!+ zhg+*oB3bWwPR}>fai6F&IroFjymNNZmRV@E;#o3hJR2+L0tXelRSsl zCV5P&-D#M>4VU>mH2-N<0YUnlgM`2#Uio4QSaeGE3R9_ zY$ON52_!zgvsc8>9i8c0Eyw~McK$*o--REygtLiZl0B||Ed7i=7yTt0;N$w&-HOE6 zzj?{U-yOPnfH1;JMxER+h5MqYB`gpi6$01?f{1yn_)>CP_CfG4IQ%$qs`z=P9B|?q z4pV_58~mDX1s{9&>F5}g{Eb@XPRT5t7VlS4#VpcUrXd*k{XMq#n|JZdh_#X*S{6B% zhg~e2K-&AG?&25rGtiDsZ|5f<6)nxYdbEBsHl9z+%|51$Wl0akH_*Zmn^uHWX`o#0T1hNqNk>ntEi$lM`eo?Hq4JxJ za2?|!z-H3z&g32%Hb~%5^b)=g&U^jrAFE|89W}&yyCHgo~q{ zAvJOeQQcUpX@9o}F0d{qUr@R%?kW{dTvQ~RgS8tfCdCG>!6#zl8l!;+RnM`B$TO#F)iN%!oT80r8!jXs+6*2NI^gt8wbc4}AGwbL)%Hf+>OzfACn@({i z)7y;1i!yv{6ubUl&bqw9PcMIxE66M96jHRFjyRgCtUJ5!lQ?x)TEOadly2Y$gma8H z)FA%~eF`m_?V_gt`3r}NI#J?ufR7urzypph8cImoPe?)#No;phs$gh#MS+;gB}iNi)oFvCy6M^J^{fm zqo?5OTt35~;kh9#`dz^L{s0ELU9$Kr<%Pq-+Oy^=uC?_`=a}#5#bUsvZI8!qO3VCy zo|*rQ*HSrwVQe1Bxy4~1%R4ga{by7Idh=g-q9khR+g$}da2zTN74H(sNj zE_MMvMnXH#4ug{BMvIRCc` z0xEIC4H`qvqXGCXUE=Yds;(7s{(6Mgm}Np&GA*plb`Q@Q_t7my2FF*1TgNH3-u{yY zDjJx)8=MCP7cQo6iN!TN@da_NCeD#wos<5i*;Z44s)8M3us}+iUP)lWb=$msH=nz9 zm{)Y4yF0cZJ~A!+Su=<~QnSDO>U*bb3^BHzJ)^xtebk0<=teLEw|*UMJ8KO)Yh1f9 zA30WmY`0rBbgC$DbG9U;-!1vRr|M0vd(B>9h+bEFSBDo2zBKvs`qJ^dLr;^yBYZtk zH%1$a+i_%^PCF+n>8fabn|}wnkHNXc>!s_X5BOxs9``)~Fx2wFc~l+)dsgghwuiiw zsAGrUKg8Q$h>a1}d^&)J_FCD*P=WhAJYz+CQRw@{_InVcV3FZ**yAOUdjG24i33=u zX!mc;Wds17%)P=`<#{G=DZ89hN|pzYj%^RW?_vJq?ar6ui0@ezzkpgqqOls?FZW0C z66HYPy5EtS@SZQbFgt%+SzFfUTCLcwr41R1$QxG^qe96JPm69IsEh?&zl84J1fEw_MB2; zKkgNJWT?Jn7 z`5VD0lNY>B9bx<9yIaS6B(rRzm;jHySIj9pZ8VP_Acg$=565mH@{bZ*e(m{oBDT*V z^<@?WR#Jt~O}~q_#VL=E6u!DDQf;P&ZZ*cDeu##5c^sr*@5LFL~x`id5l0hcWcPU`*diK?~;|=V}9`;Bl4SGnE+KYikFz8aN_6U@~ z#@1C~xqaX>rroO;%n(WYGdg;yrpk$3PDDY@6Pn(#)={|ZJ*4S#PFhOF(Z7d7<`S&p zoOl=iq|Zw`P2LC%u+?hKI$foy7_eVxVCqP33_Fv(~5G1S8r-)G}6l-f_y>#-gc}KPOe;`)4 zS|S$>%Pr!p@YH%yW7R)1dnlN8$5@nmqs3J>g#X@U|N3Bp(?s6-k~lFv--JfI)dbgI zl`cqdV6JB=z2TLnNHuba@e)}!N3XkX?M^t$lkHNzrpOyc3(Lq5Ht4g6*;n-SJQW)) zimj{twixSm{Xi6zd7~2i=ziLZMHQi`eGA${DKXcn4PAW3d@u0!G;G`4tflNPxLm~N2*SnoNHU_Wbp7PRECo2bu;L#68{W{gO4T7DKP}2 znEJQvqP(3!Vm=YNPqsd_EGQ^Y|$~f~_K!&5W>-)=jmi1RXVYWH%hoj8JLGQ9SX*%hqh(2}3VF#Io9r@rbzRdpn2kT`bi;l^(so63(*}A|hwLTj*hdi*tGR?mFUtg_=(+fzS%aS;ldZ<1tV3yp znRNmd&2ia1?6dd!B)=sNAVjdAcMq7vTWBiA9E9sAd7xDAu9#9YZSa?X zX!aFv1XU^H-wQw=%z9j}PaZ;uHL**mtp1G&gjh|tH4-YDZe&CM`G7~sy|Ydt`ag&t zBVziHMZI%3C6e@!O%kPHB^e%6yhM2q+#$ zuGR_dFT!BS?egOOD{{{wrx1G7p80e4mg7W08^< z>8mmFD}o|uirLCOFJzM(wrjNM=INI=7ke8NBI9e{R#}S1B{>7Vd$&unM*k>nhCcc? zW-n5Kn_tX>2m7#Mn+km}hEKsUI{`zKJbsLpEU^}nDKIOqMk_sbTk?bOg&4ynWh|_= z$9~~^i^n6!TCl{E`L=_3G-cZ=FY^A zk=}w%(9>nCIuR|RBGs}~q$1lh%J7LkeE&#IKI@v4veR!rRIX~ChL{^xknS2)=AU4_ z(Jr3R(lOuL!HE~x2$*LPPdWC1T3MG+Ty>XqSX_v z$D(xc`K9-K!17oDd0_Y%QoEWqvOKaGj@6!eZrTuV#{jOB7BQO7nYeFX7TV7)=P9Oo zokK$08eY;NCWDh1gr=N@Ce{fYkw(uwkrRBzD-m<=rQ!Ku;m={=pFTD1dbCx$1n(%2 z?1=|ePm<3Zh^NXJmsQAF{QR8fF&oWedgQTViTxW!Bi7DAVIio(sB3D@vBkddBYTD_ z*;S=v7^ri8tZ;=S)|$>16tLZY8gPQZxU6$-riioh{syak?g}Or_Z55vtx&*v*?CI@ z3=R@Qu%O-*u0uX4{Gic?N*m4#`*37j(~yz-_5ND{{1!rxBZ4s6S&u=4{$mvkhdFid zWC4vzPzjbf9UvJ4_m>_Y%fYL>jLZjB8-vpkCR$X>KvV>tb99bQC`6BJczAZ4qdLIQ z>G?2BDfKBIG2+-+*88wMFygmDi3&d^th6H1PEt-lwaTU%%LdS@*rO-DtPTL1ah&B%Q?HmrGV^ym)q`sbDzpX)4vW6kT~8S1WTB zc+pX3Dv^zaZBc>n#^WE2dt9MwXK#gW!ZU+pcw(MUeDUN*7OJ@KRCv(}_JMmBkFQ17 ziyuYZ`S%#cX#?iY9?TdXxR<5w&ofX-nBWfFG$;GZK5mw9m#+TwlCm9-Hv*JkHp|Td zFJuoa^XWg88Y&gOme^ymVjQwMFOJL2cV900b4?1I?LIbfQqnQVoz~Of2-%NG;7~d% zu)Bx+0asw#>P>u2wGc@Itend1m9v~ry59FR+zw*0_pnr+NjQQYPepKSEV*MF3q9G! zgBv(}io+G1mmv6uQfp-dDJ6mgiTQ*>=Omr;aKDu!W#>^9LY?5YB@JewdWFde1k)jr z9E6qliq1I%VR=FCU`JxM#~pjDQO0T6tPwC=qA61ezlTje|J=n8ULNC+@70`{A4aEN zza7N4#~e*;+yqT5RKP~hLO5g+mtNXe*}$#E91|tz{#@?3f7~Fjve19*Iw-QE&AAON z^n?CG*En4cb<}~bFqc+?O;}UzrCMbnK;~-KY#_I6q~tfA)3(J^G5V}5pIXtt)vKOa zyMzbs;JQWd!dr0EfwAw3iI4^TX;IaaIEc&@JlOAN^cc{kKn{B>UhK))J5CKHUcbrM z7h>Bk^lm`-YIYu1m;{wr9Mh6J76WR#Nua$Rw1IG#!-p_LEfVHHhPvMeQ|o}sjbK_| z^Nb+FKxxBvds!id0*doaeK2!(R7T`H@uhrC&*DOu5^hY>>Ay9&o5?^Yf-0|$Q-tz| zo(yuCT!tPEzOa2BlZCK==sA0x6AVk`_nG3pF=J%ArW=li5NZW!VIm%Rh;|>rQ)<6E1t_mB-hQpcFhZON#jyX zwsnOAh6EcF2d-^(V>0XD$^?B9egqmOTLoj&m1q)F%L=PDc;JJS&DIP#Nn=J5?i)r= z4Y|F?chWxxw(g7$4F!GN9#U7lK7t;p?y4S!o4VX6SKE23SZ~KD-#;vS4}1t={Mp@A z)XWJGGyVLq{eQM&N11i6lSlK6B6!~x$4r1=FG=s*y%es$cHe;koc9q>IdOm86NK}x zd$Pyj48B8o3pXneVeEfSy3iafu7{ncSL>1rhT}9$mT}kCO{=Vk>0}8S8*q|;pF_w_WC@sRg`B^7Oh+|ioKzA40f%3t=n2y^+y)yfBq8ITQ?r+nSj zTdFu?2eEry$NQvw)Fw`NiqfX&;3quSeD+mH{%JQNcH|#!Z;{&}S5i3aR(%$Czx1wD z=jn02+AH0=SI`3!i2;{@ST|$_!u<=`MBWGiu>u7eSm;n+INXGmWSq*w6Yg3s&pPD* zO~yl1=1}>3hWJ`yW?i#EE<=@US?$yexwdv3lGMvtzv%NWyUI)?L(@3%<#8S$f0PJ=d zrQf%SwDZ)06Q(n?f4varY)~tQ4DdkXzcvo&O=5-)TD-Y>Iwsyax0C_>kK9DV=tYGi zLRekhVROHAS37?iXw^A@h;h{;{72!v+V@w&w=L#ooQ+qghYr|JjAbuz)gVd2X)U|k zOnj`x4aRJ8aUb8JjJPv6tl9M+{hKo-=S`wBKkR8{qeafBP}4Y!Bv9G4?$yl~ad{G9 z>avS5Uw_gUga5I&Pf?YF76a$B!{x{2{dt5g118E%#uPsPWMgahA8~Y3%*$f%l?D?G zyl2`4{b@(b+XWCXMS6^IDISw;$_eeoW+-q~8&Z3+f~@=`x&ubnZ<=Q7F1PS|+eoOA zN(Bv&{=4=F)V%XdQ9|3?mA#|cn{zAb>12F%n>jUDuzjb*L~mAwr)`dLF0XP51~K7q zpvGJtc%tW-X`nlEBj~2zjZ``7)$g{Cbsx%|H@8Ogim?sk-~JskGCom8wJ+@fV6AU$ zO}i0#fOydH)UT?N!95Q<1jtjEdTNq5^oXsAgrY19@Y93-c|&_fIp^GHdGi=#I4v2< z3wAEIncwuJWUD#S0S!#ARp|Z4s4b+ayOi3Gmlsejv|%myOa5$zhllHoY+CCT1}lw7 zVR;S4yI{K%aqf;Rccp8-Xkx1SICe#x!krD{G3-ic8Pmu~79J=@p8!vm3?vRV=XjCX z+AgWSAr_y$UW9lkgkQPs1V&;i6{19ku6J89YJMK+5tKSHnYz0MpCM^-g}`9NdnUiwx)#OK64S$ZQC;3rWFI!F-#g4fP^SunS@(AI=7|j?p z)5NDCx-m4ExHiJ=nPuSGJc~#h)fe1QbBR9^FZ8Y<{es!;NZOm-I(zspWJflrP5sQu zPin>LRGJx`jo8S0*qt$dZ__`~Fc|33J-H`pc6h#ThN9_t*nR(MMqfzsTQM-!or=2e zQpwFu|I#(mwB*7?X)v!q{aYL6ddE`Zd6`q8Q@PSUmm80F-5eeV)AX3JED{5&dT9;N zxFVcKpXomF9!jKM=}VaQc&b?5h2HF8iE@ z_M57JB7-yaqgEiT=SOR2A(cux&`((eE|a5IaKQ;-;HyixDC>_C0UbIY+!xo2S0NH{y`GRJsA;?a${bCv{wZ6jNL^Ou1dbv+323z91E0MrHcqKtt)hyt-*t% zjN*@t8@Rb*O?-)JNV2oJsM}+M(%VgGDrGmC^IijKY9#vR$K{D(bPQpelA^5jyC`wY zxsIx|D3$JGsLTa>sZHUcuW?l4phVZ;qZnayTy>CaEMEZz4)G4;Bzv(i)TX05!#!$rw(1u~tsB9Lknm z7D=uz={#z^E@z0$%pHb{D+6tCvcW!DE1`^)-PWvgyLR^%aHJFYCig&8%(kGAk?c$7 zV;ek$MEA;Bbj6s)$UxC~m`7>4bCTOgp~1?|sH~0x-_?xh?8rc9D=CticTk*e;Vs)0CrEZ{ z5DQGIAx~~AP_*-kqe%A_QpgNSMKy{QFN0j6RKf`G{+jXCCF@r0@=B&Mc{v=Nsx@NO zuywNiRc8;C-SAQA8qkKVvgObW7mYm2HQ*PKXH<%;GBR%W_b% z@6wD=ipwdbgf}QnXV&F`gf8J6SylOg>F^aK;GG&CGYXWS@6)K%L`yct8Wj%#@Sxjd6OZLSOl1vEps11$OZ zMfgE#0UBjJkCp2YVtR53p8YJ;^pJUl?w{mwzqWj zCBp?sy)cMW9Df(&j;02Kpt}saV-GE(xy`S{!f-mkMQnu_Jui^h_hS+!%;Z0^JudYj zwE_I`1La#=otA}oyiZA$z@V}H5QWmCG3R-h; zFr~-}BR0Bd)K)$)KLIa`yM)&;gs#)OCxzL9FqoggSfi2(ur>;Ca=DtF2Pr~`)Zro< zz~jb4t|4%@T>5hiViZ#ww+RJKgcyNZ$OXn!(mcr!ym0k0>Q7o-mrTG5eG%Yrl<54{twFae9&;EF*b?j)Yi0pAET>e4Q=X-s^-7gQWmgy`Q zW`}@aRIF9Gey4!=^IClz38jb}o<~J}V9`$7B%)#yMc+ob`%HN%wa>4D$91aShY%DR zz4@@cCJICGAY_CF$q3QLRz@%$VW4S_C>H?P{?yc&J@;z0tySL0=^&)#VJy z1bw3IP?b(nuTjSe+@5M%y8nFRkvF8a%RUzsOaE;Vvy{HHWI1%b5>jKD{no7q6~1Hp(!31 z8glA~-LrnvOo3O|cco2_&(GV(jV~VBU(td(H-dBt)B02UH-S2(Y9-4jk%pyfC&ndG z$BljR6e*}~;;>aCyHT==<;*J^+n?Rr)7Qht%g_D$^Y`Osp{~^VLaNCZ(|8MU3 z)n~_)BW3z9w} z;w63&)4NOBVT6!SjIJ*@Y+jUM*~D`_5AD$n?b;^_lcKW#a%(fwbx75rnDqxmYb{yV zEubb#YDXYc3r~~%>N8!81E&84F;P8HD{(!Y2Q}DZ#Nite&k8KW(6SkD7kRaeF-Z^? za~o12GTATsQkEv)nk`OEU#*I?AZJ;}*zd~Pxzy7T6@c^VZ}zaoEYLrUWVn9`g})pJ zg|z^HyOx5dSArKvQE@~-N{0-8lD;J2#h1%eL-TG*avB^ZLKn(sac!wSW1NwA(R44i zHC_8}d?#@ExAwX8_EtwHl;PBwb%nQjpw*H{ugxQ8doo4z)fRkO!4v8W@g)lMlY;bR z6#13n%RVeB4fY?6fm$Ten%fTxnQnjf&6}Qzvs_N9?%j`6>U z4Erxie`VMHgJp=-@Jz@QGmS1m3Tp#KnEt^hvIC%^`4W)-H3Ieq(EIONIL2K2fOo-(io%PXyFgA2 zoI(NvI_kcLX_9A4u6`z;hNpzwnml(N7}X!dUA{QRT9daMv!WL5TB=ds+SYEJvjy8V zXjj_n7tNC|UsKZ$b_SLc$e&)DA`Xn)H5v>gLN??j%!{U<96q>RI+eliFw9v!#P`~+-^t@um@BySDcNO{$S0loy*)eRd#x|-kv^S ztP=Q!T31(mh@Zq+`yT8XT zv9GdSIGNL_$6NB#+|9;_Vt#j`7mVmZHLF*e0+P8stvpJfg>KL zXjlO14wZ4ZGO5@dwO*Ws)*1_Galcl9L;R*gAiyTIVC1pS$Ux71B1i;5{%fP@PW5b` zJ3U_I=b;fM6fRH$_oin25pF9CK00%-fn^clrhU7Nt%cD)J4oyJ24IO){UnD{W93ph z7;8y1)Xjyqn~qo#(t7423E@uY_EJhXRSs-1EPw(erV-Rb4s+q$s3}}8DR}A;1oz{r z-62*#a(3Zc(2+F3s^d${eaiBzNkauNU@6Re(aF9ksdE|n1PjEGAnwz3_-uR;hfBN8 z(hKZfr9)3X8f|yPQ&_3NtN1w)UmXv;KNN~W&E*&x`J`OMEE;0)(11n)j5jK(ZMcKm$$S1-VU)d3i8`*m53XGV(pxZz{ znl8`b*@_Y9XcqO+;)$AWt5J1T;#oT9j?yk{=&LrQ{?d^CE|}Z<6Bs8fNaK=XXR%~9 zp=~hqDXYS!IK5Fx-_F4Fg0-f+{=ry-%|nD$)EeFWQm=~5x6$+jAq+r905_5_LM~Msde##s z?tq=u;J$`RZVzxjkC2Sv>j42(@rPZ8aB8Mw~@)8JVOPkrK!e%fNM6+sDlhr2d%rNXOM-iLLh4cZL zA!>5x((RS(T&bOg|QM;p8NxgGHySMID!c=csALiSc zC_r>MmTdo-7ur=Q^2Ihqj6a~2IPp(C(1i|wr-2=^1ByeM8Z7a|L)*Lm4ZZ-x=cKGW z!2aJSlH^e%1X}m`I&$zmePHpC?>w3RxbQe4@dP4qo`^nSdp>$h9BDqffFi>Mxc-e^ zI;eqqs)_o`Rd;l=hhvEk$8{h!nV-wXz8=ONgU8LJPA_FSRG)Xt_6bZIY*pK`?|ulr z*s2D);3i#7HPSIm)u*+Zm^vl|c#*idaEQKN1u{I~}8n?IJ9!xdGmwqg)h zAf1&bwO`~Z-h=g#`BWRbBb>MHd4ABkvR+>Y?>)R*%8$ddpelGzZtGIl@tBiSBoB#C zi_mMTeJ_wkA+gr4(&6Fi5Q}OWEJVC`^k@iqgQUbzcvpWC|1?_R0t{tD+6yj!#idUt zl;CrN)FVoy;^TL$PLd{S%PC6tkYog4j7lvatqYzep>p&w6IAOcmh~2LTmNOpS9~E2+E0kC>B=$ZV_QERGvY5IAS1$~HT?fq58AM8s6K S&x81+eR!wQc4`6v0Q?W1W&D!> literal 0 HcmV?d00001 diff --git a/static/css/TTHoves/TTHoves-Regular.woff b/static/css/TTHoves/TTHoves-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..dd7b6fe5d0c13eab99ddf6f09306664a0a1b816c GIT binary patch literal 69088 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 XDf-$9t1NbYjpE_Pdtal39|`#%h(X}4 literal 0 HcmV?d00001 diff --git a/static/css/TTHoves/TTHoves-Regular.woff2 b/static/css/TTHoves/TTHoves-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a2cddb2908e5fbf6ebbc80417ee67ffa46f65c78 GIT binary patch literal 44244 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.btn { + --bs-btn-padding-x: 0.5rem; + --bs-btn-padding-y: 0.22rem; + --bs-btn-border-radius: 0.3rem; + --bs-btn-border-width: 0; + color: #545454; +} + +.bb-group>.btn-check:checked+.btn, +.bb-group .btn.active { + color: black; + font-weight: bold; + background-color: white; +} + +.paging { + display: flex; +} + +.paging .bb-group>.btn { + min-width: 2rem; + margin-left: 0.1rem; + margin-right: 0.1rem; +} + +.paging .bb-group>.btn:hover { + background-color: white; +} + +.paging a { + text-decoration: none; +} + +.btn-paging { + --bs-btn-color: #757575; + --bs-btn-border-color: #E2E2E2; + --bs-btn-hover-color: black; + --bs-btn-hover-bg: #F6F6F6; + --bs-btn-hover-border-color: #E2E2E2; + --bs-btn-focus-shadow-rgb: 108, 117, 125; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #E2E2E2; + --bs-btn-active-border-color: #E2E2E2; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-gradient: none; + --bs-btn-padding-y: 0.75rem; + --bs-btn-padding-x: 1.1rem; + --bs-btn-border-radius: 0.5rem; + --bs-btn-font-weight: bold; + background-color: #F6F6F6; +} + +span.btn-paging { + cursor: initial; +} + +span.btn-paging:hover { + color: #757575; +} + +.paging-group { + border: 1px solid #E2E2E2; + border-radius: 0.5rem; +} + +.paging-group>.bb-group { + border: 0.53rem solid #F6F6F6; +} + +#wrap { + min-height: 100%; + height: auto; + padding: 112px 0 75px 0; + margin: 0 auto -56px; +} + +#footer { + background-color: black; + color: #757575; + height: 56px; + overflow: hidden; +} + +.navbar-form { + width: 60%; +} + +.navbar-form button { + margin-left: -50px; + position: relative; +} + +.search-icon { + width: 16px; + height: 16px; + position: absolute; + top: 16px; + background-size: cover; + background-image: url("data:image/svg+xml, %3Csvg style='background: white%3B' width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7.24976 12.5C10.1493 12.5 12.4998 10.1495 12.4998 7.25C12.4998 4.35051 10.1493 2 7.24976 2C4.35026 2 1.99976 4.35051 1.99976 7.25C1.99976 10.1495 4.35026 12.5 7.24976 12.5Z' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3Cpath d='M10.962 10.9625L13.9996 14.0001' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3C/svg%3E"); +} + +.navbar-form ::placeholder { + color: #E2E2E2; +} + +.ellipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.data-table { + table-layout: fixed; + overflow-wrap: break-word; + margin-left: 8px; + margin-top: 2rem; + margin-bottom: 2rem; + width: calc(100% - 16px); +} + +.data-table thead { + padding-bottom: 20px; +} + +.table.data-table>:not(caption)>*>* { + padding: 0.8rem 0.8rem; + background-color: var(--bs-table-bg); + border-bottom-width: 1px; + box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg); +} + +.table.data-table>thead>*>* { + padding-bottom: 1.5rem; +} + +.table.data-table>*>*:last-child>* { + border-bottom: none; +} + +.data-table thead, +.data-table thead tr, +.data-table thead th { + color: #757575; + border: none; + font-weight: normal; +} + +.data-table tbody { + background: white; + border-radius: 8px; + box-shadow: 0 0 0 8px white; +} + +.data-table h3, +.data-table h6 { + margin-bottom: 0; +} + +.data-table h3 { + color: black; +} + +.info-table tbody { + display: inline-table; + width: 100%; +} + +.info-table td { + font-weight: bold; +} + +.info-table tr>td:first-child { + font-weight: normal; + color: #757575; +} + +.ns:before { + content: " "; +} + +.trezor-logo { + width: 128px; + height: 32px; + position: absolute; + top: 15px; + background-size: cover; + background-image: url("data:image/svg+xml, %3Csvg style='width: 128px%3B' version='1.1' xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 163.7 41.9' space='preserve'%3E%3Cpolygon points='101.1 12.8 118.2 12.8 118.2 17.3 108.9 29.9 118.2 29.9 118.2 35.2 101.1 35.2 101.1 30.7 110.4 18.1 101.1 18.1'%3E%3C/polygon%3E%3Cpath d='M158.8 26.9c2.1-0.8 4.3-2.9 4.3-6.6c0-4.5-3.1-7.4-7.7-7.4h-10.5v22.3h5.8v-7.5h2.2l4.1 7.5h6.7L158.8 26.9z M154.7 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C157.2 21.6 156.2 22.5 154.7 22.5z'%3E%3C/path%3E%3Cpath d='M130.8 12.5c-6.8 0-11.6 4.9-11.6 11.5s4.9 11.5 11.6 11.5s11.7-4.9 11.7-11.5S137.6 12.5 130.8 12.5z M130.8 30.3c-3.4 0-5.7-2.6-5.7-6.3c0-3.8 2.3-6.3 5.7-6.3c3.4 0 5.8 2.6 5.8 6.3C136.6 27.7 134.2 30.3 130.8 30.3z'%3E%3C/path%3E%3Cpolygon points='82.1 12.8 98.3 12.8 98.3 18 87.9 18 87.9 21.3 98 21.3 98 26.4 87.9 26.4 87.9 30 98.3 30 98.3 35.2 82.1 35.2'%3E%3C/polygon%3E%3Cpath d='M24.6 9.7C24.6 4.4 20 0 14.4 0S4.2 4.4 4.2 9.7v3.1H0v22.3h0l14.4 6.7l14.4-6.7h0V12.9h-4.2V9.7z M9.4 9.7c0-2.5 2.2-4.5 5-4.5s5 2 5 4.5v3.1H9.4V9.7z M23 31.5l-8.6 4l-8.6-4V18.1H23V31.5z'%3E%3C/path%3E%3Cpath d='M79.4 20.3c0-4.5-3.1-7.4-7.7-7.4H61.2v22.3H67v-7.5h2.2l4.1 7.5H80l-4.9-8.3C77.2 26.1 79.4 24 79.4 20.3z M71 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C73.5 21.6 72.5 22.5 71 22.5z'%3E%3C/path%3E%3Cpolygon points='40.5 12.8 58.6 12.8 58.6 18.1 52.4 18.1 52.4 35.2 46.6 35.2 46.6 18.1 40.5 18.1'%3E%3C/polygon%3E%3C/svg%3E"); +} + +.copy-icon { + width: 16px; + height: 16px; + background-size: cover; + background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.5 10.4996H13.5V2.49963H5.5V5.49963' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M10.4998 5.49976H2.49976V13.4998H10.4998V5.49976Z' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); +} + +@media (max-width: 768px) { + body { + font-size: 0.8rem; + } + + .btn { + --bs-btn-font-size: 0.8rem; + } +} + +@media (max-width: 991px) { + .trezor-logo { + top: 10px; + } + + .table.data-table>:not(caption)>*>* { + padding: 0.8rem 0.4rem; + } +} + +@media (min-width: 769px) { + body { + font-size: 0.9rem; + } + + .btn { + --bs-btn-font-size: 0.9rem; + } +} + +@media (min-width: 1200px) { + + .h1, + h1 { + font-size: 2.4rem; + } + + body { + font-size: 1rem; + } + + .btn { + --bs-btn-font-size: 1rem; + } +} \ No newline at end of file diff --git a/static/templates/address.html b/static/templates/address.html index c3bc0c9ec4..03fb4fa9c5 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -203,11 +203,11 @@

Transactions

{{- end -}}
- + {{template "paging" $data}}
{{- range $tx := $addr.Transactions}}{{$data := setTxToTemplateData $data $tx}}{{template "txdetail" $data}}{{end -}}
- +{{template "paging" $data }} {{end}}{{end}} \ No newline at end of file diff --git a/static/templates/base.html b/static/templates/base.html index 7127a63fb4..58d0bd7b43 100644 --- a/static/templates/base.html +++ b/static/templates/base.html @@ -4,90 +4,92 @@ - - + + + Trezor {{.CoinLabel}} Explorer +
{{- template "specific" . -}}
-
+ - + \ No newline at end of file diff --git a/static/templates/block.html b/static/templates/block.html index 87d4f2f8bc..5136f6e6f2 100644 --- a/static/templates/block.html +++ b/static/templates/block.html @@ -1,15 +1,13 @@ {{define "specific"}}{{$cs := .CoinShortcut}}{{$b := .Block}}{{$data := . -}} -

Block {{$b.Height}}

+

Block {{formatUint32 $b.Height}}

{{$b.Hash}}
-

Summary

-
@@ -69,10 +67,10 @@

Summary

{{- if $b.Transactions -}}

Transactions

- +
{{template "paging" $data}}
{{- range $tx := $b.Transactions}}{{$data := setTxToTemplateData $data $tx}}{{template "txdetail" $data}}{{end -}}
- +{{template "paging" $data }} {{end}}{{end}} \ No newline at end of file diff --git a/static/templates/blocks.html b/static/templates/blocks.html index cbb9d057bf..01349c7b3f 100644 --- a/static/templates/blocks.html +++ b/static/templates/blocks.html @@ -1,31 +1,32 @@ {{define "specific"}}{{$blocks := .Blocks}}{{$data := .}} -

Blocks by date -

+
+

Blocks

+
{{if $blocks.Blocks}}{{template "paging" $data }}{{end}}
+ {{if $blocks.Blocks -}} - -
- +
+
- - - - - + + + + + {{- range $b := $blocks.Blocks -}} - + - - + + {{- end -}}
HeightHashTimestampTransactionsSizeHeightHashTimestampTransactionsSize
{{$b.Height}}{{formatUint32 $b.Height}} {{$b.Hash}} {{formatUnixTime $b.Time}}{{$b.Txs}}{{$b.Size}}{{formatUint32 $b.Txs}}{{formatUint32 $b.Size}}
- +{{template "paging" $data }} {{end}}{{end}} \ No newline at end of file diff --git a/static/templates/index.html b/static/templates/index.html index 77ef6fbd5a..a5da0f9213 100644 --- a/static/templates/index.html +++ b/static/templates/index.html @@ -1,129 +1,135 @@ {{define "specific"}}{{$cs := .CoinShortcut}}{{$bb := .Info.Blockbook}}{{$be := .Info.Backend}}

Application status

{{- if $bb.InitialSync -}} -

Application is now in initial synchronization and does not provide any data.

+

Application is now in initial synchronization and does not provide any data.

{{- end -}} {{- if not $bb.SyncMode -}} -

Synchronization with backend is disabled, the state of index is not up to date.

+

Synchronization with backend is disabled, the state of index is not up to date.

{{- end -}}
-
-

Blockbook

- +
+
- - + + + + + + - + - + - + - + - + - + - + - + {{- if $bb.HasFiatRates -}} - + - + {{- end -}} - +
Coin{{$bb.Coin}}

Blockbook

Coin{{$bb.Coin}}
Host{{$bb.Host}}{{$bb.Host}}
Version / Commit / Build{{$bb.Version}} / {{$bb.GitCommit}} / {{$bb.BuildTime}}{{$bb.Version}} / {{$bb.GitCommit}} / {{$bb.BuildTime}}
Synchronized{{$bb.InSync}}
{{$bb.InSync}}
Last Block{{if .InternalExplorer}}{{$bb.BestHeight}}{{else}}{{$bb.BestHeight}}{{end}}{{if .InternalExplorer}}{{formatUint32 $bb.BestHeight}}{{else}}{{formatUint32 $bb.BestHeight}}{{end}}
Last Block Update{{formatTime $bb.LastBlockTime}}{{formatTime $bb.LastBlockTime}}
Mempool in Sync{{$bb.InSyncMempool}}
{{$bb.InSyncMempool}}
Last Mempool Update{{formatTime $bb.LastMempoolTime}}{{formatTime $bb.LastMempoolTime}}
Transactions in Mempool{{if .InternalExplorer}}{{$bb.MempoolSize}}{{else}}{{$bb.MempoolSize}}{{end}}{{if .InternalExplorer}}{{$bb.MempoolSize}}{{else}}{{formatInt $bb.MempoolSize}}{{end}}
Current Fiat rates{{formatTime $bb.CurrentFiatRatesTime}}{{formatTime $bb.CurrentFiatRatesTime}}
Historical Fiat rates{{formatTime $bb.HistoricalFiatRatesTime}}{{if $bb.HasTokenFiatRates}}
tokens {{formatTime $bb.HistoricalTokenFiatRatesTime}}{{end}}
{{formatTime $bb.HistoricalFiatRatesTime}}{{if $bb.HasTokenFiatRates}}
tokens {{formatTime $bb.HistoricalTokenFiatRatesTime}}{{end}}
Size On Disk{{$bb.DbSize}}{{formatInt64 $bb.DbSize}}
-
-

Backend

- +
+
+ + + + {{- if $be.BackendError -}} - + {{- end -}} - - + + - + {{- if $be.Subversion -}} - + {{- end -}} {{- if $be.ProtocolVersion -}} - + {{- end -}} {{- if $be.ConsensusVersion -}} - + {{- end -}} - + - + {{- if $be.Timeoffset -}} - + {{- end -}} {{- if $be.SizeOnDisk -}} - + {{- end -}} {{- if $be.Consensus -}} - + {{- end -}} {{- if $be.Warnings -}} diff --git a/static/templates/mempool.html b/static/templates/mempool.html index 0e5abb6d0f..a2f3164b0d 100644 --- a/static/templates/mempool.html +++ b/static/templates/mempool.html @@ -1,16 +1,14 @@ {{define "specific"}}{{$txs := .MempoolTxids.Mempool}}{{$data := .}} -

Mempool Transactions by time of appearance -

-
-
{{$.MempoolTxids.MempoolSize}} transactions in mempool
- -
-
-

Backend

Backend ErrorBackend Error {{$be.BackendError}}
Chain{{$be.Chain}}Chain{{$be.Chain}}
Version{{$be.Version}}{{$be.Version}}
Subversion{{$be.Subversion}}{{$be.Subversion}}
Protocol Version{{$be.ProtocolVersion}}{{$be.ProtocolVersion}}
Consensus Version{{$be.ConsensusVersion}}{{$be.ConsensusVersion}}
Last Block{{$be.Blocks}}{{formatInt $be.Blocks}}
Difficulty{{$be.Difficulty}}{{$be.Difficulty}}
Timeoffset{{$be.Timeoffset}}{{$be.Timeoffset}}
Size On Disk{{$be.SizeOnDisk}}{{formatInt64 $be.SizeOnDisk}}
Consensus{{toJSON $be.Consensus}}{{toJSON $be.Consensus}}
+
+

Mempool Transactions

{{$.MempoolTxids.MempoolSize}} transactions in mempool
+
{{if $txs}}{{template "paging" $data }}{{end}}
+ +
+
- + @@ -23,5 +21,5 @@
{{$.MempoolTxids.MempoolSize}} transactions in me
TransactionTimeTime of appearance
- +{{template "paging" $data }} {{end}} \ No newline at end of file diff --git a/static/templates/paging.html b/static/templates/paging.html index 4cb695aeaa..6dca06cd4b 100644 --- a/static/templates/paging.html +++ b/static/templates/paging.html @@ -1,11 +1,16 @@ -{{- define "paging"}}{{$data := .}}{{if $data.PrevPage -}} -
    -
  • <
  • - {{- range $p := $data.PagingRange -}} -
  • - {{- if $p}}{{$p}} - {{- else}}...{{end -}} -
  • {{- end -}} -
  • >
  • -
-{{- end}}{{end -}} \ No newline at end of file +{{define "paging"}}{{$data := .}}{{if $data.PrevPage}} + +{{end}}{{end}} \ No newline at end of file diff --git a/static/templates/xpub.html b/static/templates/xpub.html index 6b0ec8e165..6fdfea37f7 100644 --- a/static/templates/xpub.html +++ b/static/templates/xpub.html @@ -97,11 +97,11 @@

Transactions

- + {{template "paging" $data}}
{{- range $tx := $addr.Transactions}}{{$data := setTxToTemplateData $data $tx}}{{template "txdetail" $data}}{{end -}}
- +{{template "paging" $data }} {{end}}{{end}} \ No newline at end of file From 3e816b931ff9f3917bfa7b65eb8fc108022e5975 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 8 Nov 2022 12:08:47 +0100 Subject: [PATCH 097/530] Explorer redesign part 2 --- static/css/main2.css | 190 ++++++++++++++++- static/templates/base.html | 32 ++- static/templates/block.html | 54 ++--- static/templates/mempool.html | 36 ++-- static/templates/tokenDetail.html | 31 +-- static/templates/tx.html | 213 +++++++++----------- static/templates/txdetail.html | 121 +++++------ static/templates/txdetail_ethereumtype.html | 2 +- 8 files changed, 416 insertions(+), 263 deletions(-) diff --git a/static/css/main2.css b/static/css/main2.css index 18d3d44720..0ea1a4fbf0 100644 --- a/static/css/main2.css +++ b/static/css/main2.css @@ -51,6 +51,10 @@ a:hover { padding: 0.75rem 1rem; } +#header .container { + min-height: 50px; +} + .bb-group { border: 0.6rem solid #F6F6F6; background-color: #F6F6F6; @@ -244,16 +248,179 @@ span.btn-paging:hover { width: 128px; height: 32px; position: absolute; - top: 15px; + top: 16px; background-size: cover; - background-image: url("data:image/svg+xml, %3Csvg style='width: 128px%3B' version='1.1' xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 163.7 41.9' space='preserve'%3E%3Cpolygon points='101.1 12.8 118.2 12.8 118.2 17.3 108.9 29.9 118.2 29.9 118.2 35.2 101.1 35.2 101.1 30.7 110.4 18.1 101.1 18.1'%3E%3C/polygon%3E%3Cpath d='M158.8 26.9c2.1-0.8 4.3-2.9 4.3-6.6c0-4.5-3.1-7.4-7.7-7.4h-10.5v22.3h5.8v-7.5h2.2l4.1 7.5h6.7L158.8 26.9z M154.7 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C157.2 21.6 156.2 22.5 154.7 22.5z'%3E%3C/path%3E%3Cpath d='M130.8 12.5c-6.8 0-11.6 4.9-11.6 11.5s4.9 11.5 11.6 11.5s11.7-4.9 11.7-11.5S137.6 12.5 130.8 12.5z M130.8 30.3c-3.4 0-5.7-2.6-5.7-6.3c0-3.8 2.3-6.3 5.7-6.3c3.4 0 5.8 2.6 5.8 6.3C136.6 27.7 134.2 30.3 130.8 30.3z'%3E%3C/path%3E%3Cpolygon points='82.1 12.8 98.3 12.8 98.3 18 87.9 18 87.9 21.3 98 21.3 98 26.4 87.9 26.4 87.9 30 98.3 30 98.3 35.2 82.1 35.2'%3E%3C/polygon%3E%3Cpath d='M24.6 9.7C24.6 4.4 20 0 14.4 0S4.2 4.4 4.2 9.7v3.1H0v22.3h0l14.4 6.7l14.4-6.7h0V12.9h-4.2V9.7z M9.4 9.7c0-2.5 2.2-4.5 5-4.5s5 2 5 4.5v3.1H9.4V9.7z M23 31.5l-8.6 4l-8.6-4V18.1H23V31.5z'%3E%3C/path%3E%3Cpath d='M79.4 20.3c0-4.5-3.1-7.4-7.7-7.4H61.2v22.3H67v-7.5h2.2l4.1 7.5H80l-4.9-8.3C77.2 26.1 79.4 24 79.4 20.3z M71 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C73.5 21.6 72.5 22.5 71 22.5z'%3E%3C/path%3E%3Cpolygon points='40.5 12.8 58.6 12.8 58.6 18.1 52.4 18.1 52.4 35.2 46.6 35.2 46.6 18.1 40.5 18.1'%3E%3C/polygon%3E%3C/svg%3E"); + background-image: url("data:image/svg+xml,%3Csvg style='width: 128px%3B' version='1.1' xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 163.7 41.9' space='preserve'%3E%3Cpolygon points='101.1 12.8 118.2 12.8 118.2 17.3 108.9 29.9 118.2 29.9 118.2 35.2 101.1 35.2 101.1 30.7 110.4 18.1 101.1 18.1'%3E%3C/polygon%3E%3Cpath d='M158.8 26.9c2.1-0.8 4.3-2.9 4.3-6.6c0-4.5-3.1-7.4-7.7-7.4h-10.5v22.3h5.8v-7.5h2.2l4.1 7.5h6.7L158.8 26.9z M154.7 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C157.2 21.6 156.2 22.5 154.7 22.5z'%3E%3C/path%3E%3Cpath d='M130.8 12.5c-6.8 0-11.6 4.9-11.6 11.5s4.9 11.5 11.6 11.5s11.7-4.9 11.7-11.5S137.6 12.5 130.8 12.5z M130.8 30.3c-3.4 0-5.7-2.6-5.7-6.3c0-3.8 2.3-6.3 5.7-6.3c3.4 0 5.8 2.6 5.8 6.3C136.6 27.7 134.2 30.3 130.8 30.3z'%3E%3C/path%3E%3Cpolygon points='82.1 12.8 98.3 12.8 98.3 18 87.9 18 87.9 21.3 98 21.3 98 26.4 87.9 26.4 87.9 30 98.3 30 98.3 35.2 82.1 35.2'%3E%3C/polygon%3E%3Cpath d='M24.6 9.7C24.6 4.4 20 0 14.4 0S4.2 4.4 4.2 9.7v3.1H0v22.3h0l14.4 6.7l14.4-6.7h0V12.9h-4.2V9.7z M9.4 9.7c0-2.5 2.2-4.5 5-4.5s5 2 5 4.5v3.1H9.4V9.7z M23 31.5l-8.6 4l-8.6-4V18.1H23V31.5z'%3E%3C/path%3E%3Cpath d='M79.4 20.3c0-4.5-3.1-7.4-7.7-7.4H61.2v22.3H67v-7.5h2.2l4.1 7.5H80l-4.9-8.3C77.2 26.1 79.4 24 79.4 20.3z M71 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C73.5 21.6 72.5 22.5 71 22.5z'%3E%3C/path%3E%3Cpolygon points='40.5 12.8 58.6 12.8 58.6 18.1 52.4 18.1 52.4 35.2 46.6 35.2 46.6 18.1 40.5 18.1'%3E%3C/polygon%3E%3C/svg%3E"); } -.copy-icon { - width: 16px; +.copyable::before, +.copied::before { + width: 18px; height: 16px; + margin: 3px -18px; + content: ""; + position: absolute; + background-size: cover; +} + +.copyable::before { + display: none; + cursor: pointer; + background-image: url("data:image/svg+xml,%3Csvg width='18' height='16' viewBox='0 0 18 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.5 10.4996H13.5V2.49963H5.5V5.49963' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M10.4998 5.49976H2.49976V13.4998H10.4998V5.49976Z' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); +} + +.copyable:hover::before { + display: inline-block; +} + +.copied::before { + transition: all 0.4s ease; + transform: scale(1.2); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='16' viewBox='-30 -30 330 330'%3E%3Cpath d='M 30,180 90,240 240,30' style='stroke:%2300854D; stroke-width:32; fill:none'/%3E%3C/svg%3E"); +} + +.h-data { + letter-spacing: 0.12em; + font-weight: normal !important; +} + +.tx-detail { + background: #F6F6F6; + color: #757575; + border-radius: 10px; + box-shadow: 0 0 0 10px white; + width: calc(100% - 20px); + margin-left: 10px; + margin-top: 3rem; + overflow-wrap: break-word; +} + +.tx-detail:first-child { + margin-top: 1rem; +} + +.tx-detail:last-child { + margin-bottom: 3rem; +} + +.tx-detail span.ellipsis, +.tx-detail a.ellipsis { + display: block; + float: left; + max-width: 100%; +} + +.tx-detail>.head { + padding: 1.5rem; + border-radius: 10px 10px 0 0; + --bs-gutter-x: 0; +} + +.tx-detail .txid { + font-size: 106%; + letter-spacing: -0.01em; +} + +.tx-detail>.body { + padding: 0 1.5rem; + --bs-gutter-x: 0; + letter-spacing: -0.01em; +} + +.tx-detail>.footer { + padding: 1.5rem; + --bs-gutter-x: 0; +} + +.tx-in .col-12, +.tx-out .col-12 { + background-color: white; + padding: 1.2rem 1.3rem; + border-bottom: 1px solid #F6F6F6; +} + +.tx-in .col-12:last-child, +.tx-out .co-12l:last-child { + border-bottom: none; +} + +.tx-own { + background-color: #FFF9E3 !important; +} + +.tx-amt { + float: right !important; +} + +.tx-in .tx-own .tx-amt, +.spent { + color: #dc3545 !important; +} + +.tx-out .tx-own .tx-amt, +.unspent { + color: #28a745 !important; +} + +.outpoint { + color: #757575 !important; +} + +.spent, +.unspent, +.outpoint { + display: inline-block; + text-align: right; + min-width: 18px; + text-decoration: none !important; +} + +.octicon { + height: 24px; + width: 24px; + margin-left: -12px; + margin-top: 19px; + position: absolute; background-size: cover; - background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.5 10.4996H13.5V2.49963H5.5V5.49963' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M10.4998 5.49976H2.49976V13.4998H10.4998V5.49976Z' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); + background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9 4.5L16.5 12L9 19.5' stroke='%23AFAFAF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"); +} + +.txvalue { + color: black; + font-weight: bold; +} + +.unconfirmed { + color: white; + background-color: #C51E13; + padding: 0.7rem 1.2rem; + border-radius: 1.4rem; +} + +.json { + word-wrap: break-word; + font-size: smaller; + background: #002B31; + border-radius: 8px; +} + +#raw { + padding: 1.5rem 2rem; + color: #FFFFFF; + letter-spacing: 0.02em; +} + +#raw .string { + color: #2BCA87; +} + +#raw .number, +#raw .boolean { + color: #EFC941; +} + +#raw .null { + color: red; } @media (max-width: 768px) { @@ -261,16 +428,29 @@ span.btn-paging:hover { font-size: 0.8rem; } + .octicon { + scale: 60% !important; + margin-top: -2px; + } + .btn { --bs-btn-font-size: 0.8rem; } } @media (max-width: 991px) { + #header .container { + min-height: 40px + } + .trezor-logo { top: 10px; } + .octicon { + scale: 80%; + } + .table.data-table>:not(caption)>*>* { padding: 0.8rem 0.4rem; } diff --git a/static/templates/base.html b/static/templates/base.html index 58d0bd7b43..e54a7fb6a1 100644 --- a/static/templates/base.html +++ b/static/templates/base.html @@ -11,9 +11,39 @@ Trezor {{.CoinLabel}} Explorer diff --git a/static/templates/block.html b/static/templates/block.html index 5136f6e6f2..4ee93b7b64 100644 --- a/static/templates/block.html +++ b/static/templates/block.html @@ -1,75 +1,75 @@ {{define "specific"}}{{$cs := .CoinShortcut}}{{$b := .Block}}{{$data := . -}} -

Block {{formatUint32 $b.Height}}

-
- {{$b.Hash}} -
-
-

Summary

-
No Inputs (Newly Generated Coins)
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, }, }, { @@ -285,18 +278,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Address`, - `0.00012345 FAKE`, - `mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz`, - `0.00012345 FAKE`, - `7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25`, - `3172.83951061 FAKE `, - `mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL`, - `td>mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL`, - `9172.83951061 FAKE ×`, - `00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840`, - ``, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -305,11 +287,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Transaction

`, - `3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71`, - `0.00000062 FAKE`, - ``, + `Trezor Fake Coin Explorer

Transaction

3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input3172.83951062 FAKE
Total Output3172.83951 FAKE
Fees0.00000062 FAKE
Raw Transaction
`, }, }, { @@ -318,10 +296,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Error

`, - `

Transaction not found

`, - ``, + `Trezor Fake Coin Explorer

Error

Transaction not found

`, }, }, { @@ -330,14 +305,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Blocks`, - `225494`, - `00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6`, - `0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997`, - `2`, - `1234567`, - ``, + `Trezor Fake Coin Explorer

Blocks

HeightHashTimestampTransactionsSize
22549400000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b61639 days 11 hours ago42345678
2254930000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e29971640 days 9 hours ago21234567
`, }, }, { @@ -346,14 +314,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Block 225494

`, - `00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6`, - `4`, // number of transactions - `mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL`, - `9172.83951061 FAKE`, - `fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db`, - ``, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -362,12 +323,9 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Application status

`, - `

Synchronization with backend is disabled, the state of index is not up to date.

`, - `225494`, - `/Fakecoin:0.0.1/`, - ``, + `Trezor Fake Coin Explorer

Application status

Synchronization with backend is disabled, the state of index is not up to date.

`, + `

Blockbook

CoinFakecoin
Host
Version / Commit / Buildunknown / unknown / unknown
Synchronized
true
Last Block225494
Last Block Update`, + `
Mempool in Sync
false
Last Mempool Update
Transactions in Mempool0
Size On Disk

Backend

Chainfakecoin
Version001001
Subversion/Fakecoin:0.0.1/
Last Block2
Difficulty
Blockbook - blockchain indexer for Trezor wallet https://trezor.io/. Do not use for any other purpose.
`, }, }, { @@ -376,14 +334,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Block 225494

`, - `00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6`, - `4`, // number of transactions - `mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL`, - `9172.83951061 FAKE`, - `fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db`, - ``, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -392,14 +343,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Block 225494

`, - `00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6`, - `4`, // number of transactions - `mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL`, - `9172.83951061 FAKE`, - `fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db`, - ``, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -408,14 +352,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Transaction

`, - `fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db`, - `td class="data">0 FAKE`, - `mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj`, - `13.60030331 FAKE`, - `No Inputs (Newly Generated Coins)`, - ``, + `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, }, }, { @@ -424,18 +361,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Address`, - `0.00012345 FAKE`, - `mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz`, - `0.00012345 FAKE`, - `7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25`, - `3172.83951061 FAKE `, - `mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL`, - `td>mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL`, - `9172.83951061 FAKE ×`, - `00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840`, - ``, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -444,16 +370,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

XPUB 1186.419755 FAKE

upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q
`, - `Total Received1186.41975501 FAKE`, - `Total Sent0.00000001 FAKE`, - `Used XPUB Addresses2`, - `2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu1186.419755 FAKE1m/49'/1'/33'/1/3`, - ``, - ``, - `2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu10.00009876 FAKE `, - ``, + `Trezor Fake Coin Explorer

XPUB

upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q

1186.419755 FAKE

Confirmed
Total Received1186.41975501 FAKE
Total Sent0.00000001 FAKE
Final Balance1186.419755 FAKE
No. Transactions2
Used XPUB Addresses2
XPUB Addresses with Balance
AddressBalanceTxsPath
2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu1186.419755 FAKE1m/49'/1'/33'/1/3

Transactions

`, }, }, { @@ -462,12 +379,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

XPUB 0 FAKE

tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej

Confirmed

`, - `Total Received0 FAKE`, - `Total Sent0 FAKE`, - `Used XPUB Addresses0`, - ``, + `Trezor Fake Coin Explorer

XPUB

tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej

0 FAKE

Confirmed
Total Received0 FAKE
Total Sent0 FAKE
Final Balance0 FAKE
No. Transactions0
Used XPUB Addresses0
XPUB Addresses with Balance
No addresses
`, }, }, { @@ -476,10 +388,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Error

`, - `

No matching records found for '1234'

`, - ``, + `Trezor Fake Coin Explorer

Error

No matching records found for '1234'

`, }, }, { @@ -488,10 +397,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Send Raw Transaction

`, - ``, - ``, + `Trezor Fake Coin Explorer

Send Raw Transaction

`, }, }, { @@ -500,11 +406,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Fake Coin Explorer`, - `

Send Raw Transaction

`, - ``, - `
Invalid data
`, - ``, + `Trezor Fake Coin Explorer

Send Raw Transaction

Invalid data
`, }, }, { @@ -1582,7 +1484,13 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { } } +// fixedTimeNow returns always 2022-09-15 12:43:56 UTC +func fixedTimeNow() time.Time { + return time.Date(2022, 9, 15, 12, 43, 56, 0, time.UTC) +} + func Test_PublicServer_BitcoinType(t *testing.T) { + timeNow = fixedTimeNow parser := btc.NewBitcoinParser( btc.GetChainParams("test"), &btc.Configuration{ @@ -1635,3 +1543,110 @@ func Test_formatInt64(t *testing.T) { }) } } + +func Test_formatTime(t *testing.T) { + timeNow = fixedTimeNow + tests := []struct { + name string + want template.HTML + }{ + { + name: "2020-12-23 15:16:17", + want: `630 days 21 hours ago`, + }, + { + name: "2022-08-23 11:12:13", + want: `23 days 1 hour ago`, + }, + { + name: "2022-09-14 11:12:13", + want: `1 day 1 hour ago`, + }, + { + name: "2022-09-14 14:12:13", + want: `22 hours 31 mins ago`, + }, + { + name: "2022-09-15 09:33:26", + want: `3 hours 10 mins ago`, + }, + { + name: "2022-09-15 12:23:56", + want: `20 mins ago`, + }, + { + name: "2022-09-15 12:24:07", + want: `19 mins ago`, + }, + { + name: "2022-09-15 12:43:21", + want: `35 secs ago`, + }, + { + name: "2022-09-15 12:43:56", + want: `0 secs ago`, + }, + { + name: "2022-09-16 12:43:56", + want: `2022-09-16 12:43:56`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tm, _ := time.Parse("2006-01-02 15:04:05", tt.name) + if got := timeSpan(&tm); !reflect.DeepEqual(got, tt.want) { + t.Errorf("formatTime() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_appendAmountSpan(t *testing.T) { + tests := []struct { + name string + class string + amount string + shortcut string + txDate string + want string + }{ + { + name: "prim-amt 1.23456789 BTC", + class: "prim-amt", + amount: "1.23456789", + shortcut: "BTC", + want: `1.23456789 BTC`, + }, + { + name: "prim-amt 1432134.23456 BTC", + class: "prim-amt", + amount: "1432134.23456", + shortcut: "BTC", + want: `1432134.23456 BTC`, + }, + { + name: "sec-amt 431341.23 EUR", + class: "sec-amt", + amount: "4321341.23", + shortcut: "EUR", + want: `4321341.23 EUR`, + }, + { + name: "sec-amt 43141.29 EUR", + class: "sec-amt", + amount: "43141.29", + shortcut: "EUR", + txDate: "2022-03-14", + want: `43141.29 EUR`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var rv strings.Builder + appendAmountSpan(&rv, tt.class, tt.amount, tt.shortcut, tt.txDate) + if got := rv.String(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("formatTime() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/static/css/main.css b/static/css/main.css index 5914af121e..0e520cbc14 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -63,6 +63,12 @@ select { min-height: 50px; } +.form-control:focus { + outline: 0; + box-shadow: none; + border-color: #00854d; +} + .badge { vertical-align: middle; filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.15)); @@ -73,6 +79,20 @@ select { --bs-badge-border-radius: 0.6rem; } +.accordion { + --bs-accordion-border-radius: 10px; + --bs-accordion-inner-border-radius: calc(10px - 1px); + --bs-accordion-color: var(--bs-body-color); + --bs-accordion-active-color: var(--bs-body-color); + --bs-accordion-active-bg: white; + --bs-accordion-btn-active-icon: url("data:image/svg+xml,"); +} + +.accordion-button:focus { + outline: 0; + box-shadow: none; +} + .bb-group { border: 0.6rem solid #f6f6f6; background-color: #f6f6f6; @@ -180,7 +200,7 @@ span.btn-paging:hover { position: absolute; top: 16px; background-size: cover; - background-image: url("data:image/svg+xml, %3Csvg style='background: white%3B' width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7.24976 12.5C10.1493 12.5 12.4998 10.1495 12.4998 7.25C12.4998 4.35051 10.1493 2 7.24976 2C4.35026 2 1.99976 4.35051 1.99976 7.25C1.99976 10.1495 4.35026 12.5 7.24976 12.5Z' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3Cpath d='M10.962 10.9625L13.9996 14.0001' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3C/svg%3E"); + background-image: url("data:image/svg+xml, %3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7.24976 12.5C10.1493 12.5 12.4998 10.1495 12.4998 7.25C12.4998 4.35051 10.1493 2 7.24976 2C4.35026 2 1.99976 4.35051 1.99976 7.25C1.99976 10.1495 4.35026 12.5 7.24976 12.5Z' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3Cpath d='M10.962 10.9625L13.9996 14.0001' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3C/svg%3E"); } .navbar-form ::placeholder { @@ -251,6 +271,10 @@ span.btn-paging:hover { color: var(--bs-body-color); } +.accordion .table.data-table>thead>*>* { + padding-bottom: 0; +} + .info-table tbody { display: inline-table; width: 100%; @@ -339,12 +363,16 @@ span.btn-paging:hover { max-width: 100%; } -.tx-detail>.head { +.tx-detail>.head, +.tx-detail>.footer { padding: 1.5rem; - border-radius: 10px 10px 0 0; --bs-gutter-x: 0; } +.tx-detail>.head { + border-radius: 10px 10px 0 0; +} + .tx-detail .txid { font-size: 106%; letter-spacing: -0.01em; @@ -356,20 +384,38 @@ span.btn-paging:hover { letter-spacing: -0.01em; } -.tx-detail>.footer { - padding: 1.5rem; + +.tx-detail>.subhead { + padding: 1.5rem 1.5rem 0.4rem 1.5rem; --bs-gutter-x: 0; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--bs-body-color); +} + +.tx-detail>.subhead-2 { + padding: 0.3rem 1.5rem 0 1.5rem; + --bs-gutter-x: 0; + font-size: .875em; + color: var(--bs-body-color); } .tx-in .col-12, -.tx-out .col-12 { +.tx-out .col-12, +.tx-addr .col-12 { background-color: white; padding: 1.2rem 1.3rem; border-bottom: 1px solid #f6f6f6; } +.amt-out { + padding: 1.2rem 0 1.2rem 1rem; + text-align: right; + overflow-wrap: break-word; +} + .tx-in .col-12:last-child, -.tx-out .co-12l:last-child { +.tx-out .col-12:last-child { border-bottom: none; } @@ -427,8 +473,15 @@ span.btn-paging:hover { color: white; } +.txerror .copyable::before, +.txerror .copied::before { + /* turn svg stroke to white */ + filter: hue-rotate(180deg) brightness(1000%) contrast(100%); +} + .tx-amt .amt:hover, -.tx-amt.amt:hover { +.tx-amt.amt:hover, +.amt-out>.amt:hover { color: var(--bs-body-color); } @@ -444,9 +497,17 @@ span.btn-paging:hover { display: none; } +.base-amt { + display: none; +} + +.cbase-amt { + display: none; +} + .tooltip { --bs-tooltip-opacity: 1; - --bs-tooltip-max-width: 320px; + --bs-tooltip-max-width: 380px; --bs-tooltip-bg: #fff; --bs-tooltip-color: var(--bs-body-color); --bs-tooltip-padding-x: 1rem; @@ -454,27 +515,29 @@ span.btn-paging:hover { filter: drop-shadow(0px 24px 64px rgba(22, 27, 45, 0.25)); } -.amt-tooltip { +.l-tooltip { text-align: start; display: inline-block; } -.amt-tooltip .prim-amt, -.amt-tooltip .sec-amt, -.amt-tooltip .csec-amt { +.l-tooltip .prim-amt, +.l-tooltip .sec-amt, +.l-tooltip .csec-amt, +.l-tooltip .base-amt, +.l-tooltip .cbase-amt { display: initial; float: right; } -.amt-dec { - font-size: 97%; -} - -.amt-tooltip .amt-time { +.l-tooltip .amt-time { padding-right: 3rem; float: left; } +.amt-dec { + font-size: 95%; +} + .unconfirmed { color: white; background-color: #c51e13; @@ -539,6 +602,16 @@ span.btn-paging:hover { .table.data-table>:not(caption)>*>* { padding: 0.8rem 0.4rem; } + + .tx-in .col-12, + .tx-out .col-12, + .tx-addr .col-12 { + padding: 0.7rem 1.1rem; + } + + .amt-out { + padding: 0.7rem 0 0.7rem 1rem + } } @media (min-width: 769px) { diff --git a/static/js/main.js b/static/js/main.js index 7775513922..3b8eb37e30 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -50,7 +50,19 @@ function amountTooltip() { const prim = this.querySelector(".prim-amt"); const sec = this.querySelector(".sec-amt"); const csec = this.querySelector(".csec-amt"); + const base = this.querySelector(".base-amt"); + const cbase = this.querySelector(".cbase-amt"); let s = `${prim.outerHTML}
`; + if (base) { + let t = base.getAttribute("tm"); + if (!t) { + t = "now"; + } + s += `${t}${base.outerHTML}
`; + } + if (cbase) { + s += `now${cbase.outerHTML}
`; + } if (sec) { let t = sec.getAttribute("tm"); if (!t) { @@ -59,9 +71,15 @@ function amountTooltip() { s += `${t}${sec.outerHTML}
`; } if (csec) { - s += `now${csec.outerHTML}`; + s += `now${csec.outerHTML}
`; } - return `${s}`; + return `${s}`; +} + +function addressAliasTooltip() { + const type = this.getAttribute("alias-type"); + const address = this.getAttribute("cc"); + return `${type}
${address}
`; } window.addEventListener("DOMContentLoaded", () => { @@ -78,6 +96,13 @@ window.addEventListener("DOMContentLoaded", () => { ); } + document + .querySelectorAll("[alias-type]") + .forEach( + (e) => + new bootstrap.Tooltip(e, { title: addressAliasTooltip, html: true }) + ); + document .querySelectorAll("[tt]") .forEach((e) => new bootstrap.Tooltip(e, { title: e.getAttribute("tt") })); diff --git a/static/templates/address.html b/static/templates/address.html index 5af0b4cb1e..1fb0e1eb70 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -3,7 +3,7 @@

{{if $addr.ContractInfo}}Contract {{$addr.ContractInfo.Name}}{{if $addr.ContractInfo.Symbol}} ({{$addr.ContractInfo.Symbol}}){{end}}{{else}}Address{{end}}

{{$addr.AddrStr}}
-

{{amount $addr.BalanceSat $data "copyable"}}

+

{{amountSpan $addr.BalanceSat $data "copyable"}}

@@ -42,7 +42,7 @@

{{amount $addr.BalanceSat $data "copyable"}}

{{end}} Balance - {{amount $addr.BalanceSat $data "copyable"}} + {{amountSpan $addr.BalanceSat $data "copyable"}} Transactions @@ -74,7 +74,7 @@

{{amount $addr.BalanceSat $data "copyable"}}

{{range $t := $addr.Tokens}} {{if eq $t.Type "ERC20"}} - {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} + {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} {{formatAmountWithDecimals $t.BalanceSat $t.Decimals}} {{$t.Symbol}} {{$t.Transfers}} @@ -99,7 +99,7 @@

{{amount $addr.BalanceSat $data "copyable"}}

{{range $t := $addr.Tokens}} {{if eq $t.Type "ERC721"}} - {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} + {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} {{range $i, $iv := $t.Ids}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv 0}}{{end}} @@ -126,7 +126,7 @@

{{amount $addr.BalanceSat $data "copyable"}}

{{range $t := $addr.Tokens}} {{if eq $t.Type "ERC1155"}} - {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} + {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} {{range $i, $iv := $t.MultiTokenValues}}{{if $i}}, {{end}}{{$iv.Value}} of ID {{$iv.Id}}{{end}} @@ -143,15 +143,15 @@

{{amount $addr.BalanceSat $data "copyable"}}

{{else}} Total Received - {{amount $addr.TotalReceivedSat $data "copyable"}} + {{amountSpan $addr.TotalReceivedSat $data "copyable"}} Total Sent - {{amount $addr.TotalSentSat $data "copyable"}} + {{amountSpan $addr.TotalSentSat $data "copyable"}} Final Balance - {{amount $addr.BalanceSat $data "copyable"}} + {{amountSpan $addr.BalanceSat $data "copyable"}} No. Transactions @@ -169,7 +169,7 @@

{{amount $addr.BalanceSat $data "copyable"}}

Unconfirmed Balance - {{amount $addr.UnconfirmedBalanceSat $data "copyable"}} + {{amountSpan $addr.UnconfirmedBalanceSat $data "copyable"}} No. Transactions diff --git a/static/templates/block.html b/static/templates/block.html index 9edcfd2405..600175b9d4 100644 --- a/static/templates/block.html +++ b/static/templates/block.html @@ -28,7 +28,7 @@
{{formatUint32 $b.Height}} {{$b.Hash}} - {{formatUnixTime $b.Time}} + {{unixTimeSpan $b.Time}} {{formatUint32 $b.Txs}} {{formatUint32 $b.Size}} diff --git a/static/templates/index.html b/static/templates/index.html index b7e23060df..8e8922f676 100644 --- a/static/templates/index.html +++ b/static/templates/index.html @@ -36,7 +36,7 @@

tokens {{timeSpan $bb.HistoricalTokenFiatRatesTime}}{{end}} {{end}} @@ -77,7 +77,7 @@

{{$be.BackendError}} + {{$be.BackendError}} {{end}} @@ -135,7 +135,7 @@

{{$be.Warnings}} + {{$be.Warnings}} {{end}} diff --git a/static/templates/mempool.html b/static/templates/mempool.html index 44da108e3a..61cf00bf23 100644 --- a/static/templates/mempool.html +++ b/static/templates/mempool.html @@ -15,7 +15,7 @@ {{range $tx := $txs}} {{$tx.Txid}} - {{formatUnixTime $tx.Time}} + {{unixTimeSpan $tx.Time}} {{end}} diff --git a/static/templates/tokenDetail.html b/static/templates/tokenDetail.html index ecc7687a05..b09abe10ac 100644 --- a/static/templates/tokenDetail.html +++ b/static/templates/tokenDetail.html @@ -2,32 +2,32 @@

NFT Token Detail

- +
- + - + - + - + - +
Token ID{{$data.TokenId}}{{$data.TokenId}}
Contract{{$data.ContractInfo.Contract}} {{$data.ContractInfo.Name}}{{$data.ContractInfo.Contract}}
{{$data.ContractInfo.Name}}
Contract type{{$data.ContractInfo.Type}}{{$data.ContractInfo.Type}}
-
+
Metadata
@@ -38,7 +38,7 @@
Metadata

Transactions

ERC721 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
ID 1 S205
Fee: 0.00008794500041041 FAKE
Unconfirmed Transaction!0 FAKE
ERC20 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
871.180000950184 S74
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
7.674999999999991915 S13
Fee: 0.000216368 FAKE
Unconfirmed Transaction!0 FAKE
`, + `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE

Confirmed
Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers
Contract 130.000000001000123013 S131
Contract 740.001000123074 S741
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
`, }, }, { @@ -33,7 +33,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address 0.000000000123450093 FAKE

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

Confirmed

Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ERC1155 Tokens
ContractTokensTransfers
Contract 1111 of ID 1776, 10 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0 FAKE
ERC1155 Token Transfers
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
Fee: 0.000081891755740665 FAKE
Unconfirmed Transaction!0 FAKE
`, + `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE

Confirmed
Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, }, }, { @@ -42,14 +42,14 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101

Summary

In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE
Fees0.002081 FAKE
RBFON

Details

Input Data
Transfer
Method ID: 0xa9059cbb
Function: transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE (40 Gwei)
Fees0.002081 FAKE
RBFON
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, }, }, { name: "explorerTokenDetail " + dbtestdata.EthAddr7b, r: newGetRequest(ts.URL + "/nft/" + dbtestdata.EthAddrContractCd + "/" + "1"), status: http.StatusOK, contentType: "text/html; charset=utf-8", - body: []string{`

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9 Contract 205
Contract typeERC20
`, `Loading metadata from https://ipfs.io/ipfs/cda9fc258358ecaa88845f19af595e908bb7efe9.json`}, + body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
Contract typeERC20
`}, }, { name: "apiIndex", @@ -70,7 +70,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":12,"balance":"1000075074"}]}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":12,"balance":"1000075074"}],"totalBaseValue":1.23450075e-10}`, }, }, { @@ -79,7 +79,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"},"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9":{"Type":"Contract","Alias":"Contract 205"}}}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"}],"totalBaseValue":1.23450123e-10,"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"},"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9":{"Type":"Contract","Alias":"Contract 205"}}}`, }, }, { diff --git a/server/public_test.go b/server/public_test.go index 4b60745661..403dd8e8c3 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -278,7 +278,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -361,7 +361,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { diff --git a/server/websocket.go b/server/websocket.go index 15633af22d..837e2bc01a 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -63,7 +63,6 @@ type fiatRatesSubscription struct { // WebsocketServer is a handle to websocket server type WebsocketServer struct { - socket *websocket.Conn upgrader *websocket.Upgrader db *db.RocksDB txCache *db.TxCache @@ -483,15 +482,16 @@ func (s *WebsocketServer) onRequest(c *websocketChannel, req *websocketReq) { } type accountInfoReq struct { - Descriptor string `json:"descriptor"` - Details string `json:"details"` - Tokens string `json:"tokens"` - PageSize int `json:"pageSize"` - Page int `json:"page"` - FromHeight int `json:"from"` - ToHeight int `json:"to"` - ContractFilter string `json:"contractFilter"` - Gap int `json:"gap"` + Descriptor string `json:"descriptor"` + Details string `json:"details"` + Tokens string `json:"tokens"` + PageSize int `json:"pageSize"` + Page int `json:"page"` + FromHeight int `json:"from"` + ToHeight int `json:"to"` + ContractFilter string `json:"contractFilter"` + SecondaryCurrency string `json:"secondaryCurrency"` + Gap int `json:"gap"` } func unmarshalGetAccountInfoRequest(params []byte) (*accountInfoReq, error) { @@ -540,7 +540,7 @@ func (s *WebsocketServer) getAccountInfo(req *accountInfoReq) (res *api.Address, } a, err := s.api.GetXpubAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, req.Gap) if err != nil { - return s.api.GetAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter) + return s.api.GetAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, strings.ToLower(req.SecondaryCurrency)) } return a, nil } @@ -825,6 +825,8 @@ func (s *WebsocketServer) subscribeFiatRates(c *websocketChannel, d *fiatRatesSu currency := d.Currency if currency == "" { currency = allFiatRates + } else { + currency = strings.ToLower(currency) } as, ok := s.fiatRatesSubscriptions[currency] if !ok { diff --git a/static/css/main.css b/static/css/main.css index 0e520cbc14..2616a29c48 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -69,6 +69,12 @@ select { border-color: #00854d; } +.base-value { + color: #757575 !important; + padding-left: 0.5rem; + font-weight: normal; +} + .badge { vertical-align: middle; filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.15)); @@ -79,6 +85,10 @@ select { --bs-badge-border-radius: 0.6rem; } +.bg-secondary { + background-color: #757575 !important; +} + .accordion { --bs-accordion-border-radius: 10px; --bs-accordion-inner-border-radius: calc(10px - 1px); @@ -93,6 +103,10 @@ select { box-shadow: none; } +.accordion-body { + letter-spacing: -0.01em; +} + .bb-group { border: 0.6rem solid #f6f6f6; background-color: #f6f6f6; @@ -464,7 +478,7 @@ span.btn-paging:hover { } .txerror { - background-color: #c51f13b3; + background-color: #c51f13a0; color: white !important; } diff --git a/static/templates/address.html b/static/templates/address.html index 1fb0e1eb70..e1cc495f30 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -1,9 +1,12 @@ {{define "specific"}}{{$addr := .Address}}{{$data := .}}
-

{{if $addr.ContractInfo}}Contract {{$addr.ContractInfo.Name}}{{if $addr.ContractInfo.Symbol}} ({{$addr.ContractInfo.Symbol}}){{end}}{{else}}Address{{end}}

+

{{if $addr.ContractInfo}}Contract {{$addr.ContractInfo.Name}}{{if $addr.ContractInfo.Symbol}} ({{$addr.ContractInfo.Symbol}}){{end}}{{else}}Address {{addressAlias $addr.AddrStr $data}}{{end}}

{{$addr.AddrStr}}
-

{{amountSpan $addr.BalanceSat $data "copyable"}}

+

+ {{formattedAmountSpan $addr.BalanceSat 0 $data.CoinShortcut $data "copyable"}} + {{if $addr.TotalFiatValue}}{{summaryValuesSpan $addr.TotalBaseValue $addr.TotalFiatValue $data}}{{end}} +

@@ -20,6 +23,26 @@

{{amountSpan $addr.BalanceSat $data "copyable"}}

{{if eq .ChainType 1}} + + Balance + {{amountSpan $addr.BalanceSat $data "copyable"}} + + + Transactions + {{formatInt $addr.Txs}} + + + Non-contract Transactions + {{formatInt $addr.NonTokenTxs}} + + + Internal Transactions + {{formatInt $addr.InternalTxs}} + + + Nonce + {{$addr.Nonce}} + {{if $addr.ContractInfo}} {{if $addr.ContractInfo.Type}} @@ -30,153 +53,167 @@

{{amountSpan $addr.BalanceSat $data "copyable"}}

{{if $addr.ContractInfo.CreatedInBlock}} Created in Block - {{$addr.ContractInfo.CreatedInBlock}} + {{formatUint32 $addr.ContractInfo.CreatedInBlock}} {{end}} {{if $addr.ContractInfo.DestructedInBlock}} Destructed in Block - {{$addr.ContractInfo.DestructedInBlock}} + {{formatUint32 $addr.ContractInfo.DestructedInBlock}} {{end}} {{end}} + {{else}} - Balance - {{amountSpan $addr.BalanceSat $data "copyable"}} + Total Received + {{amountSpan $addr.TotalReceivedSat $data "copyable"}} - Transactions - {{$addr.Txs}} + Total Sent + {{amountSpan $addr.TotalSentSat $data "copyable"}} - Non-contract Transactions - {{$addr.NonTokenTxs}} + Final Balance + {{amountSpan $addr.BalanceSat $data "copyable"}} - Internal Transactions - {{$addr.InternalTxs}} + No. Transactions + {{formatInt $addr.Txs}} + {{end}} + + +{{if $addr.UnconfirmedTxs}} + + - - + + - {{if tokenCount $addr.Tokens "ERC20"}} - - - - {{end}} - {{if tokenCount $addr.Tokens "ERC721"}} - - - - - {{end}} - {{if tokenCount $addr.Tokens "ERC1155"}} - - - - - {{end}} - - {{else}} - - - - - - - - - - - - - - - - - {{end}} - -
Nonce{{$addr.Nonce}}
Unconfirmed
ERC20 Tokens - + + + + + + + + +
Unconfirmed Balance{{amountSpan $addr.UnconfirmedBalanceSat $data "copyable"}}
No. Transactions{{formatInt $addr.UnconfirmedTxs}}
+{{end}} +{{if eq .ChainType 1}} +{{if tokenCount $addr.Tokens "ERC20"}} +
+
+
+ +
+
+
+ - - - + + + + {{range $t := $addr.Tokens}} {{if eq $t.Type "ERC20"}} - - - + + + + {{end}} {{end}}
ContractTokensTransfersContractQuantityValueTransfers
{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}}{{formatAmountWithDecimals $t.BalanceSat $t.Decimals}} {{$t.Symbol}}{{$t.Transfers}}{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}}{{formattedAmountSpan $t.BalanceSat $t.Decimals $t.Symbol $data "copyable"}}{{summaryValuesSpan $t.BaseValue $t.FiatValue $data}}{{formatInt $t.Transfers}}
-
ERC721 Tokens - + + + + +{{end}} +{{if tokenCount $addr.Tokens "ERC721"}} +
+
+
+ +
+
+
+
- - - + + + {{range $t := $addr.Tokens}} {{if eq $t.Type "ERC721"}} - + - + {{end}} {{end}}
ContractTokensTransfersContractTokensTransfers
{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}}{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} {{range $i, $iv := $t.Ids}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv 0}}{{end}} {{$t.Transfers}}{{$t.Transfers}}
-
ERC1155 Tokens - + + + + +{{end}} +{{if tokenCount $addr.Tokens "ERC1155"}} +
+
+
+ +
+
+
+
- - - + + + {{range $t := $addr.Tokens}} {{if eq $t.Type "ERC1155"}} - + - + {{end}} {{end}}
ContractTokensTransfersContractTokensTransfers
{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}}{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} - {{range $i, $iv := $t.MultiTokenValues}}{{if $i}}, {{end}}{{$iv.Value}} of ID {{$iv.Id}}{{end}} + {{range $i, $iv := $t.MultiTokenValues}}{{if $i}}, {{end}}{{formattedAmountSpan $iv.Value 0 $t.Symbol $data ""}} of ID {{$iv.Id}}{{end}} {{$t.Transfers}}{{formatInt $t.Transfers}}
-
Total Received{{amountSpan $addr.TotalReceivedSat $data "copyable"}}
Total Sent{{amountSpan $addr.TotalSentSat $data "copyable"}}
Final Balance{{amountSpan $addr.BalanceSat $data "copyable"}}
No. Transactions{{formatInt $addr.Txs}}
-{{if $addr.UnconfirmedTxs}} - - - - - - - - - - - - - - - -
Unconfirmed
Unconfirmed Balance{{amountSpan $addr.UnconfirmedBalanceSat $data "copyable"}}
No. Transactions{{formatInt $addr.UnconfirmedTxs}}
+
+
+
+
+{{end}} {{end}} {{if or $addr.Transactions $addr.Filter}}
diff --git a/static/templates/tokenDetail.html b/static/templates/tokenDetail.html index b09abe10ac..2bcd02672c 100644 --- a/static/templates/tokenDetail.html +++ b/static/templates/tokenDetail.html @@ -6,11 +6,11 @@

NFT Token Detail

Token ID - {{$data.TokenId}} + {{$data.TokenId}} NTF Name - + NTF Description @@ -18,7 +18,7 @@

NFT Token Detail

Contract - {{$data.ContractInfo.Contract}}
{{$data.ContractInfo.Name}} + {{$data.ContractInfo.Contract}}
{{$data.ContractInfo.Name}} Contract type diff --git a/static/templates/txdetail_ethereumtype.html b/static/templates/txdetail_ethereumtype.html index d07b62383f..58646f8a2b 100644 --- a/static/templates/txdetail_ethereumtype.html +++ b/static/templates/txdetail_ethereumtype.html @@ -48,7 +48,7 @@
No Outputs
{{end}}
- +
{{amountSpan $tx.ValueOutSat $data "tx-out copyable"}}
@@ -175,7 +175,7 @@
{{range $i, $iv := $tt.MultiTokenValues}} - {{if $i}}, {{end}}{{$iv.Value}} {{$tt.Symbol}} of ID {{$iv.Id}} + {{if $i}}, {{end}}{{formattedAmountSpan $iv.Value 0 $tt.Symbol $data ""}} of ID {{$iv.Id}} {{end}}
diff --git a/static/test-websocket.html b/static/test-websocket.html index 37ab4c754f..99d3bc3fcc 100644 --- a/static/test-websocket.html +++ b/static/test-websocket.html @@ -141,6 +141,7 @@ const from = parseInt(document.getElementById("getAccountInfoFrom").value); const to = parseInt(document.getElementById("getAccountInfoTo").value); const contractFilter = document.getElementById("getAccountInfoContract").value.trim(); + const secondaryCurrency = document.getElementById("getAccountInfoSecondaryCurrency").value.trim(); const pageSize = 10; const method = 'getAccountInfo'; const tokens = "derived"; // could be "nonzero", "used", default is "derived" i.e. all @@ -152,7 +153,8 @@ pageSize, from, to, - contractFilter + contractFilter, + secondaryCurrency, // default gap=20 }; send(method, params, function (result) { @@ -471,9 +473,10 @@

Blockbook Websocket Test Page

- - - + + + +
From 9919f1a6853d868ce8c553529cb2501e5be45e67 Mon Sep 17 00:00:00 2001 From: Vitalij Dovhanyc <45185420+vdovhanych@users.noreply.github.com> Date: Wed, 30 Nov 2022 21:53:08 +0100 Subject: [PATCH 103/530] chore: update footer links (#838) --- api/embed/tos_link | 2 +- server/public_ethereumtype_test.go | 8 ++++---- server/public_test.go | 32 +++++++++++++++--------------- static/templates/base.html | 4 ++-- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/api/embed/tos_link b/api/embed/tos_link index 9b16245e61..8c90089b92 100644 --- a/api/embed/tos_link +++ b/api/embed/tos_link @@ -1 +1 @@ -https://shop.trezor.io/static/shared/about/terms-of-use.pdf +https://trezor.io/terms-of-use diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index e6dcff492f..48251a9d0a 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -24,7 +24,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE

Confirmed
Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers
Contract 130.000000001000123013 S131
Contract 740.001000123074 S741
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
`, + `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE

Confirmed
Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers
Contract 130.000000001000123013 S131
Contract 740.001000123074 S741
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
`, }, }, { @@ -33,7 +33,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE

Confirmed
Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, + `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE

Confirmed
Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, }, }, { @@ -42,14 +42,14 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE (40 Gwei)
Fees0.002081 FAKE
RBFON
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE (40 Gwei)
Fees0.002081 FAKE
RBFON
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, }, }, { name: "explorerTokenDetail " + dbtestdata.EthAddr7b, r: newGetRequest(ts.URL + "/nft/" + dbtestdata.EthAddrContractCd + "/" + "1"), status: http.StatusOK, contentType: "text/html; charset=utf-8", - body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
Contract typeERC20
`}, + body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
Contract typeERC20
`}, }, { name: "apiIndex", diff --git a/server/public_test.go b/server/public_test.go index 403dd8e8c3..b08d2732f9 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -269,7 +269,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, }, }, { @@ -278,7 +278,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -287,7 +287,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input3172.83951062 FAKE
Total Output3172.83951 FAKE
Fees0.00000062 FAKE
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input3172.83951062 FAKE
Total Output3172.83951 FAKE
Fees0.00000062 FAKE
Raw Transaction
`, }, }, { @@ -296,7 +296,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Error

Transaction not found

`, + `Trezor Fake Coin Explorer

Error

Transaction not found

`, }, }, { @@ -305,7 +305,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Blocks

HeightHashTimestampTransactionsSize
22549400000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b61639 days 11 hours ago42345678
2254930000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e29971640 days 9 hours ago21234567
`, + `Trezor Fake Coin Explorer

Blocks

HeightHashTimestampTransactionsSize
22549400000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b61639 days 11 hours ago42345678
2254930000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e29971640 days 9 hours ago21234567
`, }, }, { @@ -314,7 +314,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -325,7 +325,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { body: []string{ `Trezor Fake Coin Explorer

Application status

Synchronization with backend is disabled, the state of index is not up to date.

`, - `

Blockbook

CoinFakecoin
Host
Version / Commit / Buildunknown / unknown / unknown
Synchronized
true
Last Block225494
Last Block Update`, `
Mempool in Sync
false
Last Mempool Update
Transactions in Mempool0
Size On Disk

Backend

Chainfakecoin
Version001001
Subversion/Fakecoin:0.0.1/
Last Block2
Difficulty
Blockbook - blockchain indexer for Trezor wallet https://trezor.io/. Do not use for any other purpose.
`, + `

Backend

Chainfakecoin
Version001001
Subversion/Fakecoin:0.0.1/
Last Block2
Difficulty
Blockbook - blockchain indexer for Trezor wallet https://trezor.io/. Do not use for any other purpose.`, }, }, { @@ -334,7 +334,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -343,7 +343,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -352,7 +352,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, }, }, { @@ -361,7 +361,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -370,7 +370,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

XPUB

upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q

1186.419755 FAKE

Confirmed
Total Received1186.41975501 FAKE
Total Sent0.00000001 FAKE
Final Balance1186.419755 FAKE
No. Transactions2
Used XPUB Addresses2
XPUB Addresses with Balance
AddressBalanceTxsPath
2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu1186.419755 FAKE1m/49'/1'/33'/1/3

Transactions

`, + `Trezor Fake Coin Explorer

XPUB

upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q

1186.419755 FAKE

Confirmed
Total Received1186.41975501 FAKE
Total Sent0.00000001 FAKE
Final Balance1186.419755 FAKE
No. Transactions2
Used XPUB Addresses2
XPUB Addresses with Balance
AddressBalanceTxsPath
2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu1186.419755 FAKE1m/49'/1'/33'/1/3

Transactions

`, }, }, { @@ -379,7 +379,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

XPUB

tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej

0 FAKE

Confirmed
Total Received0 FAKE
Total Sent0 FAKE
Final Balance0 FAKE
No. Transactions0
Used XPUB Addresses0
XPUB Addresses with Balance
No addresses
`, + `Trezor Fake Coin Explorer

XPUB

tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej

0 FAKE

Confirmed
Total Received0 FAKE
Total Sent0 FAKE
Final Balance0 FAKE
No. Transactions0
Used XPUB Addresses0
XPUB Addresses with Balance
No addresses
`, }, }, { @@ -388,7 +388,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Error

No matching records found for '1234'

`, + `Trezor Fake Coin Explorer

Error

No matching records found for '1234'

`, }, }, { @@ -397,7 +397,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Send Raw Transaction

`, + `Trezor Fake Coin Explorer

Send Raw Transaction

`, }, }, { @@ -406,7 +406,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Send Raw Transaction

Invalid data
`, + `Trezor Fake Coin Explorer

Send Raw Transaction

Invalid data
`, }, }, { diff --git a/static/templates/base.html b/static/templates/base.html index 4793091dc8..17a933f19c 100644 --- a/static/templates/base.html +++ b/static/templates/base.html @@ -73,7 +73,7 @@ Trezor
- Suite + Suite Support @@ -82,7 +82,7 @@ Send Transaction - Don't have a Trezor? Get one! + Don't have a Trezor? Get one! From dca00bf7701c10d1bc6127066b4c85f857be764a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 3 Dec 2022 00:30:20 +0100 Subject: [PATCH 104/530] Explorer redesing tuning --- api/types.go | 19 +++--- api/types_test.go | 102 +++++++++++++++++++++++++++++ api/worker.go | 29 ++++---- api/xpub.go | 18 ++++- bchain/coins/eth/ethrpc.go | 3 +- server/public.go | 19 ++++-- server/public_ethereumtype_test.go | 12 ++-- server/public_test.go | 32 ++++----- server/websocket.go | 2 +- static/css/main.css | 16 ++++- static/templates/address.html | 21 ++++-- static/templates/base.html | 2 +- static/templates/index.html | 2 +- static/templates/tx.html | 4 +- static/templates/xpub.html | 7 +- 15 files changed, 221 insertions(+), 67 deletions(-) diff --git a/api/types.go b/api/types.go index e15d053698..3941dcdcb0 100644 --- a/api/types.go +++ b/api/types.go @@ -5,7 +5,6 @@ import ( "errors" "math/big" "sort" - "strings" "time" "github.com/trezor/blockbook/bchain" @@ -190,14 +189,17 @@ func (a Tokens) Less(i, j int) bool { } else if ti.BaseValue > tj.BaseValue { return true } - c := strings.Compare(ti.Name, tj.Name) - if c == 1 { - return false - } else if c == -1 { - return true + if ti.Name == "" { + if tj.Name != "" { + return false + } + } else { + if tj.Name == "" { + return true + } + return ti.Name < tj.Name } - c = strings.Compare(ti.Contract, tj.Contract) - return c == -1 + return ti.Contract < tj.Contract } // TokenTransfer contains info about a token transfer done in a transaction @@ -329,6 +331,7 @@ type Address struct { Nonce string `json:"nonce,omitempty"` UsedTokens int `json:"usedTokens,omitempty"` Tokens Tokens `json:"tokens,omitempty"` + FiatValue float64 `json:"fiatValue,omitempty"` TokensBaseValue float64 `json:"tokensBaseValue,omitempty"` TokensFiatValue float64 `json:"tokensFiatValue,omitempty"` TotalBaseValue float64 `json:"totalBaseValue,omitempty"` diff --git a/api/types_test.go b/api/types_test.go index 07b3a54bd2..d2d53380a3 100644 --- a/api/types_test.go +++ b/api/types_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "math/big" "reflect" + "sort" "testing" ) @@ -219,3 +220,104 @@ func TestAmount_Compare(t *testing.T) { }) } } + +func TestTokens_Sort(t *testing.T) { + tests := []struct { + name string + unsorted Tokens + sorted Tokens + }{ + { + name: "one", + unsorted: Tokens{ + { + Name: "a", + Contract: "0x1", + BaseValue: 12.34, + }, + }, + sorted: Tokens{ + { + Name: "a", + Contract: "0x1", + BaseValue: 12.34, + }, + }, + }, + { + name: "mix", + unsorted: Tokens{ + { + Name: "", + Contract: "0x6", + BaseValue: 0, + }, + { + Name: "", + Contract: "0x5", + BaseValue: 0, + }, + { + Name: "b", + Contract: "0x2", + BaseValue: 1, + }, + { + Name: "d", + Contract: "0x4", + BaseValue: 0, + }, + { + Name: "a", + Contract: "0x1", + BaseValue: 12.34, + }, + { + Name: "c", + Contract: "0x3", + BaseValue: 0, + }, + }, + sorted: Tokens{ + { + Name: "a", + Contract: "0x1", + BaseValue: 12.34, + }, + { + Name: "b", + Contract: "0x2", + BaseValue: 1, + }, + { + Name: "c", + Contract: "0x3", + BaseValue: 0, + }, + { + Name: "d", + Contract: "0x4", + BaseValue: 0, + }, + { + Name: "", + Contract: "0x5", + BaseValue: 0, + }, + { + Name: "", + Contract: "0x6", + BaseValue: 0, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sort.Sort(tt.unsorted) + if !reflect.DeepEqual(tt.unsorted, tt.sorted) { + t.Errorf("Tokens Sort got %v, want %v", tt.unsorted, tt.sorted) + } + }) + } +} diff --git a/api/worker.go b/api/worker.go index 60675692f1..e22904a2fa 100644 --- a/api/worker.go +++ b/api/worker.go @@ -1256,22 +1256,26 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco } } } - var secondaryRate, totalFiatValue float64 - ticker := w.is.GetCurrentTicker("", "") - totalBaseValue, err := strconv.ParseFloat((*Amount)(&ba.BalanceSat).DecimalString(w.chainParser.AmountDecimals()), 64) - if ticker != nil && err == nil { - r, found := ticker.Rates[secondaryCoin] - if found { - secondaryRate = float64(r) - } - } if w.chainType == bchain.ChainBitcoinType { totalReceived = ba.ReceivedSat() totalSent = &ba.SentSat - } else { - totalBaseValue += ed.tokensBaseValue } - totalFiatValue = secondaryRate * totalBaseValue + var secondaryRate, totalFiatValue, totalBaseValue, fiatValue float64 + if secondaryCoin != "" { + ticker := w.is.GetCurrentTicker("", "") + balance, err := strconv.ParseFloat((*Amount)(&ba.BalanceSat).DecimalString(w.chainParser.AmountDecimals()), 64) + if ticker != nil && err == nil { + r, found := ticker.Rates[secondaryCoin] + if found { + secondaryRate = float64(r) + } + } + fiatValue = secondaryRate * balance + if w.chainType == bchain.ChainEthereumType { + totalBaseValue += balance + ed.tokensBaseValue + totalFiatValue = secondaryRate * totalBaseValue + } + } r := &Address{ Paging: pg, AddrStr: address, @@ -1286,6 +1290,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco Transactions: txs, Txids: txids, Tokens: ed.tokens, + FiatValue: fiatValue, TokensBaseValue: ed.tokensBaseValue, TokensFiatValue: ed.tokensFiatValue, TotalBaseValue: totalBaseValue, diff --git a/api/xpub.go b/api/xpub.go index b555e379a5..3a1b4cc2c4 100644 --- a/api/xpub.go +++ b/api/xpub.go @@ -4,6 +4,7 @@ import ( "fmt" "math/big" "sort" + "strconv" "sync" "time" @@ -387,7 +388,7 @@ func (w *Worker) getXpubData(xd *bchain.XpubDescriptor, page int, txsOnPage int, } // GetXpubAddress computes address value and gets transactions for given address -func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option AccountDetails, filter *AddressFilter, gap int) (*Address, error) { +func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option AccountDetails, filter *AddressFilter, gap int, secondaryCoin string) (*Address, error) { start := time.Now() page-- if page < 0 { @@ -567,6 +568,20 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc setIsOwnAddresses(txs, xpubAddresses) var totalReceived big.Int totalReceived.Add(&data.balanceSat, &data.sentSat) + + var fiatValue float64 + if secondaryCoin != "" { + ticker := w.is.GetCurrentTicker("", "") + balance, err := strconv.ParseFloat((*Amount)(&data.balanceSat).DecimalString(w.chainParser.AmountDecimals()), 64) + if ticker != nil && err == nil { + r, found := ticker.Rates[secondaryCoin] + if found { + secondaryRate := float64(r) + fiatValue = secondaryRate * balance + } + } + } + addr := Address{ Paging: pg, AddrStr: xpub, @@ -580,6 +595,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc Txids: txids, UsedTokens: usedTokens, Tokens: tokens, + FiatValue: fiatValue, XPubAddresses: xpubAddresses, AddressAliases: w.getAddressAliases(addresses), } diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 2077d10250..f241d7b16f 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -592,7 +592,7 @@ func (b *EthereumRPC) getCreationContractInfo(contract string, height uint32) *b func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInternalData, contracts []bchain.ContractInfo, blockHeight uint32) []bchain.ContractInfo { value, err := hexutil.DecodeBig(call.Value) - if call.Type == "CREATE" { + if call.Type == "CREATE" || call.Type == "CREATE2" { d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ Type: bchain.CREATE, Value: *value, @@ -600,7 +600,6 @@ func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInt To: call.To, // new contract address }) contracts = append(contracts, *b.getCreationContractInfo(call.To, blockHeight)) - } else if call.Type == "SELFDESTRUCT" { d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ Type: bchain.SELFDESTRUCT, diff --git a/server/public.go b/server/public.go index 019f65d0ba..8147db9ce9 100644 --- a/server/public.go +++ b/server/public.go @@ -850,8 +850,12 @@ func (s *PublicServer) summaryValuesSpan(baseValue float64, secondaryValue float rv.WriteString(")") } } else { - if td.SecondaryCoin != "" { - rv.WriteString("-") + if baseValue > 0 { + appendAmountSpan(&rv, "", strconv.FormatFloat(baseValue, 'f', 6, 64), td.CoinShortcut, "") + } else { + if td.SecondaryCoin != "" { + rv.WriteString("-") + } } } return template.HTML(rv.String()) @@ -1184,16 +1188,16 @@ func (s *PublicServer) explorerXpub(w http.ResponseWriter, r *http.Request) (tpl return errorTpl, nil, api.NewAPIError("Missing xpub", true) } s.metrics.ExplorerViews.With(common.Labels{"action": "xpub"}).Inc() - page, _, _, filter, filterParam, gap := s.getAddressQueryParams(r, api.AccountDetailsTxHistoryLight, txsOnPage) // do not allow txsOnPage and details to be changed by query params - address, err := s.api.GetXpubAddress(xpub, page, txsOnPage, api.AccountDetailsTxHistoryLight, filter, gap) + page, _, _, filter, filterParam, gap := s.getAddressQueryParams(r, api.AccountDetailsTxHistoryLight, txsOnPage) + data := s.newTemplateData(r) + address, err := s.api.GetXpubAddress(xpub, page, txsOnPage, api.AccountDetailsTxHistoryLight, filter, gap, strings.ToLower(data.SecondaryCoin)) if err != nil { if err == api.ErrUnsupportedXpub { err = api.NewAPIError("XPUB functionality is not supported", true) } return errorTpl, nil, err } - data := s.newTemplateData(r) data.AddrStr = address.AddrStr data.Address = address data.Page = address.Page @@ -1267,7 +1271,7 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t var err error s.metrics.ExplorerViews.With(common.Labels{"action": "search"}).Inc() if len(q) > 0 { - address, err = s.api.GetXpubAddress(q, 0, 1, api.AccountDetailsBasic, &api.AddressFilter{Vout: api.AddressFilterVoutOff}, 0) + address, err = s.api.GetXpubAddress(q, 0, 1, api.AccountDetailsBasic, &api.AddressFilter{Vout: api.AddressFilterVoutOff}, 0, "") if err == nil { http.Redirect(w, r, joinURL("/xpub/", url.QueryEscape(address.AddrStr)), http.StatusFound) return noTpl, nil, nil @@ -1501,7 +1505,8 @@ func (s *PublicServer) apiXpub(r *http.Request, apiVersion int) (interface{}, er var err error s.metrics.ExplorerViews.With(common.Labels{"action": "api-xpub"}).Inc() page, pageSize, details, filter, _, gap := s.getAddressQueryParams(r, api.AccountDetailsTxidHistory, txsInAPI) - address, err = s.api.GetXpubAddress(xpub, page, pageSize, details, filter, gap) + secondaryCoin := strings.ToLower(r.URL.Query().Get("secondary")) + address, err = s.api.GetXpubAddress(xpub, page, pageSize, details, filter, gap, secondaryCoin) if err == nil && apiVersion == apiV1 { return s.api.AddressToV1(address), nil } diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 48251a9d0a..92731f90f1 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -24,7 +24,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE

Confirmed
Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers
Contract 130.000000001000123013 S131
Contract 740.001000123074 S741
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
`, + `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE

Confirmed
Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers#
Contract 130.000000001000123013 S131
Contract 740.001000123074 S741
ContractTokensTransfers#
Contract 20511

Transactions

ERC721 Token Transfers
`, }, }, { @@ -33,7 +33,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE

Confirmed
Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, + `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE

Confirmed
Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers#
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, }, }, { @@ -42,14 +42,14 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE (40 Gwei)
Fees0.002081 FAKE
RBFON
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE (40 Gwei)
Fees0.002081 FAKE
RBFON
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, }, }, { name: "explorerTokenDetail " + dbtestdata.EthAddr7b, r: newGetRequest(ts.URL + "/nft/" + dbtestdata.EthAddrContractCd + "/" + "1"), status: http.StatusOK, contentType: "text/html; charset=utf-8", - body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
Contract typeERC20
`}, + body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
Contract typeERC20
`}, }, { name: "apiIndex", @@ -70,7 +70,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":12,"balance":"1000075074"}],"totalBaseValue":1.23450075e-10}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":12,"balance":"1000075074"}]}`, }, }, { @@ -79,7 +79,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"}],"totalBaseValue":1.23450123e-10,"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"},"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9":{"Type":"Contract","Alias":"Contract 205"}}}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"}],"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"},"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9":{"Type":"Contract","Alias":"Contract 205"}}}`, }, }, { diff --git a/server/public_test.go b/server/public_test.go index b08d2732f9..ba9216858b 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -269,7 +269,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, }, }, { @@ -278,7 +278,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -287,7 +287,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input3172.83951062 FAKE
Total Output3172.83951 FAKE
Fees0.00000062 FAKE
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input3172.83951062 FAKE
Total Output3172.83951 FAKE
Fees0.00000062 FAKE
Raw Transaction
`, }, }, { @@ -296,7 +296,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Error

Transaction not found

`, + `Trezor Fake Coin Explorer

Error

Transaction not found

`, }, }, { @@ -305,7 +305,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Blocks

HeightHashTimestampTransactionsSize
22549400000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b61639 days 11 hours ago42345678
2254930000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e29971640 days 9 hours ago21234567
`, + `Trezor Fake Coin Explorer

Blocks

HeightHashTimestampTransactionsSize
22549400000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b61639 days 11 hours ago42345678
2254930000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e29971640 days 9 hours ago21234567
`, }, }, { @@ -314,7 +314,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -325,7 +325,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { body: []string{ `Trezor Fake Coin Explorer

Application status

Synchronization with backend is disabled, the state of index is not up to date.

`, - `

Blockbook

CoinFakecoin
Host
Version / Commit / Buildunknown / unknown / unknown
Synchronized
true
Last Block225494
Last Block Update`, `
Mempool in Sync
false
Last Mempool Update
Transactions in Mempool0
Size On Disk

Backend

Chainfakecoin
Version001001
Subversion/Fakecoin:0.0.1/
Last Block2
Difficulty
Blockbook - blockchain indexer for Trezor wallet https://trezor.io/. Do not use for any other purpose.
`, + `

Backend

Chainfakecoin
Version001001
Subversion/Fakecoin:0.0.1/
Last Block2
Difficulty
Blockbook - blockchain indexer for Trezor wallet https://trezor.io/. Do not use for any other purpose.`, }, }, { @@ -334,7 +334,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -343,7 +343,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -352,7 +352,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, }, }, { @@ -361,7 +361,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -370,7 +370,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

XPUB

upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q

1186.419755 FAKE

Confirmed
Total Received1186.41975501 FAKE
Total Sent0.00000001 FAKE
Final Balance1186.419755 FAKE
No. Transactions2
Used XPUB Addresses2
XPUB Addresses with Balance
AddressBalanceTxsPath
2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu1186.419755 FAKE1m/49'/1'/33'/1/3

Transactions

`, + `Trezor Fake Coin Explorer

XPUB

upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q

1186.419755 FAKE

Confirmed
Total Received1186.41975501 FAKE
Total Sent0.00000001 FAKE
Final Balance1186.419755 FAKE
No. Transactions2
Used XPUB Addresses2
XPUB Addresses with Balance
AddressBalanceTxsPath
2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu1186.419755 FAKE1m/49'/1'/33'/1/3

Transactions

`, }, }, { @@ -379,7 +379,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

XPUB

tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej

0 FAKE

Confirmed
Total Received0 FAKE
Total Sent0 FAKE
Final Balance0 FAKE
No. Transactions0
Used XPUB Addresses0
XPUB Addresses with Balance
No addresses
`, + `Trezor Fake Coin Explorer

XPUB

tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej

0 FAKE

Confirmed
Total Received0 FAKE
Total Sent0 FAKE
Final Balance0 FAKE
No. Transactions0
Used XPUB Addresses0
XPUB Addresses with Balance
No addresses
`, }, }, { @@ -388,7 +388,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Error

No matching records found for '1234'

`, + `Trezor Fake Coin Explorer

Error

No matching records found for '1234'

`, }, }, { @@ -397,7 +397,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Send Raw Transaction

`, + `Trezor Fake Coin Explorer

Send Raw Transaction

`, }, }, { @@ -406,7 +406,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Send Raw Transaction

Invalid data
`, + `Trezor Fake Coin Explorer

Send Raw Transaction

Invalid data
`, }, }, { diff --git a/server/websocket.go b/server/websocket.go index 837e2bc01a..4d17b4323c 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -538,7 +538,7 @@ func (s *WebsocketServer) getAccountInfo(req *accountInfoReq) (res *api.Address, if req.PageSize == 0 { req.PageSize = txsOnPage } - a, err := s.api.GetXpubAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, req.Gap) + a, err := s.api.GetXpubAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, req.Gap, strings.ToLower(req.SecondaryCurrency)) if err != nil { return s.api.GetAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, strings.ToLower(req.SecondaryCurrency)) } diff --git a/static/css/main.css b/static/css/main.css index 2616a29c48..18c1fe349a 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -15,7 +15,7 @@ body { body { min-height: 100%; margin: 0; - background: linear-gradient(to bottom, #f6f6f6 300px, #e5e5e5 0), #e5e5e5; + background: linear-gradient(to bottom, #f6f6f6 360px, #e5e5e5 0), #e5e5e5; background-repeat: no-repeat; } @@ -588,6 +588,16 @@ span.btn-paging:hover { @media (max-width: 768px) { body { font-size: 0.8rem; + background: linear-gradient(to bottom, #f6f6f6 500px, #e5e5e5 0), #e5e5e5; + } + + .container { + padding-left: 2px; + padding-right: 2px; + } + + .accordion-body { + padding: var(--bs-accordion-body-padding-y) 0; } .octicon { @@ -595,6 +605,10 @@ span.btn-paging:hover { margin-top: -2px; } + .unconfirmed { + padding: 0.1rem 0.8rem; + } + .btn { --bs-btn-font-size: 0.8rem; } diff --git a/static/templates/address.html b/static/templates/address.html index e1cc495f30..9c6f4c3afa 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -1,12 +1,19 @@ {{define "specific"}}{{$addr := .Address}}{{$data := .}} -
+

{{if $addr.ContractInfo}}Contract {{$addr.ContractInfo.Name}}{{if $addr.ContractInfo.Symbol}} ({{$addr.ContractInfo.Symbol}}){{end}}{{else}}Address {{addressAlias $addr.AddrStr $data}}{{end}}

{{$addr.AddrStr}}
-

- {{formattedAmountSpan $addr.BalanceSat 0 $data.CoinShortcut $data "copyable"}} - {{if $addr.TotalFiatValue}}{{summaryValuesSpan $addr.TotalBaseValue $addr.TotalFiatValue $data}}{{end}} +

+
{{formattedAmountSpan $addr.BalanceSat 0 $data.CoinShortcut $data "copyable"}}
+ {{if $addr.FiatValue}}
{{summaryValuesSpan 0 $addr.FiatValue $data}}
{{end}}

+ {{if gt $addr.TotalFiatValue $addr.FiatValue}} +
Including Tokens
+

+
{{summaryValuesSpan $addr.TotalBaseValue 0 $data}}
+
{{summaryValuesSpan 0 $addr.TotalFiatValue $data}}
+

+ {{end}}
@@ -121,7 +128,7 @@
{{summaryValuesSpa Contract Quantity Value - Transfers + Transfers# {{range $t := $addr.Tokens}} {{if eq $t.Type "ERC20"}} @@ -157,7 +164,7 @@
ERC721 Tokens {{toke Contract Tokens - Transfers + Transfers# {{range $t := $addr.Tokens}} {{if eq $t.Type "ERC721"}} @@ -194,7 +201,7 @@
ERC1155 Tokens {{tok Contract Tokens - Transfers + Transfers# {{range $t := $addr.Tokens}} {{if eq $t.Type "ERC1155"}} diff --git a/static/templates/base.html b/static/templates/base.html index 17a933f19c..723488d13a 100644 --- a/static/templates/base.html +++ b/static/templates/base.html @@ -82,7 +82,7 @@ Send Transaction - Don't have a Trezor? Get one! + Don't have a Trezor? Get one!
diff --git a/static/templates/index.html b/static/templates/index.html index 8e8922f676..305c3f01d8 100644 --- a/static/templates/index.html +++ b/static/templates/index.html @@ -48,7 +48,7 @@

{{$bb.MempoolSize}}{{else}}{{formatInt $bb.MempoolSize}}{{end}} + {{if .InternalExplorer}}{{formatInt $bb.MempoolSize}}{{else}}{{formatInt $bb.MempoolSize}}{{end}} {{if $bb.HasFiatRates}} diff --git a/static/templates/tx.html b/static/templates/tx.html index 696de7b64d..6eab41cd10 100644 --- a/static/templates/tx.html +++ b/static/templates/tx.html @@ -111,8 +111,8 @@
{{if $tx.EthereumSpecific.ParsedData.Name}}{{$tx.EthereumSpecif
-
{{$tx.EthereumSpecific.Data}}
-
{{$tx.EthereumSpecific.ParsedData.Function}}
+
{{$tx.EthereumSpecific.Data}}
+
{{$tx.EthereumSpecific.ParsedData.Function}}
{{if $tx.EthereumSpecific.ParsedData.Params}}
diff --git a/static/templates/xpub.html b/static/templates/xpub.html index e607620287..edff44740e 100644 --- a/static/templates/xpub.html +++ b/static/templates/xpub.html @@ -3,7 +3,10 @@

XPUB

{{$addr.AddrStr}}
-

{{amountSpan $addr.BalanceSat $data "copyable"}}

+

+
{{formattedAmountSpan $addr.BalanceSat 0 $data.CoinShortcut $data "copyable"}}
+ {{if $addr.FiatValue}}
{{summaryValuesSpan 0 $addr.FiatValue $data}}
{{end}} +

@@ -60,7 +63,7 @@

{{amountSpan $addr.BalanceSat $data "copyable"}}

- + {{end}} {{else}} From 54f13daad340550356fa26edbdfcdf1b8f23a32f Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 5 Dec 2022 10:01:50 +0100 Subject: [PATCH 105/530] Trim spaces from ETH contract name and symbol --- bchain/coins/eth/contract.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go index 82c89908a5..c56f688d3c 100644 --- a/bchain/coins/eth/contract.go +++ b/bchain/coins/eth/contract.go @@ -298,7 +298,7 @@ func (b *EthereumRPC) fetchContractInfo(address string) (*bchain.ContractInfo, e return nil, nil // return nil, errors.Annotatef(err, "erc20NameSignature %v", address) } - name := parseSimpleStringProperty(data) + name := strings.TrimSpace(parseSimpleStringProperty(data)) if name != "" { data, err = b.ethCall(contractSymbolSignature, address) if err != nil { @@ -306,7 +306,7 @@ func (b *EthereumRPC) fetchContractInfo(address string) (*bchain.ContractInfo, e return nil, nil // return nil, errors.Annotatef(err, "erc20SymbolSignature %v", address) } - symbol := parseSimpleStringProperty(data) + symbol := strings.TrimSpace(parseSimpleStringProperty(data)) data, _ = b.ethCall(contractDecimalsSignature, address) // if err != nil { // glog.Warning(errors.Annotatef(err, "Contract DecimalsSignature %v", address)) From ea0391e03fe49bc3ba67e1f22c54d7743d799acf Mon Sep 17 00:00:00 2001 From: vdovhanych Date: Wed, 30 Nov 2022 22:38:20 +0100 Subject: [PATCH 106/530] chore: update about text --- api/embed/about | 2 +- server/public_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/embed/about b/api/embed/about index 71c44dc9b0..79156218b0 100644 --- a/api/embed/about +++ b/api/embed/about @@ -1 +1 @@ -Blockbook - blockchain indexer for Trezor wallet https://trezor.io/. Do not use for any other purpose. +Blockbook - blockchain indexer for Trezor Suite https://trezor.io/trezor-suite. Do not use for any other purpose. diff --git a/server/public_test.go b/server/public_test.go index ba9216858b..023855852b 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -325,7 +325,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { body: []string{ `Trezor Fake Coin Explorer

Application status

Synchronization with backend is disabled, the state of index is not up to date.

{{amountSpan $t.BalanceSat $data "copyable"}} {{formatInt $t.Transfers}} {{$t.Path}}
`, - `

Blockbook

CoinFakecoin
Host
Version / Commit / Buildunknown / unknown / unknown
Synchronized
true
Last Block225494
Last Block Update`, `
Mempool in Sync
false
Last Mempool Update
Transactions in Mempool0
Size On Disk

Backend

Chainfakecoin
Version001001
Subversion/Fakecoin:0.0.1/
Last Block2
Difficulty
Blockbook - blockchain indexer for Trezor wallet https://trezor.io/. Do not use for any other purpose.
`, + `

Backend

Chainfakecoin
Version001001
Subversion/Fakecoin:0.0.1/
Last Block2
Difficulty

Blockbook - blockchain indexer for Trezor Suite https://trezor.io/trezor-suite. Do not use for any other purpose.
`, }, }, { @@ -924,7 +924,7 @@ func socketioTestsBitcoinType(t *testing.T, ts *httptest.Server) { { name: "socketio getInfo", req: socketioReq{"getInfo", []interface{}{}}, - want: `{"result":{"blocks":225494,"testnet":true,"network":"fakecoin","subversion":"/Fakecoin:0.0.1/","coin_name":"Fakecoin","about":"Blockbook - blockchain indexer for Trezor wallet https://trezor.io/. Do not use for any other purpose."}}`, + want: `{"result":{"blocks":225494,"testnet":true,"network":"fakecoin","subversion":"/Fakecoin:0.0.1/","coin_name":"Fakecoin","about":"Blockbook - blockchain indexer for Trezor Suite https://trezor.io/trezor-suite. Do not use for any other purpose."}}`, }, { name: "socketio estimateFee", From aebc1c3495d41b8644d36a70720036c608dad2af Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 10 Dec 2022 00:22:08 +0100 Subject: [PATCH 107/530] Add secondary currency picker to explorer --- blockbook.go | 19 +-- configs/coins/bcash.json | 1 + configs/coins/bgold.json | 1 + configs/coins/bitcoin.json | 1 + configs/coins/bitcore.json | 1 + configs/coins/dash.json | 1 + configs/coins/digibyte.json | 1 + configs/coins/dogecoin.json | 1 + configs/coins/ecash.json | 1 + configs/coins/ethereum-classic.json | 1 + configs/coins/ethereum.json | 1 + configs/coins/ethereum_archive.json | 1 + configs/coins/fujicoin.json | 7 +- configs/coins/groestlcoin.json | 1 + configs/coins/groestlcoin_regtest.json | 10 +- configs/coins/groestlcoin_signet.json | 10 +- configs/coins/groestlcoin_testnet.json | 10 +- configs/coins/litecoin.json | 1 + configs/coins/monacoin.json | 1 + configs/coins/namecoin.json | 1 + configs/coins/omotenashicoin.json | 1 + configs/coins/omotenashicoin_testnet.json | 130 ++++++++++---------- configs/coins/trezarcoin.json | 1 + configs/coins/vertcoin.json | 1 + configs/coins/zcash.json | 1 + fiat/coingecko.go | 59 +++++---- fiat/fiat_rates.go | 4 +- fiat/fiat_rates_test.go | 2 +- server/public.go | 24 +++- server/public_ethereumtype_test.go | 6 +- static/css/main.css | 44 +++++-- static/templates/base.html | 6 + static/templates/index.html | 1 + static/templates/txdetail_ethereumtype.html | 12 +- 34 files changed, 212 insertions(+), 151 deletions(-) diff --git a/blockbook.go b/blockbook.go index b95c26da47..aac9696498 100644 --- a/blockbook.go +++ b/blockbook.go @@ -677,29 +677,30 @@ func computeFeeStats(stopCompute chan os.Signal, blockFrom, blockTo int, db *db. return err } -func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, configfile string) { - data, err := ioutil.ReadFile(configfile) +func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, configFile string) { + data, err := ioutil.ReadFile(configFile) if err != nil { - glog.Errorf("Error reading file %v, %v", configfile, err) + glog.Errorf("Error reading file %v, %v", configFile, err) return } var config struct { - FiatRates string `json:"fiat_rates"` - FiatRatesParams string `json:"fiat_rates_params"` - FourByteSignatures string `json:"fourByteSignatures"` + FiatRates string `json:"fiat_rates"` + FiatRatesParams string `json:"fiat_rates_params"` + FiatRatesVsCurrencies string `json:"fiat_rates_vs_currencies"` + FourByteSignatures string `json:"fourByteSignatures"` } err = json.Unmarshal(data, &config) if err != nil { - glog.Errorf("Error parsing config file %v, %v", configfile, err) + glog.Errorf("Error parsing config file %v, %v", configFile, err) return } if config.FiatRates == "" || config.FiatRatesParams == "" { - glog.Infof("FiatRates config (%v) is empty, not downloading fiat rates", configfile) + glog.Infof("FiatRates config (%v) is empty, not downloading fiat rates", configFile) } else { - fiatRates, err := fiat.NewFiatRatesDownloader(db, config.FiatRates, config.FiatRatesParams, onNewFiatRatesTicker) + fiatRates, err := fiat.NewFiatRatesDownloader(db, config.FiatRates, config.FiatRatesParams, config.FiatRatesVsCurrencies, onNewFiatRatesTicker) if err != nil { glog.Errorf("NewFiatRatesDownloader Init error: %v", err) } else { diff --git a/configs/coins/bcash.json b/configs/coins/bcash.json index ac048c7073..537c042561 100644 --- a/configs/coins/bcash.json +++ b/configs/coins/bcash.json @@ -56,6 +56,7 @@ "slip44": 145, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin-cash\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/bgold.json b/configs/coins/bgold.json index cdd0384278..e5afbb5fcf 100644 --- a/configs/coins/bgold.json +++ b/configs/coins/bgold.json @@ -252,6 +252,7 @@ "slip44": 156, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin-gold\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index d1ad69ac29..996b5f1e53 100644 --- a/configs/coins/bitcoin.json +++ b/configs/coins/bitcoin.json @@ -66,6 +66,7 @@ "alternative_estimate_fee": "whatthefee-disabled", "alternative_estimate_fee_params": "{\"url\": \"https://whatthefee.io/data.json\", \"periodSeconds\": 60}", "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/bitcore.json b/configs/coins/bitcore.json index a835b65e27..193ee7d2c0 100644 --- a/configs/coins/bitcore.json +++ b/configs/coins/bitcore.json @@ -60,6 +60,7 @@ "block_addresses_to_keep": 300, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcore\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/dash.json b/configs/coins/dash.json index 2e955f0269..9b4dc6d2a7 100644 --- a/configs/coins/dash.json +++ b/configs/coins/dash.json @@ -58,6 +58,7 @@ "slip44": 5, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"dash\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/digibyte.json b/configs/coins/digibyte.json index f82fc3e52e..0c66750f39 100644 --- a/configs/coins/digibyte.json +++ b/configs/coins/digibyte.json @@ -59,6 +59,7 @@ "slip44": 20, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"digibyte\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/dogecoin.json b/configs/coins/dogecoin.json index 0ad5a87ae3..5b8248422e 100644 --- a/configs/coins/dogecoin.json +++ b/configs/coins/dogecoin.json @@ -67,6 +67,7 @@ "slip44": 3, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"dogecoin\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/ecash.json b/configs/coins/ecash.json index e9f967b548..562bb01bdd 100644 --- a/configs/coins/ecash.json +++ b/configs/coins/ecash.json @@ -60,6 +60,7 @@ "slip44": 899, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ecash\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/ethereum-classic.json b/configs/coins/ethereum-classic.json index 6dd3c561fd..456fe9fcf9 100644 --- a/configs/coins/ethereum-classic.json +++ b/configs/coins/ethereum-classic.json @@ -54,6 +54,7 @@ "mempoolTxTimeoutHours": 48, "queryBackendOnMempoolResync": true, "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum-classic\", \"periodSeconds\": 900}", "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 120cc9ee69..395868aee0 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -61,6 +61,7 @@ "mempoolTxTimeoutHours": 48, "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" } } diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 1d93dd3680..3c73486abe 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -63,6 +63,7 @@ "processInternalTransactions": true, "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } diff --git a/configs/coins/fujicoin.json b/configs/coins/fujicoin.json index ce2d086d14..c3188e660d 100644 --- a/configs/coins/fujicoin.json +++ b/configs/coins/fujicoin.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "8aa699f3fbd6681391b90f744a25155d21a94f5ca63d6cc3b85172f3aca6e2a0", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/fujicoin-qt" - ], + "exclude_files": ["bin/fujicoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/fujicoind -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": "", @@ -61,6 +59,7 @@ "slip44": 75, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"fujicoin\", \"periodSeconds\": 600}" } } @@ -69,4 +68,4 @@ "package_maintainer": "Motty", "package_maintainer_email": "fujicoin@gmail.com" } -} \ No newline at end of file +} diff --git a/configs/coins/groestlcoin.json b/configs/coins/groestlcoin.json index a11f913a64..5fdfd42bc4 100644 --- a/configs/coins/groestlcoin.json +++ b/configs/coins/groestlcoin.json @@ -60,6 +60,7 @@ "slip44": 17, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"groestlcoin\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/groestlcoin_regtest.json b/configs/coins/groestlcoin_regtest.json index e34d9736c1..c85b30bf82 100644 --- a/configs/coins/groestlcoin_regtest.json +++ b/configs/coins/groestlcoin_regtest.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "4b69743190e2697d7b7772bf6f63cde595d590ff6664abf15a7201dab2a6098b", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/groestlcoin-qt" - ], + "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -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/regtest/*.log", "postinst_script_template": "", @@ -65,11 +63,7 @@ "xpub_magic": 70617039, "xpub_magic_segwit_p2sh": 71979618, "xpub_magic_segwit_native": 73342198, - "slip44": 1, - "additional_params": { - "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"groestlcoin\", \"periodSeconds\": 60}" - } + "slip44": 1 } }, "meta": { diff --git a/configs/coins/groestlcoin_signet.json b/configs/coins/groestlcoin_signet.json index 9919b6ea08..7859a6908b 100644 --- a/configs/coins/groestlcoin_signet.json +++ b/configs/coins/groestlcoin_signet.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "4b69743190e2697d7b7772bf6f63cde595d590ff6664abf15a7201dab2a6098b", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/groestlcoin-qt" - ], + "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -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/signet/*.log", "postinst_script_template": "", @@ -59,11 +57,7 @@ "xpub_magic": 70617039, "xpub_magic_segwit_p2sh": 71979618, "xpub_magic_segwit_native": 73342198, - "slip44": 1, - "additional_params": { - "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"groestlcoin\", \"periodSeconds\": 60}" - } + "slip44": 1 } }, "meta": { diff --git a/configs/coins/groestlcoin_testnet.json b/configs/coins/groestlcoin_testnet.json index a4d2139d77..05a67c2548 100644 --- a/configs/coins/groestlcoin_testnet.json +++ b/configs/coins/groestlcoin_testnet.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "4b69743190e2697d7b7772bf6f63cde595d590ff6664abf15a7201dab2a6098b", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/groestlcoin-qt" - ], + "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -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/testnet3/*.log", "postinst_script_template": "", @@ -59,11 +57,7 @@ "xpub_magic": 70617039, "xpub_magic_segwit_p2sh": 71979618, "xpub_magic_segwit_native": 73342198, - "slip44": 1, - "additional_params": { - "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"groestlcoin\", \"periodSeconds\": 60}" - } + "slip44": 1 } }, "meta": { diff --git a/configs/coins/litecoin.json b/configs/coins/litecoin.json index 7483d26473..f965e4e813 100644 --- a/configs/coins/litecoin.json +++ b/configs/coins/litecoin.json @@ -65,6 +65,7 @@ "slip44": 2, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"litecoin\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/monacoin.json b/configs/coins/monacoin.json index 6b274e9a09..7cf2715ac8 100644 --- a/configs/coins/monacoin.json +++ b/configs/coins/monacoin.json @@ -59,6 +59,7 @@ "slip44": 22, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"monacoin\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/namecoin.json b/configs/coins/namecoin.json index aff9fc08b4..d9675283c5 100644 --- a/configs/coins/namecoin.json +++ b/configs/coins/namecoin.json @@ -62,6 +62,7 @@ "slip44": 7, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"namecoin\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/omotenashicoin.json b/configs/coins/omotenashicoin.json index 52a9a7f408..4a27eab55e 100644 --- a/configs/coins/omotenashicoin.json +++ b/configs/coins/omotenashicoin.json @@ -57,6 +57,7 @@ "slip44": 341, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"omotenashicoin\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/omotenashicoin_testnet.json b/configs/coins/omotenashicoin_testnet.json index bd14828fa8..993d3abe34 100644 --- a/configs/coins/omotenashicoin_testnet.json +++ b/configs/coins/omotenashicoin_testnet.json @@ -1,70 +1,64 @@ { - "coin": { - "name": "Omotenashicoin Testnet", - "shortcut": "tMTNS", - "label": "Omotenashicoin Testnet", - "alias": "omotenashicoin_testnet" - }, - "ports": { - "blockbook_internal": 19089, - "blockbook_public": 19189, - "backend_rpc": 18089, - "backend_message_queue": 48389 - }, - "ipc": { - "rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}", - "rpc_user": "rpc", - "rpc_pass": "mtnsrpc", - "rpc_timeout": 25, - "message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}" - }, - "backend": { - "package_name": "backend-mtns-testnet", - "package_revision": "satoshilabs-1", - "system_user": "mtns", - "version": "1.7.3", - "binary_url": "https://github.com/omotenashicoin-project/OmotenashiCoin-HDwalletbinaries/raw/master/stable/omotenashicoin-x86_64-linux-gnu.tar.gz", - "verification_type": "", - "verification_source": "", - "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/omotenashicoin-qt" - ], - "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/omotenashicoind -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/testnet4/*.log", - "postinst_script_template": "", - "service_type": "forking", - "service_additional_params_template": "", - "protect_memory": true, - "mainnet": false, - "server_config_file": "bitcoin_like.conf", - "client_config_file": "bitcoin_like_client.conf", - "additional_params": { - "whitelist": "127.0.0.1" - } - }, - "blockbook": { - "package_name": "blockbook-mtns-testnet", - "system_user": "blockbook-mtns", - "internal_binding_template": ":{{.Ports.BlockbookInternal}}", - "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "", - "additional_params": "", - "block_chain": { - "parse": true, - "mempool_workers": 8, - "mempool_sub_workers": 2, - "block_addresses_to_keep": 300, - "xpub_magic": 70544129, - "slip44": 1, - "additional_params": { - "fiat_rates": "coingecko", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"omotenashicoin\", \"periodSeconds\": 60}" - } - } - }, - "meta": { - "package_maintainer": "omotenashicoin dev", - "package_maintainer_email": "git@omotenashicoin.site" - } + "coin": { + "name": "Omotenashicoin Testnet", + "shortcut": "tMTNS", + "label": "Omotenashicoin Testnet", + "alias": "omotenashicoin_testnet" + }, + "ports": { + "blockbook_internal": 19089, + "blockbook_public": 19189, + "backend_rpc": 18089, + "backend_message_queue": 48389 + }, + "ipc": { + "rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_user": "rpc", + "rpc_pass": "mtnsrpc", + "rpc_timeout": 25, + "message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}" + }, + "backend": { + "package_name": "backend-mtns-testnet", + "package_revision": "satoshilabs-1", + "system_user": "mtns", + "version": "1.7.3", + "binary_url": "https://github.com/omotenashicoin-project/OmotenashiCoin-HDwalletbinaries/raw/master/stable/omotenashicoin-x86_64-linux-gnu.tar.gz", + "verification_type": "", + "verification_source": "", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": ["bin/omotenashicoin-qt"], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/omotenashicoind -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/testnet4/*.log", + "postinst_script_template": "", + "service_type": "forking", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "bitcoin_like.conf", + "client_config_file": "bitcoin_like_client.conf", + "additional_params": { + "whitelist": "127.0.0.1" + } + }, + "blockbook": { + "package_name": "blockbook-mtns-testnet", + "system_user": "blockbook-mtns", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "xpub_magic": 70544129, + "slip44": 1 + } + }, + "meta": { + "package_maintainer": "omotenashicoin dev", + "package_maintainer_email": "git@omotenashicoin.site" + } } diff --git a/configs/coins/trezarcoin.json b/configs/coins/trezarcoin.json index e20cccf7e1..83fe5e5452 100644 --- a/configs/coins/trezarcoin.json +++ b/configs/coins/trezarcoin.json @@ -57,6 +57,7 @@ "slip44": 232, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"trezarcoin\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/vertcoin.json b/configs/coins/vertcoin.json index 74726839dc..23a3436512 100644 --- a/configs/coins/vertcoin.json +++ b/configs/coins/vertcoin.json @@ -59,6 +59,7 @@ "slip44": 28, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"vertcoin\", \"periodSeconds\": 900}" } } diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index eae0ecd92a..21ad17b2bb 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -57,6 +57,7 @@ "slip44": 133, "additional_params": { "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"zcash\", \"periodSeconds\": 900}" } } diff --git a/fiat/coingecko.go b/fiat/coingecko.go index a1f4c47bdd..aa72835ae1 100644 --- a/fiat/coingecko.go +++ b/fiat/coingecko.go @@ -18,17 +18,18 @@ import ( // Coingecko is a structure that implements RatesDownloaderInterface type Coingecko struct { - url string - coin string - platformIdentifier string - platformVsCurrency string - httpTimeoutSeconds time.Duration - throttlingDelay time.Duration - timeFormat string - httpClient *http.Client - db *db.RocksDB - updatingCurrent bool - updatingTokens bool + url string + coin string + platformIdentifier string + platformVsCurrency string + allowedVsCurrencies map[string]struct{} + httpTimeout time.Duration + throttlingDelay time.Duration + timeFormat string + httpClient *http.Client + db *db.RocksDB + updatingCurrent bool + updatingTokens bool } // simpleSupportedVSCurrencies https://api.coingecko.com/api/v3/simple/supported_vs_currencies @@ -50,21 +51,28 @@ type marketChartPrices struct { } // NewCoinGeckoDownloader creates a coingecko structure that implements the RatesDownloaderInterface -func NewCoinGeckoDownloader(db *db.RocksDB, url string, coin string, platformIdentifier string, platformVsCurrency string, timeFormat string, throttleDown bool) RatesDownloaderInterface { +func NewCoinGeckoDownloader(db *db.RocksDB, url string, coin string, platformIdentifier string, platformVsCurrency string, allowedVsCurrencies string, timeFormat string, throttleDown bool) RatesDownloaderInterface { var throttlingDelayMs int if throttleDown { throttlingDelayMs = 100 } - httpTimeoutSeconds := 15 * time.Second + httpTimeout := 15 * time.Second + allowedVsCurrenciesMap := make(map[string]struct{}) + if len(allowedVsCurrencies) > 0 { + for _, c := range strings.Split(strings.ToLower(allowedVsCurrencies), ",") { + allowedVsCurrenciesMap[c] = struct{}{} + } + } return &Coingecko{ - url: url, - coin: coin, - platformIdentifier: platformIdentifier, - platformVsCurrency: platformVsCurrency, - httpTimeoutSeconds: httpTimeoutSeconds, - timeFormat: timeFormat, + url: url, + coin: coin, + platformIdentifier: platformIdentifier, + platformVsCurrency: platformVsCurrency, + allowedVsCurrencies: allowedVsCurrenciesMap, + httpTimeout: httpTimeout, + timeFormat: timeFormat, httpClient: &http.Client{ - Timeout: httpTimeoutSeconds, + Timeout: httpTimeout, }, db: db, throttlingDelay: time.Duration(throttlingDelayMs) * time.Millisecond, @@ -123,7 +131,16 @@ func (cg *Coingecko) simpleSupportedVSCurrencies() (simpleSupportedVSCurrencies, if err != nil { return nil, err } - return data, nil + if len(cg.allowedVsCurrencies) == 0 { + return data, nil + } + filtered := make([]string, 0, len(cg.allowedVsCurrencies)) + for _, c := range data { + if _, found := cg.allowedVsCurrencies[c]; found { + filtered = append(filtered, c) + } + } + return filtered, nil } // SimplePrice /simple/price Multiple ID and Currency (ids, vs_currencies) diff --git a/fiat/fiat_rates.go b/fiat/fiat_rates.go index d78db9b887..ee92ea9ca7 100644 --- a/fiat/fiat_rates.go +++ b/fiat/fiat_rates.go @@ -33,7 +33,7 @@ type RatesDownloader struct { } // NewFiatRatesDownloader initializes the downloader for FiatRates API. -func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, callback OnNewFiatRatesTicker) (*RatesDownloader, error) { +func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, allowedVsCurrencies string, callback OnNewFiatRatesTicker) (*RatesDownloader, error) { var rd = &RatesDownloader{} type fiatRatesParams struct { URL string `json:"url"` @@ -65,7 +65,7 @@ func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, callb // a small hack - in tests the callback is not used, therefore there is no delay slowing the test throttle = false } - rd.downloader = NewCoinGeckoDownloader(db, rdParams.URL, rdParams.Coin, rdParams.PlatformIdentifier, rdParams.PlatformVsCurrency, rd.timeFormat, throttle) + rd.downloader = NewCoinGeckoDownloader(db, rdParams.URL, rdParams.Coin, rdParams.PlatformIdentifier, rdParams.PlatformVsCurrency, allowedVsCurrencies, rd.timeFormat, throttle) if is != nil { is.HasFiatRates = true is.HasTokenFiatRates = rd.downloadTokens diff --git a/fiat/fiat_rates_test.go b/fiat/fiat_rates_test.go index d1fdd9832e..417c960be1 100644 --- a/fiat/fiat_rates_test.go +++ b/fiat/fiat_rates_test.go @@ -148,7 +148,7 @@ func TestFiatRates(t *testing.T) { t.Fatalf("Error parsing FiatRates config - empty parameter") return } - fiatRates, err := NewFiatRatesDownloader(d, config.FiatRates, config.FiatRatesParams, nil) + fiatRates, err := NewFiatRatesDownloader(d, config.FiatRates, config.FiatRatesParams, "", nil) if err != nil { t.Fatalf("FiatRates init error: %v", err) } diff --git a/server/public.go b/server/public.go index 8147db9ce9..28481087bf 100644 --- a/server/public.go +++ b/server/public.go @@ -15,6 +15,7 @@ import ( "regexp" "runtime" "runtime/debug" + "sort" "strconv" "strings" "time" @@ -373,6 +374,11 @@ func (s *PublicServer) newTemplateData(r *http.Request) *TemplateData { t.SecondaryCoin = strings.ToUpper(secondary) t.CurrentSecondaryCoinRate = float64(ticker.Rates[secondary]) t.CurrentTicker = ticker + t.SecondaryCurrencies = make([]string, 0, len(ticker.Rates)) + for k := range ticker.Rates { + t.SecondaryCurrencies = append(t.SecondaryCurrencies, strings.ToUpper(k)) + } + sort.Strings(t.SecondaryCurrencies) // sort to get deterministic results t.UseSecondaryCoin, _ = strconv.ParseBool(r.URL.Query().Get("use_secondary")) if !t.UseSecondaryCoin { t.UseSecondaryCoin = cookieUseSecondary @@ -501,6 +507,7 @@ type TemplateData struct { UseSecondaryCoin bool CurrentSecondaryCoinRate float64 CurrentTicker *common.CurrencyRatesTicker + SecondaryCurrencies []string TxDate string TxSecondaryCoinRate float64 TxTicker *common.CurrencyRatesTicker @@ -724,6 +731,13 @@ func appendAmountWrapperSpan(rv *strings.Builder, primary, symbol, classes strin rv.WriteString(`">`) } +func formatSecondaryAmount(a float64, td *TemplateData) string { + if td.SecondaryCoin == "BTC" || td.SecondaryCoin == "ETH" { + return strconv.FormatFloat(a, 'f', 6, 64) + } + return strconv.FormatFloat(a, 'f', 2, 64) +} + func (s *PublicServer) amountSpan(a *api.Amount, td *TemplateData, classes string) template.HTML { primary := s.formatAmount(a) var rv strings.Builder @@ -732,7 +746,7 @@ func (s *PublicServer) amountSpan(a *api.Amount, td *TemplateData, classes strin if td.SecondaryCoin != "" { p, err := strconv.ParseFloat(primary, 64) if err == nil { - currentSecondary := strconv.FormatFloat(p*td.CurrentSecondaryCoinRate, 'f', 2, 64) + currentSecondary := formatSecondaryAmount(p*td.CurrentSecondaryCoinRate, td) txSecondary := "" // if tx is specified, compute secondary amount is at the time of tx and amount with current rate is returned with class "csec-amt" if td.Tx != nil { @@ -748,7 +762,7 @@ func (s *PublicServer) amountSpan(a *api.Amount, td *TemplateData, classes strin } } if td.TxSecondaryCoinRate != 0 { - txSecondary = strconv.FormatFloat(p*td.TxSecondaryCoinRate, 'f', 2, 64) + txSecondary = formatSecondaryAmount(p*td.TxSecondaryCoinRate, td) } } if txSecondary != "" { @@ -798,13 +812,13 @@ func (s *PublicServer) tokenAmountSpan(t *api.TokenTransfer, td *TemplateData, c if found { base := p * baseRate currentBase = strconv.FormatFloat(base, 'f', 6, 64) - currentSecondary = strconv.FormatFloat(base*td.CurrentSecondaryCoinRate, 'f', 2, 64) + currentSecondary = formatSecondaryAmount(base*td.CurrentSecondaryCoinRate, td) } baseRate, found = s.api.GetContractBaseRate(td.TxTicker, t.Contract, td.Tx.Blocktime) if found { base := p * baseRate txBase = strconv.FormatFloat(base, 'f', 6, 64) - txSecondary = strconv.FormatFloat(base*td.TxSecondaryCoinRate, 'f', 2, 64) + txSecondary = formatSecondaryAmount(base*td.TxSecondaryCoinRate, td) } } if txBase != "" { @@ -843,7 +857,7 @@ func (s *PublicServer) formattedAmountSpan(a *api.Amount, d int, symbol string, func (s *PublicServer) summaryValuesSpan(baseValue float64, secondaryValue float64, td *TemplateData) template.HTML { var rv strings.Builder if secondaryValue > 0 { - appendAmountSpan(&rv, "", strconv.FormatFloat(secondaryValue, 'f', 2, 64), td.SecondaryCoin, "") + appendAmountSpan(&rv, "", formatSecondaryAmount(secondaryValue, td), td.SecondaryCoin, "") if baseValue > 0 && s.chainParser.GetChainType() == bchain.ChainEthereumType { rv.WriteString(`(`) appendAmountSpan(&rv, "", strconv.FormatFloat(baseValue, 'f', 6, 64), td.CoinShortcut, "") diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 92731f90f1..6d87cf866c 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -24,7 +24,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE

Confirmed
Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers#
Contract 130.000000001000123013 S131
Contract 740.001000123074 S741
ContractTokensTransfers#
Contract 20511

Transactions

ERC721 Token Transfers
`, + `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE

Confirmed
Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers#
Contract 130.000000001000123013 S131
Contract 740.001000123074 S741
ContractTokensTransfers#
Contract 20511

Transactions

ERC721 Token Transfers
`, }, }, { @@ -33,7 +33,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE

Confirmed
Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers#
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, + `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE

Confirmed
Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers#
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, }, }, { @@ -42,7 +42,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE (40 Gwei)
Fees0.002081 FAKE
RBFON
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE (40 Gwei)
Fees0.002081 FAKE
RBFON
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, }, }, { name: "explorerTokenDetail " + dbtestdata.EthAddr7b, diff --git a/static/css/main.css b/static/css/main.css index 18c1fe349a..8af83979c9 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -38,7 +38,7 @@ select { } #header { - position: absolute; + position: fixed; top: 0; left: 0; width: 100%; @@ -46,10 +46,18 @@ select { padding-bottom: 0; padding-top: 0; background-color: white; - border: 0; + border-bottom: 1px solid #f6f6f6; z-index: 10; } +#header a { + color: var(--bs-navbar-brand-color); +} + +#header a:hover { + color: var(--bs-navbar-brand-hover-color); +} + #header .navbar { --bs-navbar-padding-y: 0.7rem; } @@ -63,6 +71,23 @@ select { min-height: 50px; } +#header .btn.dropdown-toggle { + padding-right: 0; +} + +#header .dropdown-menu { + --bs-dropdown-min-width: 13rem; +} + +#header .dropdown-menu[data-bs-popper] { + left: initial; + right: 0; +} + +#header .dropdown-menu.show { + display: flex; +} + .form-control:focus { outline: 0; box-shadow: none; @@ -77,7 +102,6 @@ select { .badge { vertical-align: middle; - filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.15)); text-transform: uppercase; letter-spacing: 0.15em; --bs-badge-padding-x: 0.8rem; @@ -478,19 +502,18 @@ span.btn-paging:hover { } .txerror { - background-color: #c51f13a0; - color: white !important; + color: #c51f13; } .txerror a, .txerror .txvalue { - color: white; + color: #c51f13; } .txerror .copyable::before, .txerror .copied::before { - /* turn svg stroke to white */ - filter: hue-rotate(180deg) brightness(1000%) contrast(100%); + /* turn svg stroke to red */ + filter: invert(86%) sepia(43%) saturate(732%) hue-rotate(367deg) brightness(84%); } .tx-amt .amt:hover, @@ -619,6 +642,11 @@ span.btn-paging:hover { min-height: 40px; } + #header .dropdown-menu[data-bs-popper] { + left: 0; + right: initial; + } + .trezor-logo { top: 10px; } diff --git a/static/templates/base.html b/static/templates/base.html index 723488d13a..1ecce68e0d 100644 --- a/static/templates/base.html +++ b/static/templates/base.html @@ -48,6 +48,12 @@ + + {{end}} diff --git a/static/templates/index.html b/static/templates/index.html index 305c3f01d8..451c8c9d06 100644 --- a/static/templates/index.html +++ b/static/templates/index.html @@ -143,4 +143,5 @@

{{$bb.About}} +{{if .SecondaryCoin}}Exchange rates provided by Coingecko.{{end}} {{end}} \ No newline at end of file diff --git a/static/templates/txdetail_ethereumtype.html b/static/templates/txdetail_ethereumtype.html index 58646f8a2b..3d2281c1b2 100644 --- a/static/templates/txdetail_ethereumtype.html +++ b/static/templates/txdetail_ethereumtype.html @@ -1,17 +1,17 @@ {{define "txdetail"}}{{$cs := .CoinShortcut}}{{$addr := .AddrStr}}{{$tx := .Tx}}{{$data := .}}
-
-
+
+
{{$tx.Txid}} {{if $tx.Rbf}} RBF{{end}}
{{if $tx.Blocktime}}
{{if $tx.Confirmations}}mined{{else}}first seen{{end}} {{unixTimeSpan $tx.Blocktime}}
{{end}} + {{if eq $tx.EthereumSpecific.Status 0}}
Failed{{if $tx.EthereumSpecific.Error}}{{$tx.EthereumSpecific.Error}}{{end}}
{{end}} {{if $tx.EthereumSpecific.ParsedData}} {{if $tx.EthereumSpecific.ParsedData.Name}}
{{$tx.EthereumSpecific.ParsedData.Name}}{{if $tx.EthereumSpecific.ParsedData.MethodId}} ({{$tx.EthereumSpecific.ParsedData.MethodId}}){{end}}
{{else}} {{if $tx.EthereumSpecific.ParsedData.MethodId}}
{{$tx.EthereumSpecific.ParsedData.MethodId}}
{{end}} {{end}} {{end}} - {{if eq $tx.EthereumSpecific.Status 0}}
Failed{{if $tx.EthereumSpecific.Error}}{{$tx.EthereumSpecific.Error}}{{end}}
{{end}}
@@ -184,12 +184,12 @@ {{end}} @@ -135,7 +135,7 @@
{{summaryValuesSpa {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} {{formattedAmountSpan $t.BalanceSat $t.Decimals $t.Symbol $data "copyable"}} - {{summaryValuesSpan $t.BaseValue $t.FiatValue $data}} + {{summaryValuesSpan $t.BaseValue $t.SecondaryValue $data}} {{formatInt $t.Transfers}} {{end}} diff --git a/static/templates/xpub.html b/static/templates/xpub.html index edff44740e..6b1c40fc01 100644 --- a/static/templates/xpub.html +++ b/static/templates/xpub.html @@ -5,7 +5,7 @@

XPUB

{{$addr.AddrStr}}

{{formattedAmountSpan $addr.BalanceSat 0 $data.CoinShortcut $data "copyable"}}
- {{if $addr.FiatValue}}
{{summaryValuesSpan 0 $addr.FiatValue $data}}
{{end}} + {{if $addr.SecondaryValue}}
{{summaryValuesSpan 0 $addr.SecondaryValue $data}}
{{end}}

From c1256d22e9e34844acfbae5f094365e74da7dc99 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 1 Feb 2023 13:55:44 +0100 Subject: [PATCH 123/530] Docs update v0.4.0 --- docs/api.md | 227 +++++++++++++++++++++++++++++++-------------- docs/rocksdb.md | 239 +++++++++++++++++++++++++++++------------------- 2 files changed, 306 insertions(+), 160 deletions(-) diff --git a/docs/api.md b/docs/api.md index 648b08c05d..c67a4a20f6 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,33 +1,6 @@ # Blockbook API -**Blockbook** provides REST, websocket and socket.io API to the indexed blockchain. - -There are two versions of provided API. - -## Legacy API V1 - -The legacy API is a compatible subset of API provided by **Bitcore Insight**. It supports only Bitcoin-type coins. The details of the REST/socket.io requests can be found in the Insight's documentation. - -### REST API - -``` -GET /api/v1/block-index/ -GET /api/v1/tx/ -GET /api/v1/address/
-GET /api/v1/utxo/
-GET /api/v1/block/ -GET /api/v1/estimatefee/ -GET /api/v1/sendtx/ -POST /api/v1/sendtx/ (hex tx data in request body) -``` - -### Socket.io API - -Socket.io interface is provided at `/socket.io/`. The interface also can be explored using Blockbook Socket.io Test Page found at `/test-socketio.html`. - -The legacy API is provided as is and will not be further developed. - -The legacy API is currently (as of Blockbook v0.4.0) also accessible without the _/v1/_ prefix, however in the future versions the version less access will be removed. +**Blockbook** provides REST and websocket API to the indexed blockchain. ## API V2 @@ -35,7 +8,7 @@ API V2 is the current version of API. It can be used with all coin types that Bl Common principles used in API V2: -- all amounts are transferred as strings, in the lowest denomination (satoshis, wei, ...), without decimal point +- all crypto amounts are transferred as strings, in the lowest denomination (satoshis, wei, ...), without decimal point - empty fields are omitted. Empty field is a string of value _null_ or _""_, a number of value _0_, an object of value _null_ or an array without elements. The reason for this is that the interface serves many different coins which use only subset of the fields. Sometimes this principle can lead to slightly confusing results, for example when transaction version is 0, the field _version_ is omitted. ### REST API @@ -176,57 +149,114 @@ Response for Bitcoin-type coins: } ``` -Response for Ethereum-type coins. There is always only one _vin_, only one _vout_, possibly an array of _tokenTransfers_ and _ethereumSpecific_ part. Missing is _hex_ field: +Response for Ethereum-type coins. Data of the transaction consist of: + +- always only one _vin_, only one _vout_ +- an array of _tokenTransfers_ (ERC20, ERC721 or ERC1155) +- _ethereumSpecific_ data + - _type_ (returned only for contract creation - value `1` and destruction value `2`) + - _status_ (`1` OK, `0` Failure, `-1` pending), potential _error_ message, _gasLimit_, _gasUsed_, _gasPrice_, _nonce_, input _data_ + - parsed input data in the field _parsedData_, if a match with the 4byte directory was found + - internal transfers (type `0` transfer, type `1` contract creation, type `2` contract destruction) +- _addressAliases_ - maps addresses in the transaction to names from contract or ENS. Only addresses with known names are returned. ```javascript { - "txid": "0xb78a36a4a0e7d708d595c3b193cace8f5b420e72e1f595a5387d87de509f0806", + "txid": "0xa6c8ae1f91918d09cf2bd67bbac4c168849e672fd81316fa1d26bb9b4fc0f790", "vin": [ { "n": 0, - "addresses": [ - "0x9c2e011c0ce0d75c2b62b9c5a0ba0a7456593803" - ], + "addresses": ["0xd446089cf19C3D3Eb1743BeF3A852293Fd2C7775"], "isAddress": true } ], "vout": [ { - "value": "0", + "value": "5615959129349132871", "n": 0, - "addresses": [ - "0xc32ae45504ee9482db99cfa21066a59e877bc0e6" - ], + "addresses": ["0xC36442b4a4522E871399CD717aBDD847Ab11FE88"], "isAddress": true } ], - "blockHash": "0x39df7fb0893200e1e78c04f98691637a89b64e7a3edd96c16f2537e2fd56c414", - "blockHeight": 5241585, + "blockHash": "0x10ea8cfecda89d6d864c1d919911f819c9febc2b455b48c9918cee3c6cdc4adb", + "blockHeight": 16529834, "confirmations": 3, - "blockTime": 1553088337, - "value": "0", - "fees": "402501000000000", + "blockTime": 1675204631, + "value": "5615959129349132871", + "fees": "19141662404282012", "tokenTransfers": [ { "type": "ERC20", - "from": "0x9c2e011c0ce0d75c2b62b9c5a0ba0a7456593803", - "to": "0x583cbbb8a8443b38abcc0c956bece47340ea1367", - "token": "0xc32ae45504ee9482db99cfa21066a59e877bc0e6", - "name": "Tangany Test Token", - "symbol": "TATETO", + "from": "0xd446089cf19C3D3Eb1743BeF3A852293Fd2C7775", + "to": "0x3B685307C8611AFb2A9E83EBc8743dc20480716E", + "contract": "0x4E15361FD6b4BB609Fa63C81A2be19d873717870", + "name": "Fantom Token", + "symbol": "FTM", + "decimals": 18, + "value": "15362368338194882707417" + }, + { + "type": "ERC20", + "from": "0xC36442b4a4522E871399CD717aBDD847Ab11FE88", + "to": "0x3B685307C8611AFb2A9E83EBc8743dc20480716E", + "contract": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "name": "Wrapped Ether", + "symbol": "WETH", "decimals": 18, - "value": "133800000" + "value": "5615959129349132871" + }, + { + "type": "ERC721", + "from": "0x0000000000000000000000000000000000000000", + "to": "0xd446089cf19C3D3Eb1743BeF3A852293Fd2C7775", + "contract": "0xC36442b4a4522E871399CD717aBDD847Ab11FE88", + "name": "Uniswap V3 Positions NFT-V1", + "symbol": "UNI-V3-POS", + "decimals": 18, + "value": "428189" } ], "ethereumSpecific": { "status": 1, - "nonce": 2830, - "gasLimit": 36591, - "gasUsed": 36591, - "gasPrice": "11000000000", - "data": "0xa9059cbb000000000000000000000000ba98d6a5" + "nonce": 505, + "gasLimit": 550941, + "gasUsed": 434686, + "gasPrice": "44035608242", + "data": "0xac9650d800000000000000000000", + "parsedData": { + "methodId": "0xfa2b068f", + "name": "Mint", + "function": "mint(address, uint256, uint32, bytes32[], address)", + "params": [ + { + "type": "address", + "values": ["0xa5fD1Da088598e88ba731B0E29AECF0BC2A31F82"] + }, + { "type": "uint256", "values": ["688173296"] }, + { "type": "uint32", "values": ["0"] } + ] + }, + "internalTransfers": [ + { + "type": 0, + "from": "0xC36442b4a4522E871399CD717aBDD847Ab11FE88", + "to": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "value": "5615959129349132871" + } + ] + }, + "addressAliases": { + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": { + "Type": "Contract", + "Alias": "Wrapped Ether" + }, + "0xC36442b4a4522E871399CD717aBDD847Ab11FE88": { + "Type": "Contract", + "Alias": "Uniswap V3 Positions NFT-V1" + } } } + ``` A note about the `blockTime` field: @@ -298,7 +328,7 @@ Example response: Returns balances and transactions of an address. The returned transactions are sorted by block height, newest blocks first. ``` -GET /api/v2/address/
[?page=&pageSize=&from=&to=&details=&contract=] +GET /api/v2/address/
[?page=&pageSize=&from=&to=&details=&contract=&secondary=usd] ``` The optional query parameters: @@ -314,8 +344,9 @@ The optional query parameters: - _txslight_: _tokenBalances_ + list of transaction with limited details (only data from index), subject to _from_, _to_ filter and paging - _txs_: _tokenBalances_ + list of transaction with details, subject to _from_, _to_ filter and paging - _contract_: return only transactions which affect specified contract (applicable only to coins which support contracts) +- _secondary_: specifies secondary (fiat) currency in which the token and total balances are returned in addition to crypto values -Response: +Example response for bitcoin type coin, _details_ set to _txids_: ```javascript { @@ -337,6 +368,39 @@ Response: } ``` +Example response for ethereum type coin, _details_ set to _tokenBalances_ and _secondary_ set to _usd_. The _baseValue_ is value of the token in the base currency (ETH), _secondaryValue_ is value of the token in specified _secondary_ currency: + +```javascript +{ + "address": "0x2df3951b2037bA620C20Ed0B73CCF45Ea473e83B", + "balance": "21004631949601199", + "unconfirmedBalance": "0", + "unconfirmedTxs": 0, + "txs": 5, + "nonTokenTxs": 3, + "nonce": "1", + "tokens": [ + { + "type": "ERC20", + "name": "Tether USD", + "contract": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "transfers": 3, + "symbol": "USDT", + "decimals": 6, + "balance": "4913000000", + "baseValue": 3.104622978658881, + "secondaryValue": 4914.214559070491 + } + ], + "secondaryValue": 33.247601671503574, + "tokensBaseValue": 3.104622978658881, + "tokensSecondaryValue": 4914.214559070491, + "totalBaseValue": 3.125627610608482, + "totalSecondaryValue": 4947.462160741995 +} + +``` + #### Get xpub Returns balances and transactions of an xpub or output descriptor, applicable only for Bitcoin-type coins. @@ -366,7 +430,7 @@ Blockbook supports BIP44, BIP49, BIP84 and BIP86 (Taproot) derivation schemes, u The returned transactions are sorted by block height, newest blocks first. ``` -GET /api/v2/xpub/[?page=&pageSize=&from=&to=&details=&tokens=] +GET /api/v2/xpub/[?page=&pageSize=&from=&to=&details=&tokens=&secondary=eur] ``` The optional query parameters: @@ -384,6 +448,7 @@ The optional query parameters: - _nonzero_: return only addresses with nonzero balance - _used_: return addresses with at least one transaction - _derived_: return all derived addresses +- _secondary_: specifies secondary (fiat) currency in which the balances are returned in addition to crypto values Response: @@ -393,8 +458,8 @@ Response: "totalPages": 1, "itemsOnPage": 1000, "address": "dgub8sbe5Mi8LA4dXB9zPfLZW8arm...9Vjp2HHx91xdDEmWYpmD49fpoUYF", - "balance": "0", - "totalReceived": "3083381250", + "balance": "90000000", + "totalReceived": "3093381250", "totalSent": "3083381250", "unconfirmedBalance": "0", "unconfirmedTxs": 0, @@ -414,8 +479,8 @@ Response: "path": "m/44'/3'/0'/0/0", "transfers": 3, "decimals": 8, - "balance": "0", - "totalReceived": "2803986975", + "balance": "90000000", + "totalReceived": "2903986975", "totalSent": "2803986975" }, { @@ -428,7 +493,8 @@ Response: "totalReceived": "279394275", "totalSent": "279394275" } - ] + ], + "secondaryValue": 21195.47633568 } ``` @@ -611,7 +677,7 @@ or in case of error #### Tickers list -Returns a list of available currency rate tickers for the specified date, along with an actual data timestamp. +Returns a list of available currency rate tickers (secondary currencies) for the specified date, along with an actual data timestamp. ``` GET /api/v2/tickers-list/?timestamp= @@ -696,10 +762,10 @@ Query parameters: The optional query parameters: -- _fiatcurrency_: if specified, the response will contain fiat rate at the time of transaction. If not, all available currencies will be returned. +- _fiatcurrency_: if specified, the response will contain secondary (fiat) rate at the time of transaction. If not, all available currencies will be returned. - _groupBy_: an interval in seconds, to group results by. Default is 3600 seconds. -Example response (fiatcurrency not specified): +Example response (_fiatcurrency_ not specified): ```javascript [ @@ -800,7 +866,7 @@ The client can subscribe to the following events: - `subscribeNewBlock` - new block added to blockchain - `subscribeNewTransaction` - new transaction added to blockchain (all addresses) -- `subscribeAddresses` - new transaction for given address (list of addresses) +- `subscribeAddresses` - new transaction for a given address (list of addresses) added to mempool - `subscribeFiatRates` - new currency rate ticker There can be always only one subscription of given event per connection, i.e. new list of addresses replaces previous list of addresses. @@ -811,7 +877,7 @@ _Note: If there is reorg on the backend (blockchain), you will get a new block h Websocket communication format -``` +```javascript { "id":"1", //an id to help to identify the response "method":"", @@ -821,7 +887,7 @@ Websocket communication format Example for subscribing to an address (or multiple addresses) -``` +```javascript { "id":"1", "method":"subscribeAddresses", @@ -830,3 +896,28 @@ Example for subscribing to an address (or multiple addresses) } } ``` + +## Legacy API V1 + +The legacy API is a compatible subset of API provided by **Bitcore Insight**. It is supported only Bitcoin-type coins. The details of the REST/socket.io requests can be found in the Insight's documentation. + +### REST API + +``` +GET /api/v1/block-index/ +GET /api/v1/tx/ +GET /api/v1/address/
+GET /api/v1/utxo/
+GET /api/v1/block/ +GET /api/v1/estimatefee/ +GET /api/v1/sendtx/ +POST /api/v1/sendtx/ (hex tx data in request body) +``` + +### Socket.io API + +Socket.io interface is provided at `/socket.io/`. The interface also can be explored using Blockbook Socket.io Test Page found at `/test-socketio.html`. + +The legacy API is provided as is and will not be further developed. + +The legacy API is currently (as of Blockbook v0.4.0) also accessible without the _/v1/_ prefix, however in the future versions the version less access will be removed. diff --git a/docs/rocksdb.md b/docs/rocksdb.md index 755049d205..ddc2356f93 100644 --- a/docs/rocksdb.md +++ b/docs/rocksdb.md @@ -2,143 +2,198 @@ **Blockbook** stores data the key-value store [RocksDB](https://github.com/facebook/rocksdb/wiki). As there are multiple indexes, Blockbook uses RocksDB **column families** feature to store indexes separately. ->The database structure is described in golang pseudo types in the form *(name type)*. +> The database structure is described in golang pseudo types in the form _(name type)_. > ->Operators used in the description: ->- *->* mapping from key to value. ->- *\+* concatenation, ->- *[]* array +> Operators used in the description: > ->Types used in the description: ->- *[]byte* - variable length array of bytes ->- *[32]byte* - fixed length array of bytes (32 bytes long in this case) ->- *uint32* - unsigned integer, stored as array of 4 bytes in big endian* ->- *vint*, *vuint* - variable length signed/unsigned int ->- *addrDesc* - address descriptor, abstraction of an address. -For Bitcoin type coins it is the transaction output script, stored as variable length array of bytes. -For Ethereum type coins it is fixed size array of 20 bytes. ->- *bigInt* - unsigned big integer, stored as length of the array (1 byte) followed by array of bytes of big int, i.e. *(int_len byte)+(int_value []byte)*. Zero is stored as one byte of value 0. +> - _->_ mapping from key to value. +> - _\+_ concatenation, +> - _[]_ array +> +> Types used in the description: +> +> - _[]byte_ - variable length array of bytes +> - _[32]byte_ - fixed length array of bytes (32 bytes long in this case) +> - _uint32_ - unsigned integer, stored as array of 4 bytes in big endian\* +> - _vint_, _vuint_ - variable length signed/unsigned int +> - _addrDesc_ - address descriptor, abstraction of an address. +> For Bitcoin type coins it is the transaction output script, stored as variable length array of bytes. +> For Ethereum type coins it is fixed size array of 20 bytes. +> - _bigInt_ - unsigned big integer, stored as length of the array (1 byte) followed by array of bytes of big int, i.e. _(int_len byte)+(int_value []byte)_. Zero is stored as one byte of value 0. +> - _float32_ - float32 number stored as _uint32_ +> - string - string stored as `(len vuint)+(value []byte)` **Database structure:** -The database structure described here is of Blockbook version **0.3.6** (internal data format version 5). +The database structure described here is of Blockbook version **0.4.0** (internal data format version 6). + +The database structure for **Bitcoin type** and **Ethereum type** coins is different. Column families used for both types: -The database structure for **Bitcoin type** and **Ethereum type** coins is slightly different. Column families used for both types: -- default, height, addresses, transactions, blockTxs +- default, height, addresses, transactions, blockTxs, fiatRates Column families used only by **Bitcoin type** coins: + - addressBalance, txAddresses Column families used only by **Ethereum type** coins: -- addressContracts + +- addressContracts, internalData, contracts, functionSignatures, blockInternalDataErrors, addressAliases **Column families description:** - **default** - Stores internal state in json format, under the key *internalState*. - + Stores internal state in json format, under the key _internalState_. + Most important internal state values are: + - coin - which coin is indexed in DB - - data format version - currently 5 + - data format version - currently 6 - dbState - closed, open, inconsistent - + Blockbook is checking on startup these values and does not allow to run against wrong coin, data format version and in inconsistent state. The database must be recreated if the internal state does not match. -- **height** +- **height** + + Maps _block height_ to _block hash_ and additional data about block. - Maps *block height* to *block hash* and additional data about block. - ``` - (height uint32) -> (hash [32]byte)+(time uint32)+(nr_txs vuint)+(size vuint) - ``` + ``` + (height uint32) -> (hash [32]byte)+(time uint32)+(nr_txs vuint)+(size vuint) + ``` - **addresses** - Maps *addrDesc+block height* to *array of transactions with array of input/output indexes*. - - The *block height* in the key is stored as bitwise complement ^ of the height to sort the keys in the order from newest to oldest. - - As there can be multiple inputs/outputs for the same address in one transaction, each txid is followed by variable length array of input/output indexes. - The index values in the array are multiplied by two, the last element of the array has the lowest bit set to 1. - Input or output is distinguished by the sign of the index, output is positive, input is negative (by operation bitwise complement ^ performed on the number). - ``` - (addrDesc []byte)+(^height uint32) -> []((txid [32]byte)+[](index vint)) - ``` + Maps _addrDesc+block height_ to _array of transactions with array of input/output indexes_. + + The _block height_ in the key is stored as bitwise complement ^ of the height to sort the keys in the order from newest to oldest. + + As there can be multiple inputs/outputs for the same address in one transaction, each txid is followed by variable length array of input/output indexes. + The index values in the array are multiplied by two, the last element of the array has the lowest bit set to 1. + Input or output is distinguished by the sign of the index, output is positive, input is negative (by operation bitwise complement ^ performed on the number). + + ``` + (addrDesc []byte)+(^height uint32) -> []((txid [32]byte)+[](index vint)) + ``` - **addressBalance** (used only by Bitcoin type coins) - Maps *addrDesc* to *number of transactions*, *sent amount*, *total balance* and a list of *unspent transactions outputs (UTXOs)*, ordered from oldest to newest - ``` - (addrDesc []byte) -> (nr_txs vuint)+(sent_amount bigInt)+(balance bigInt)+ - []((txid [32]byte)+(vout vuint)+(block_height vuint)+(amount bigInt)) - ``` + Maps _addrDesc_ to _number of transactions_, _sent amount_, _total balance_ and a list of _unspent transactions outputs (UTXOs)_, ordered from oldest to newest + + ``` + (addrDesc []byte) -> (nr_txs vuint)+(sent_amount bigInt)+(balance bigInt)+ + []((txid [32]byte)+(vout vuint)+(block_height vuint)+(amount bigInt)) + ``` - **txAddresses** (used only by Bitcoin type coins) - Maps *txid* to *block height* and array of *input addrDesc* with *amounts* and array of *output addrDesc* with *amounts*, with flag if output is spent. In case of spent output, *addrDesc_len* is negative (negative sign is achieved by bitwise complement ^). - ``` - (txid []byte) -> (height vuint)+ - (nr_inputs vuint)+[]((addrDesc_len vuint)+(addrDesc []byte)+(amount bigInt))+ - (nr_outputs vuint)+[]((addrDesc_len vint)+(addrDesc []byte)+(amount bigInt)) - ``` + Maps _txid_ to _block height_ and array of _input addrDesc_ with _amounts_ and array of _output addrDesc_ with _amounts_, with flag if output is spent. In case of spent output, _addrDesc_len_ is negative (negative sign is achieved by bitwise complement ^). + + ``` + (txid []byte) -> (height vuint)+ + (nr_inputs vuint)+[]((addrDesc_len vuint)+(addrDesc []byte)+(amount bigInt))+ + (nr_outputs vuint)+[]((addrDesc_len vint)+(addrDesc []byte)+(amount bigInt)) + ``` - **addressContracts** (used only by Ethereum type coins) - Maps *addrDesc* to *total number of transactions*, *number of non contract transactions*, *number of internal transactions* - and array of *contracts* with *number of transfers* of given address. - ``` - (addrDesc []byte) -> (total_txs vuint)+(non-contract_txs vuint)+(internal_txs vuint)+ - []((contractAddrDesc []byte)+(type+4*nr_transfers vuint))+ - <(value bigInt) if ERC20> or <(nr_values vuint)+[](id bigInt) if ERC721> or <(nr_values vuint)+[]((id bigInt)+(value bigInt)) if ERC1155> - ``` + Maps _addrDesc_ to _total number of transactions_, _number of non contract transactions_, _number of internal transactions_ + and array of _contracts_ with _number of transfers_ of given address. + + ``` + (addrDesc []byte) -> (total_txs vuint)+(non-contract_txs vuint)+(internal_txs vuint)+ + []((contractAddrDesc []byte)+(type+4*nr_transfers vuint))+ + <(value bigInt) if ERC20> or + <(nr_values vuint)+[](id bigInt) if ERC721> or + <(nr_values vuint)+[]((id bigInt)+(value bigInt)) if ERC1155> + ``` - **internalData** (used only by Ethereum type coins) - Maps *txid* to *type (CALL 0 | CREATE 1)*, *addrDesc of created contract for CREATE type*, array of *type (CALL 0 | CREATE 1 | SELFDESTRUCT 2)*, *from addrDesc*, *to addrDesc*, *value bigInt* and possible *error*. - ``` - (txid []byte) -> (type+2*nr_transfers vuint)+<(addrDesc []byte) if CREATE>+ - []((type byte)+(fromAddrDesc []byte)+(toAddrDesc []byte)+(value bigInt))+ - (error []byte) - ``` + Maps _txid_ to _type (CALL 0 | CREATE 1)_, _addrDesc of created contract for CREATE type_, array of _type (CALL 0 | CREATE 1 | SELFDESTRUCT 2)_, _from addrDesc_, _to addrDesc_, _value bigInt_ and possible _error_. + + ``` + (txid []byte) -> (type+2*nr_transfers vuint)+<(addrDesc []byte) if CREATE>+ + []((type byte)+(fromAddrDesc []byte)+(toAddrDesc []byte)+(value bigInt))+ + (error []byte) + ``` - **blockTxs** - Maps *block height* to data necessary for blockchain rollback. Only last 300 (by default) blocks are kept. - The content of value data differs for Bitcoin and Ethereum types. - - - Bitcoin type - - The value is an array of *txids* and *input points* in the block. - ``` - (height uint32) -> []((txid [32]byte)+(nr_inputs vuint)+[]((txid [32]byte)+(index vint))) - ``` - - - Ethereum type - - The value is an array of transaction data. For each transaction is stored *txid*, - *from* and *to* address descriptors and array of contract transfer infos consisting of - *from*, *to* and *contract* address descriptors, *type (ERC20 0 | ERC721 1 | ERC1155 2)* and value (or list of id+value for ERC1155) - ``` - (height uint32) -> []( - (txid [32]byte)+(from addrDesc)+(to addrDesc)+(nr_contracts vuint)+ - []((from addrDesc)+(to addrDesc)+(contract addrDesc)+(type byte)+ - <(value bigInt) if ERC20 or ERC721> or <(nr_values vuint)+[]((id bigInt)+(value bigInt)) if ERC1155>) - ) - ``` + Maps _block height_ to data necessary for blockchain rollback. Only last 300 (by default) blocks are kept. + The content of value data differs for Bitcoin and Ethereum types. + + - Bitcoin type + + The value is an array of _txids_ and _input points_ in the block. + + ``` + (height uint32) -> []((txid [32]byte)+(nr_inputs vuint)+[]((txid [32]byte)+(index vint))) + ``` + + - Ethereum type + + The value is an array of transaction data. For each transaction is stored _txid_, + _from_ and _to_ address descriptors and array of contract transfer infos consisting of + _from_, _to_ and _contract_ address descriptors, _type (ERC20 0 | ERC721 1 | ERC1155 2)_ and value (or list of id+value for ERC1155) + + ``` + (height uint32) -> []( + (txid [32]byte)+(from addrDesc)+(to addrDesc)+(nr_contracts vuint)+ + []((from addrDesc)+(to addrDesc)+(contract addrDesc)+(type byte)+ + <(value bigInt) if ERC20 or ERC721> or + <(nr_values vuint)+[]((id bigInt)+(value bigInt)) if ERC1155>) + ) + ``` - **transactions** - Transaction cache, *txdata* is generated by coin specific parser function PackTx. - ``` - (txid []byte) -> (txdata []byte) - ``` + Transaction cache, _txdata_ is generated by coin specific parser function PackTx. + + ``` + (txid []byte) -> (txdata []byte) + ``` - **fiatRates** - Stores fiat rates in json format. - ``` - (timestamp YYYYMMDDhhmmss) -> (rates json) - ``` + Stored daily fiat rates, one day as one entry. + + ``` + (timestamp YYYYMMDDhhmmss) -> (nr_currencies vuint)+[]((currency string)+(rate float32))+ + (nr_tokens vuint)+[]((tokenContract string)+(tokenRate float32)) + ``` + +- **contracts** (used only by Ethereum type coins) + + Maps contract _addrDesc_ to information about contract - _name_, _symbol_, _type_ (ERC20,ERC721 or ERC1155), _decimals_, _created_ and _destructed_ in block height + + ``` + (addrDesc []byte) -> (name string)+(symbol string)+(type string)+(decimals vuint)+ + (createdInBlock vuint)+(destroyedInBlock vuint) + ``` + +- **functionSignatures** (used only by Ethereum type coins) + + Database of four byte signatures downloaded from https://www.4byte.directory/. + + ``` + (fourBytes uint32)+(id uint32) -> (signatureName string)+[]((parameter string)) + ``` + +- **blockInternalDataErrors** (used only by Ethereum type coins) + + Errors when fetching internal data from backend. Stored so that the action can be retried. + + ``` + (blockHeight uint32) -> (blockHash [32]byte)+(retryCount byte)+(errorMessage []byte) + ``` + +- **addressAliases** (used only by Ethereum type coins) + + Maps _address_ to address ENS name. + ``` + (address []byte) -> (ensName []byte) + ``` -The `txid` field as specified in this documentation is a byte array of fixed size with length 32 bytes (*[32]byte*), however some coins may define other fixed size lengths. +**Note:** +The `txid` field as specified in this documentation is a byte array of fixed size with length 32 bytes (_[32]byte_), however some coins may define other fixed size lengths. From 97f1a41e554c1b470da9f1f684b141b58c27e1ce Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 1 Feb 2023 17:18:04 +0100 Subject: [PATCH 124/530] Fix issue with git during build Git is reporting fatal: detected dubious ownership in repository at '/src --- build/docker/bin/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/docker/bin/Makefile b/build/docker/bin/Makefile index c73045ce69..9111e24e0b 100644 --- a/build/docker/bin/Makefile +++ b/build/docker/bin/Makefile @@ -1,6 +1,6 @@ SHELL = /bin/bash VERSION ?= devel -GITCOMMIT = $(shell cd /src && git describe --always --dirty) +GITCOMMIT = $(shell cd /src && git config --global --add safe.directory /src && git describe --always --dirty) BUILDTIME = $(shell date --iso-8601=seconds) LDFLAGS := -X github.com/trezor/blockbook/common.version=$(VERSION) -X github.com/trezor/blockbook/common.gitcommit=$(GITCOMMIT) -X github.com/trezor/blockbook/common.buildtime=$(BUILDTIME) BLOCKBOOK_BASE := $(GOPATH)/src/github.com/trezor From b227dfedcbde37219a4cb16500529513cc48e1e8 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 1 Feb 2023 18:09:49 +0100 Subject: [PATCH 125/530] Documentation update --- docs/api.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/api.md b/docs/api.md index c67a4a20f6..4b0cf795ad 100644 --- a/docs/api.md +++ b/docs/api.md @@ -98,7 +98,7 @@ Get transaction returns "normalized" data about transaction, which has the same GET /api/v2/tx/ ``` -Response for Bitcoin-type coins: +Response for Bitcoin-type coins, confirmed transaction: ```javascript { @@ -142,6 +142,8 @@ Response for Bitcoin-type coins: "blockHeight": 2647927, "confirmations": 1, "blockTime": 1553088212, + "size": 234, + "vsize": 153, "value": "55795008999999", "valueIn": "55795108999999", "fees": "100000000", @@ -149,6 +151,54 @@ Response for Bitcoin-type coins: } ``` +Response for Bitcoin-type coins, unconfirmed transaction (_blockHeight_: -1, _confirmations_: 0, mining estimates _confirmationETABlocks_ and _confirmationETASeconds_): + +```javascript +{ + "txid": "cd8ec77174e426070d0a50779232bba7312b712e2c6843d82d963d7076c61366", + "version": 2, + "vin": [ + { + "txid": "47687cc4abb58d815168686465a38113a0608b2568a6d6480129d197e653f6dc", + "sequence": 4294967295, + "n": 0, + "addresses": ["bc1qka0gpenex558g8gpxmpx247mwhw695k6a7yhs4"], + "isAddress": true, + "value": "1983687" + } + ], + "vout": [ + { + "value": "3106", + "n": 0, + "hex": "0020d7da4868055fde790a8581637ab81c216e17a3f8a099283da6c4a27419ffa539", + "addresses": [ + "bc1q6ldys6q9tl08jz59s93h4wquy9hp0glc5zvjs0dxcj38gx0l55uspu8x86" + ], + "isAddress": true + }, + { + "value": "1979101", + "n": 1, + "hex": "0014381be30ca46ddf378ef69ebc4a601bd6ff30b754", + "addresses": ["bc1q8qd7xr9ydh0n0rhkn67y5cqm6mlnpd65dcyeeg"], + "isAddress": true + } + ], + "blockHeight": -1, + "confirmations": 0, + "confirmationETABlocks": 3, + "confirmationETASeconds": 2055, + "blockTime": 1675270935, + "size": 234, + "vsize": 153, + "value": "1982207", + "valueIn": "1983687", + "fees": "1480", + "hex": "020000000001...b18f00000000" +} +``` + Response for Ethereum-type coins. Data of the transaction consist of: - always only one _vin_, only one _vout_ From 62f3a3bbce822dc0d6301a58d50673cdc47eb31d Mon Sep 17 00:00:00 2001 From: JoHnY Date: Thu, 9 Feb 2023 09:17:52 +0000 Subject: [PATCH 126/530] =?UTF-8?q?etc=201.12.7=20=E2=86=92=201.12.10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum-classic.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/ethereum-classic.json b/configs/coins/ethereum-classic.json index 456fe9fcf9..e54b70ef40 100644 --- a/configs/coins/ethereum-classic.json +++ b/configs/coins/ethereum-classic.json @@ -21,10 +21,10 @@ "package_name": "backend-ethereum-classic", "package_revision": "satoshilabs-1", "system_user": "ethereum-classic", - "version": "1.12.7", - "binary_url": "https://github.com/etclabscore/core-geth/releases/download/v1.12.7/core-geth-linux-v1.12.7.zip", + "version": "1.12.10", + "binary_url": "https://github.com/etclabscore/core-geth/releases/download/v1.12.10/core-geth-linux-v1.12.10.zip", "verification_type": "sha256", - "verification_source": "91e8834b01e89aaea7b89a70cb005b527ab7815f17ce123229733aa49ff95ec3", + "verification_source": "40f423fb19b36b9412388adb18353d78bfda31c71395be56a2c51772a12cbf81", "extract_command": "unzip -d backend", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --classic --ipcdisable --txlookuplimit 0 --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From c163b8f33d5aaecfaf3cf3c07a2a6f64c7fef555 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 17 Feb 2023 23:26:06 +0100 Subject: [PATCH 127/530] Add goerli archive integration test --- .gitlab-ci.yml | 10 +++--- .../ethereum_testnet_goerli_archive.json | 34 +++++++++++++++++++ tests/tests.json | 2 +- 3 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 tests/rpc/testdata/ethereum_testnet_goerli_archive.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 779d22370a..efc9e57f57 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -146,7 +146,7 @@ backend-deploy-and-test-bitcoin_testnet: - configs/coins/bitcoin_testnet.json tags: - blockbook - script: ./contrib/scripts/backend-deploy-and-test.sh bitcoin_testnet bitcoin-testnet bitcoin=test testnet3/debug.log + script: ./contrib/scripts/backend-deploy-and-test.sh bitcoin_testnet bitcoin-testnet bitcoin=test testnet3/debug.log backend-deploy-and-test-zcash_testnet: stage: backend-deploy-and-test @@ -157,15 +157,15 @@ backend-deploy-and-test-zcash_testnet: - configs/coins/zcash_testnet.json tags: - blockbook - script: ./contrib/scripts/backend-deploy-and-test.sh zcash_testnet zcash-testnet zcash=test testnet3/debug.log + script: ./contrib/scripts/backend-deploy-and-test.sh zcash_testnet zcash-testnet zcash=test testnet3/debug.log -backend-deploy-and-test-ethereum_testnet_ropsten: +backend-deploy-and-test-goerli-archive: stage: backend-deploy-and-test only: refs: - master changes: - - configs/coins/ethereum_testnet_ropsten.json + - configs/coins/ethereum_testnet_goerli_archive.json tags: - blockbook - script: ./contrib/scripts/backend-deploy-and-test.sh ethereum_testnet_ropsten ethereum-testnet-ropsten ethereum=test ethereum_testnet_ropsten.log + script: ./contrib/scripts/backend-deploy-and-test.sh ethereum_testnet_goerli_archive ethereum-testnet-goerli-archive ethereum=test ethereum_testnet_goerli_archive.log diff --git a/tests/rpc/testdata/ethereum_testnet_goerli_archive.json b/tests/rpc/testdata/ethereum_testnet_goerli_archive.json new file mode 100644 index 0000000000..ac2b80ee06 --- /dev/null +++ b/tests/rpc/testdata/ethereum_testnet_goerli_archive.json @@ -0,0 +1,34 @@ +{ + "blockHeight": 6509294, + "blockHash": "0x55eced8804c4358572c612e5507994590db91000db483d4f30588be2e85a31ca", + "blockTime": 1646866921, + "blockSize": 60725, + "blockTxs": [ + "0x583468dcbc06bd14a523a5809872b2cd0be9481f24380e78463337e79740135f", + "0x4ff5b60fceab52918f2e1f9d39c125c8c5856fa2349003c6d163225a145e34db", + "0xa08ca828de3986f3d182dc13c7293068ec5d64d63221a196b5e589fec10a448a", + "0x43dad1209906ad2866cc9bb5e0309530b6ab744b55f62c5c66406197b64583ae", + "0x43a8f9a93060681a466f918dd90d836fe089115e8c92a4b13e37b2982ba76090", + "0x1d1184e4d4b125e7017ad0ea8fafd59d34ea848abc88a0b0afa648b5d148ff53" + ], + "txDetails": { + "0x1d1184e4d4b125e7017ad0ea8fafd59d34ea848abc88a0b0afa648b5d148ff53": { + "txid": "0x1d1184e4d4b125e7017ad0ea8fafd59d34ea848abc88a0b0afa648b5d148ff53", + "blocktime": 1646866921, + "time": 1646866921, + "vin": [ + { + "addresses": ["0x68A3E5Ec00Ec5880Fae10CB69f047fa42Cd2d32C"] + } + ], + "vout": [ + { + "value": 0.4, + "scriptPubKey": { + "addresses": ["0x71F33321375494206d23Cc3950A923a9b4c615A4"] + } + } + ] + } + } +} diff --git a/tests/tests.json b/tests/tests.json index 382962e889..3bc8339384 100644 --- a/tests/tests.json +++ b/tests/tests.json @@ -97,7 +97,7 @@ "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"], "sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"] }, - "ethereum_testnet_ropsten": { + "ethereum_testnet_goerli_archive": { "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"] }, From 026899edf1b1c060cc02608dd8deb1bab4a3c923 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 18 Feb 2023 01:11:04 +0100 Subject: [PATCH 128/530] Fix dogecoin integration test --- tests/rpc/testdata/dogecoin.json | 101 ++++++++++++++++--------------- tests/tests.json | 2 +- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/tests/rpc/testdata/dogecoin.json b/tests/rpc/testdata/dogecoin.json index 75b32f85ba..3fce9a45ec 100644 --- a/tests/rpc/testdata/dogecoin.json +++ b/tests/rpc/testdata/dogecoin.json @@ -1,53 +1,54 @@ { - "blockHeight": 2000002, - "blockHash": "6db0b6fc543cacbc4244f5fab23af56792a87eacc3149957a1922fd59d4d03e0", - "blockTime": 1512601001, - "blockTxs": [ - "cc3c92a9da8e28f18faf17efe74e96f541f03e3a300c55fef0c4dbc9e5b14c01", - "cb3cb43e34385556c9617699687b15f3dbad57cb0e8548114b66c55512c51a52", - "f6be02faa646a1ad764e4eb49fb6f02cbae69e5bc396a8977e59c7d887aa38b2", - "79883da5f57c942feb922e4bb635af631219aae2e70037459b07840945d6466c", - "23b5caddcb3c1222a0b97ff6f67185266b982465ef1a3546c7fc6ad8414ba7d0", - "82cf9f6822362ecfc0d47cc6b7372862ef0fdd14b259c42c8f2def8589f266f0", - "12a4e4daa6e3b842a2395c315f296f284ac2085e2f2fb4f356f94e08117963ba", - "cc136353be3db35e051e5dd34ff29f9cfe47a7b3f6ab7383fd644bc00ab1e348", - "f2db6749bc5f7e727f9e6c9a5553ad44dc59d9994dcadc24a57cf6042d1d07e7" - ], - "txDetails": { - "f6be02faa646a1ad764e4eb49fb6f02cbae69e5bc396a8977e59c7d887aa38b2": { - "hex": "0100000002361742ebea8bdce921359dbe528b23cc8050dc86950fd07011a74258cd0215dde5030000db00483045022100885238254408979e8f44c087c06333376980a73594c4d51c8cdf457934514e34022026287952d554f9f9f24c0a47096cce0db53547099aa700a91fa9046eb5c4960801483045022100a4ce7700bdf654dfe2a4fc5796bd3466be6af3691dc263821d4c71f91f9d9d1e02200b988bea6bf9e7d44e9a4b8cee9e390c72d284e3df1aacf6ab23368560b3e53e0147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52aeffffffff85f2900e642720043fae056075b48ea83cbd3653797c40fb4e7e512938dcda7573020000da004730440220138a8dd272f3081e54b172fb3e6cf43fe5375369d7e93c310ceba852c22ba16f022050de068f932f6d6db9889a8d4e489ffeda2ed1848cd2a71a06d635e6d1799b8e01483045022100af99d412c97062219ef56c3c8ad99023feab94042d310f792392940c61df9bdd02200b4542395624412260c96c46427509c2b801df33e15da08c6c505594c7c142010147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52aeffffffff01c0ebf7b90100000017a9147541523df4d0d0875c024e1906b0d195abaf20958700000000", - "txid": "f6be02faa646a1ad764e4eb49fb6f02cbae69e5bc396a8977e59c7d887aa38b2", - "blocktime": 1512601001, - "time": 1512601001, - "locktime": 0, - "version": 1, - "vin": [ - { - "txid": "dd1502cd5842a71170d00f9586dc5080cc238b52be9d3521e9dc8beaeb421736", - "vout": 997, - "scriptSig": { - "hex": "00483045022100885238254408979e8f44c087c06333376980a73594c4d51c8cdf457934514e34022026287952d554f9f9f24c0a47096cce0db53547099aa700a91fa9046eb5c4960801483045022100a4ce7700bdf654dfe2a4fc5796bd3466be6af3691dc263821d4c71f91f9d9d1e02200b988bea6bf9e7d44e9a4b8cee9e390c72d284e3df1aacf6ab23368560b3e53e0147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52ae" - }, - "sequence": 4294967295 - }, - { - "txid": "75dadc3829517e4efb407c795336bd3ca88eb4756005ae3f042027640e90f285", - "vout": 627, - "scriptSig": { - "hex": "004730440220138a8dd272f3081e54b172fb3e6cf43fe5375369d7e93c310ceba852c22ba16f022050de068f932f6d6db9889a8d4e489ffeda2ed1848cd2a71a06d635e6d1799b8e01483045022100af99d412c97062219ef56c3c8ad99023feab94042d310f792392940c61df9bdd02200b4542395624412260c96c46427509c2b801df33e15da08c6c505594c7c142010147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52ae" - }, - "sequence": 4294967295 - } - ], - "vout": [ - { - "value": 74.15000000, - "n": 0, - "scriptPubKey": { - "hex": "a9147541523df4d0d0875c024e1906b0d195abaf209587" - } - } - ] + "blockHeight": 2000002, + "blockHash": "6db0b6fc543cacbc4244f5fab23af56792a87eacc3149957a1922fd59d4d03e0", + "blockTime": 1512601001, + "blockTxs": [ + "cc3c92a9da8e28f18faf17efe74e96f541f03e3a300c55fef0c4dbc9e5b14c01", + "cb3cb43e34385556c9617699687b15f3dbad57cb0e8548114b66c55512c51a52", + "f6be02faa646a1ad764e4eb49fb6f02cbae69e5bc396a8977e59c7d887aa38b2", + "79883da5f57c942feb922e4bb635af631219aae2e70037459b07840945d6466c", + "23b5caddcb3c1222a0b97ff6f67185266b982465ef1a3546c7fc6ad8414ba7d0", + "82cf9f6822362ecfc0d47cc6b7372862ef0fdd14b259c42c8f2def8589f266f0", + "12a4e4daa6e3b842a2395c315f296f284ac2085e2f2fb4f356f94e08117963ba", + "cc136353be3db35e051e5dd34ff29f9cfe47a7b3f6ab7383fd644bc00ab1e348", + "f2db6749bc5f7e727f9e6c9a5553ad44dc59d9994dcadc24a57cf6042d1d07e7" + ], + "txDetails": { + "f6be02faa646a1ad764e4eb49fb6f02cbae69e5bc396a8977e59c7d887aa38b2": { + "hex": "0100000002361742ebea8bdce921359dbe528b23cc8050dc86950fd07011a74258cd0215dde5030000db00483045022100885238254408979e8f44c087c06333376980a73594c4d51c8cdf457934514e34022026287952d554f9f9f24c0a47096cce0db53547099aa700a91fa9046eb5c4960801483045022100a4ce7700bdf654dfe2a4fc5796bd3466be6af3691dc263821d4c71f91f9d9d1e02200b988bea6bf9e7d44e9a4b8cee9e390c72d284e3df1aacf6ab23368560b3e53e0147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52aeffffffff85f2900e642720043fae056075b48ea83cbd3653797c40fb4e7e512938dcda7573020000da004730440220138a8dd272f3081e54b172fb3e6cf43fe5375369d7e93c310ceba852c22ba16f022050de068f932f6d6db9889a8d4e489ffeda2ed1848cd2a71a06d635e6d1799b8e01483045022100af99d412c97062219ef56c3c8ad99023feab94042d310f792392940c61df9bdd02200b4542395624412260c96c46427509c2b801df33e15da08c6c505594c7c142010147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52aeffffffff01c0ebf7b90100000017a9147541523df4d0d0875c024e1906b0d195abaf20958700000000", + "txid": "f6be02faa646a1ad764e4eb49fb6f02cbae69e5bc396a8977e59c7d887aa38b2", + "blocktime": 1512601001, + "time": 1512601001, + "locktime": 0, + "version": 1, + "vsize": 561, + "vin": [ + { + "txid": "dd1502cd5842a71170d00f9586dc5080cc238b52be9d3521e9dc8beaeb421736", + "vout": 997, + "scriptSig": { + "hex": "00483045022100885238254408979e8f44c087c06333376980a73594c4d51c8cdf457934514e34022026287952d554f9f9f24c0a47096cce0db53547099aa700a91fa9046eb5c4960801483045022100a4ce7700bdf654dfe2a4fc5796bd3466be6af3691dc263821d4c71f91f9d9d1e02200b988bea6bf9e7d44e9a4b8cee9e390c72d284e3df1aacf6ab23368560b3e53e0147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52ae" + }, + "sequence": 4294967295 + }, + { + "txid": "75dadc3829517e4efb407c795336bd3ca88eb4756005ae3f042027640e90f285", + "vout": 627, + "scriptSig": { + "hex": "004730440220138a8dd272f3081e54b172fb3e6cf43fe5375369d7e93c310ceba852c22ba16f022050de068f932f6d6db9889a8d4e489ffeda2ed1848cd2a71a06d635e6d1799b8e01483045022100af99d412c97062219ef56c3c8ad99023feab94042d310f792392940c61df9bdd02200b4542395624412260c96c46427509c2b801df33e15da08c6c505594c7c142010147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52ae" + }, + "sequence": 4294967295 } + ], + "vout": [ + { + "value": 74.15, + "n": 0, + "scriptPubKey": { + "hex": "a9147541523df4d0d0875c024e1906b0d195abaf209587" + } + } + ] } -} \ No newline at end of file + } +} diff --git a/tests/tests.json b/tests/tests.json index 3bc8339384..cb5cc1ac23 100644 --- a/tests/tests.json +++ b/tests/tests.json @@ -85,7 +85,7 @@ "sync": ["ConnectBlocksParallel", "ConnectBlocks"] }, "dogecoin": { - "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync"], + "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "MempoolSync"], "sync": ["ConnectBlocksParallel", "ConnectBlocks"] }, "dogecoin_testnet": { From 8e28ebe8dc781f843958917cf045b39899101390 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 18 Feb 2023 01:17:56 +0100 Subject: [PATCH 129/530] Fix backward compatibility with v0.3.6 --- api/types.go | 1 + api/worker.go | 10 ++++++++-- bchain/types_ethereum_type.go | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/api/types.go b/api/types.go index c0ef138304..23d4dd1fc4 100644 --- a/api/types.go +++ b/api/types.go @@ -339,6 +339,7 @@ type Address struct { TotalBaseValue float64 `json:"totalBaseValue,omitempty"` // value including tokens in base currency TotalSecondaryValue float64 `json:"totalSecondaryValue,omitempty"` // value including tokens in secondary currency ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"` + Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty"` // deprecated AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` // helpers for explorer Filter string `json:"-"` diff --git a/api/worker.go b/api/worker.go index e263f51387..3024a5e34f 100644 --- a/api/worker.go +++ b/api/worker.go @@ -1007,6 +1007,7 @@ type ethereumTypeAddressData struct { func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter, secondaryCoin string) (*db.AddrBalance, *ethereumTypeAddressData, error) { var ba *db.AddrBalance + var n uint64 // unknown number of results for paging initially d := ethereumTypeAddressData{totalResults: -1} ca, err := w.db.GetAddrDescContracts(addrDesc) @@ -1031,11 +1032,10 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto if b != nil { ba.BalanceSat = *b } - n, err := w.chain.EthereumTypeGetNonce(addrDesc) + n, err = w.chain.EthereumTypeGetNonce(addrDesc) if err != nil { return nil, nil, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc) } - d.nonce = strconv.Itoa(int(n)) ticker := w.is.GetCurrentTicker("", "") if details > AccountDetailsBasic { d.tokens = make([]Token, len(ca.Contracts)) @@ -1089,6 +1089,8 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto } } } + // returns 0 for unknown address + d.nonce = strconv.Itoa(int(n)) // special handling if filtering for a contract, return the contract details even though the address had no transactions with it if len(d.tokens) == 0 && len(filterDesc) > 0 && details >= AccountDetailsTokens { t, err := w.getEthereumContractBalanceFromBlockchain(addrDesc, filterDesc, details) @@ -1345,6 +1347,10 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco Nonce: ed.nonce, AddressAliases: w.getAddressAliases(addresses), } + // keep address backward compatible, set deprecated Erc20Contract value if ERC20 token + if ed.contractInfo != nil && ed.contractInfo.Type == bchain.ERC20TokenType { + r.Erc20Contract = ed.contractInfo + } glog.Info("GetAddress ", address, ", ", time.Since(start)) return r, nil } diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index a04a0810ec..adceec3b2e 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -55,7 +55,7 @@ type EthereumInternalData struct { Error string } -// ContractInfo contains info about ERC20 contract +// ContractInfo contains info about a contract type ContractInfo struct { Type TokenTypeName `json:"type"` Contract string `json:"contract"` From 324a4446ec6b2d9c7c4bc84a6c90360dba1f5337 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Fri, 17 Feb 2023 09:41:16 +0000 Subject: [PATCH 130/530] =?UTF-8?q?eth=20(+testnets)=201.10.26=20=E2=86=92?= =?UTF-8?q?=201.11.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 6 +++--- configs/coins/ethereum_archive.json | 6 +++--- configs/coins/ethereum_testnet_goerli.json | 6 +++--- configs/coins/ethereum_testnet_goerli_archive.json | 6 +++--- configs/coins/ethereum_testnet_sepolia.json | 6 +++--- configs/coins/ethereum_testnet_sepolia_archive.json | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 395868aee0..dfa2124498 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.26-e5eb32ac", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", + "version": "1.11.1-76961066", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 3c73486abe..abfd86b0c9 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.26-e5eb32ac", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", + "version": "1.11.1-76961066", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index 8ab40d4d65..fdc96a9cc3 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-goerli", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.26-e5eb32ac", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", + "version": "1.11.1-76961066", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli_archive.json b/configs/coins/ethereum_testnet_goerli_archive.json index 955019585b..f56216f366 100644 --- a/configs/coins/ethereum_testnet_goerli_archive.json +++ b/configs/coins/ethereum_testnet_goerli_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-goerli-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.26-e5eb32ac", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", + "version": "1.11.1-76961066", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 4503e73029..06d3ee7b55 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.26-e5eb32ac", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", + "version": "1.11.1-76961066", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --sepolia --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index ae4f12b36f..6201520768 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.10.26-e5eb32ac", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz", + "version": "1.11.1-76961066", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --sepolia --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From dcad5b79df5b41aa8ba84fd4889ff17d3797b25d Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 21 Feb 2023 10:53:56 +0100 Subject: [PATCH 131/530] Ignore DELEGATECALL in EVM call trace Geth v1.11 changed the tracer and are now returning the delegated value. See https://github.com/ethereum/go-ethereum/issues/26726 --- bchain/coins/eth/ethrpc.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index c4210fb951..61173b05c5 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -620,6 +620,9 @@ func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInt To: call.To, }) contracts = append(contracts, bchain.ContractInfo{Contract: call.From, DestructedInBlock: blockHeight}) + } else if call.Type == "DELEGATECALL" { + // ignore DELEGATECALL (geth v1.11 the changed tracer behavior) + // https://github.com/ethereum/go-ethereum/issues/26726 } else if err == nil && (value.BitLen() > 0 || b.ChainConfig.ProcessZeroInternalTransactions) { d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ Value: *value, From a80ea655fa0f8fbe981d9f1475197026ccf8753f Mon Sep 17 00:00:00 2001 From: JoHnY Date: Mon, 20 Feb 2023 15:02:50 +0000 Subject: [PATCH 132/530] =?UTF-8?q?zec=20(+testnet)=205.3.2=20=E2=86=92=20?= =?UTF-8?q?5.4.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/zcash.json | 6 +++--- configs/coins/zcash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index 21ad17b2bb..b080239f23 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,10 +22,10 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "5.3.2", - "binary_url": "https://z.cash/downloads/zcash-5.3.2-linux64-debian-bullseye.tar.gz", + "version": "5.4.1", + "binary_url": "https://z.cash/downloads/zcash-5.4.1-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "20b0aa39b72826fe5c2d967151ce8cccbd11c1cf1b6c2adf8ddad0c596e241fc", + "verification_source": "237e35ae9c6751f66dfd0d0d93f2844664609cc32580077d5f055c8497568313", "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 1a924e7b6e..9fd3650173 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -21,10 +21,10 @@ "backend": { "package_name": "backend-zcash-testnet", "package_revision": "satoshilabs-1", - "version": "5.3.2", - "binary_url": "https://z.cash/downloads/zcash-5.3.2-linux64-debian-bullseye.tar.gz", + "version": "5.4.1", + "binary_url": "https://z.cash/downloads/zcash-5.4.1-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "20b0aa39b72826fe5c2d967151ce8cccbd11c1cf1b6c2adf8ddad0c596e241fc", + "verification_source": "237e35ae9c6751f66dfd0d0d93f2844664609cc32580077d5f055c8497568313", "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 7dac974ba227b8b35d5bab25fbf9742d0ab02ec3 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 22 Feb 2023 10:58:51 +0100 Subject: [PATCH 133/530] Fix visual overflow in display of block in explorer --- server/public_test.go | 6 +++--- static/templates/block.html | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/public_test.go b/server/public_test.go index c18920601d..4189d8c6ea 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -314,7 +314,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -334,7 +334,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -343,7 +343,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { diff --git a/static/templates/block.html b/static/templates/block.html index 600175b9d4..fe9b7d2bfb 100644 --- a/static/templates/block.html +++ b/static/templates/block.html @@ -50,7 +50,7 @@
{{$b.Nonce}} + {{$b.Nonce}} Bits From 211aeff22d6f9ce59b26895883aa85905bba566b Mon Sep 17 00:00:00 2001 From: JoHnY Date: Mon, 27 Feb 2023 10:03:44 +0000 Subject: [PATCH 134/530] =?UTF-8?q?eth=20(+testnets)=201.11.1=20=E2=86=92?= =?UTF-8?q?=201.11.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 6 +++--- configs/coins/ethereum_archive.json | 6 +++--- configs/coins/ethereum_testnet_goerli.json | 6 +++--- configs/coins/ethereum_testnet_goerli_archive.json | 6 +++--- configs/coins/ethereum_testnet_sepolia.json | 6 +++--- configs/coins/ethereum_testnet_sepolia_archive.json | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index dfa2124498..e32587656b 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.1-76961066", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", + "version": "1.11.2-73b01f40", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index abfd86b0c9..4ba2ac43fb 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.1-76961066", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", + "version": "1.11.2-73b01f40", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index fdc96a9cc3..b4bc2a5fe3 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-goerli", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.1-76961066", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", + "version": "1.11.2-73b01f40", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli_archive.json b/configs/coins/ethereum_testnet_goerli_archive.json index f56216f366..b444b33564 100644 --- a/configs/coins/ethereum_testnet_goerli_archive.json +++ b/configs/coins/ethereum_testnet_goerli_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-goerli-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.1-76961066", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", + "version": "1.11.2-73b01f40", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 06d3ee7b55..268fac2e43 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.1-76961066", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", + "version": "1.11.2-73b01f40", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --sepolia --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 6201520768..8eb272bf45 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.1-76961066", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz", + "version": "1.11.2-73b01f40", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.1-76961066.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --sepolia --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From 0f23f10fe0d70534d669bef639ed06cc08284e8b Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 24 Feb 2023 01:05:14 +0100 Subject: [PATCH 135/530] Handle raw display of huge transactions in explorer --- server/public.go | 2 +- server/public_ethereumtype_test.go | 8 ++--- server/public_test.go | 32 +++++++++---------- static/css/{main.min.1.css => main.min.2.css} | 0 static/js/main.js | 3 ++ static/js/main.min.1.js | 1 - static/js/main.min.2.js | 1 + 7 files changed, 25 insertions(+), 22 deletions(-) rename static/css/{main.min.1.css => main.min.2.css} (100%) delete mode 100644 static/js/main.min.1.js create mode 100644 static/js/main.min.2.js diff --git a/server/public.go b/server/public.go index 4b561959bc..cd57179c43 100644 --- a/server/public.go +++ b/server/public.go @@ -344,7 +344,7 @@ func (s *PublicServer) newTemplateData(r *http.Request) *TemplateData { TOSLink: api.Text.TOSLink, } if !s.debug { - t.Minified = ".min.1" + t.Minified = ".min.2" } if s.is.HasFiatRates { // get the secondary coin and if it should be shown either from query parameters "secondary" and "use_secondary" diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index efca8c13cc..9094ca1751 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -24,7 +24,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE

Confirmed
Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers#
Contract 130.000000001000123013 S131
Contract 740.001000123074 S741
ContractTokensTransfers#
Contract 20511

Transactions

ERC721 Token Transfers
`, + `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE

Confirmed
Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers#
Contract 130.000000001000123013 S131
Contract 740.001000123074 S741
ContractTokensTransfers#
Contract 20511

Transactions

ERC721 Token Transfers
`, }, }, { @@ -33,7 +33,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE

Confirmed
Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers#
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, + `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE

Confirmed
Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers#
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, }, }, { @@ -42,14 +42,14 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE (40 Gwei)
Fees0.002081 FAKE
RBFON
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE (40 Gwei)
Fees0.002081 FAKE
RBFON
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, }, }, { name: "explorerTokenDetail " + dbtestdata.EthAddr7b, r: newGetRequest(ts.URL + "/nft/" + dbtestdata.EthAddrContractCd + "/" + "1"), status: http.StatusOK, contentType: "text/html; charset=utf-8", - body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
Contract typeERC20
`}, + body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
Contract typeERC20
`}, }, { name: "apiIndex", diff --git a/server/public_test.go b/server/public_test.go index 4189d8c6ea..7957f5d5d6 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -269,7 +269,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, }, }, { @@ -278,7 +278,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -287,7 +287,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input3172.83951062 FAKE
Total Output3172.83951 FAKE
Fees0.00000062 FAKE
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input3172.83951062 FAKE
Total Output3172.83951 FAKE
Fees0.00000062 FAKE
Raw Transaction
`, }, }, { @@ -296,7 +296,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Error

Transaction not found

`, + `Trezor Fake Coin Explorer

Error

Transaction not found

`, }, }, { @@ -305,7 +305,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Blocks

HeightHashTimestampTransactionsSize
22549400000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b61639 days 11 hours ago42345678
2254930000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e29971640 days 9 hours ago21234567
`, + `Trezor Fake Coin Explorer

Blocks

HeightHashTimestampTransactionsSize
22549400000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b61639 days 11 hours ago42345678
2254930000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e29971640 days 9 hours ago21234567
`, }, }, { @@ -314,7 +314,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -323,7 +323,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Application status

Synchronization with backend is disabled, the state of index is not up to date.

OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6

Blockbook

CoinFakecoin
Host
Version / Commit / Buildunknown / unknown / unknown
Synchronized
true
Last Block225494
Last Block Update`, + `Trezor Fake Coin Explorer

Application status

Synchronization with backend is disabled, the state of index is not up to date.

`, `

Blockbook

CoinFakecoin
Host
Version / Commit / Buildunknown / unknown / unknown
Synchronized
true
Last Block225494
Last Block Update`, `
Mempool in Sync
false
Last Mempool Update
Transactions in Mempool0
Size On Disk

Backend

Chainfakecoin
Version001001
Subversion/Fakecoin:0.0.1/
Last Block2
Difficulty
Blockbook - blockchain indexer for Trezor Suite https://trezor.io/trezor-suite. Do not use for any other purpose.
`, }, @@ -334,7 +334,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -343,7 +343,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -352,7 +352,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, }, }, { @@ -361,7 +361,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -370,7 +370,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

XPUB

upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q

1186.419755 FAKE

Confirmed
Total Received1186.41975501 FAKE
Total Sent0.00000001 FAKE
Final Balance1186.419755 FAKE
No. Transactions2
Used XPUB Addresses2
XPUB Addresses with Balance
AddressBalanceTxsPath
2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu1186.419755 FAKE1m/49'/1'/33'/1/3

Transactions

`, + `Trezor Fake Coin Explorer

XPUB

upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q

1186.419755 FAKE

Confirmed
Total Received1186.41975501 FAKE
Total Sent0.00000001 FAKE
Final Balance1186.419755 FAKE
No. Transactions2
Used XPUB Addresses2
XPUB Addresses with Balance
AddressBalanceTxsPath
2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu1186.419755 FAKE1m/49'/1'/33'/1/3

Transactions

`, }, }, { @@ -379,7 +379,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

XPUB

tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej

0 FAKE

Confirmed
Total Received0 FAKE
Total Sent0 FAKE
Final Balance0 FAKE
No. Transactions0
Used XPUB Addresses0
XPUB Addresses with Balance
No addresses
`, + `Trezor Fake Coin Explorer

XPUB

tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej

0 FAKE

Confirmed
Total Received0 FAKE
Total Sent0 FAKE
Final Balance0 FAKE
No. Transactions0
Used XPUB Addresses0
XPUB Addresses with Balance
No addresses
`, }, }, { @@ -388,7 +388,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Error

No matching records found for '1234'

`, + `Trezor Fake Coin Explorer

Error

No matching records found for '1234'

`, }, }, { @@ -397,7 +397,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Send Raw Transaction

`, + `Trezor Fake Coin Explorer

Send Raw Transaction

`, }, }, { @@ -406,7 +406,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Send Raw Transaction

Invalid data
`, + `Trezor Fake Coin Explorer

Send Raw Transaction

Invalid data
`, }, }, { diff --git a/static/css/main.min.1.css b/static/css/main.min.2.css similarity index 100% rename from static/css/main.min.1.css rename to static/css/main.min.2.css diff --git a/static/js/main.js b/static/js/main.js index 3b8eb37e30..3efffd4704 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -4,6 +4,9 @@ function syntaxHighlight(json) { .replace(/&/g, "&") .replace(//g, ">"); + if (json.length > 1000000) { + return `${json}`; + } return json.replace( /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, (match) => { diff --git a/static/js/main.min.1.js b/static/js/main.min.1.js deleted file mode 100644 index ed2ffb6fed..0000000000 --- a/static/js/main.min.1.js +++ /dev/null @@ -1 +0,0 @@ -function syntaxHighlight(t){return(t=(t=JSON.stringify(t,void 0,2)).replace(/&/g,"&").replace(//g,">")).replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,t=>{let e="number";return/^"/.test(t)?e=/:$/.test(t)?"key":"string":/true|false/.test(t)?e="boolean":/null/.test(t)&&(e="null"),`${t}`})}function getCoinCookie(){return document.cookie.split("; ").find(t=>t.startsWith("secondary_coin="))?.split("=")}function changeCSSStyle(t,e,l){let a=document.all?"rules":"cssRules";for(i=0,len=document.styleSheets[1][a].length;i`;if(a){let n=a.getAttribute("tm");n||(n="now"),s+=`${n}${a.outerHTML}
`}if(r&&(s+=`now${r.outerHTML}
`),e){let o=e.getAttribute("tm");o||(o="now"),s+=`${o}${e.outerHTML}
`}return l&&(s+=`now${l.outerHTML}
`),`${s}`}function addressAliasTooltip(){let t=this.getAttribute("alias-type"),e=this.getAttribute("cc");return`${t}
${e}
`}window.addEventListener("DOMContentLoaded",()=>{let t=getCoinCookie();t?.length===3&&("true"===t[2]&&(changeCSSStyle(".prim-amt","display","none"),changeCSSStyle(".sec-amt","display","initial")),document.querySelectorAll(".amt").forEach(t=>new bootstrap.Tooltip(t,{title:amountTooltip,html:!0}))),document.querySelectorAll("[alias-type]").forEach(t=>new bootstrap.Tooltip(t,{title:addressAliasTooltip,html:!0})),document.querySelectorAll("[tt]").forEach(t=>new bootstrap.Tooltip(t,{title:t.getAttribute("tt")})),document.querySelectorAll("#header .bb-group>.btn-check").forEach(t=>t.addEventListener("click",t=>{let e=getCoinCookie(),l="secondary-coin"===t.target.id;e?.length===3&&"true"===e[2]!==l&&(document.cookie=`${e[0]}=${e[1]}=${l}; Path=/`,changeCSSStyle(".prim-amt","display",l?"none":"initial"),changeCSSStyle(".sec-amt","display",l?"initial":"none"))})),document.querySelectorAll(".copyable").forEach(t=>t.addEventListener("click",t=>{if(t.clientXt.target.className=t.target.className.replace("copied","copyable"),1e3),t.preventDefault()}}))}); \ No newline at end of file diff --git a/static/js/main.min.2.js b/static/js/main.min.2.js new file mode 100644 index 0000000000..271d4598ff --- /dev/null +++ b/static/js/main.min.2.js @@ -0,0 +1 @@ +function syntaxHighlight(t){return(t=(t=JSON.stringify(t,void 0,2)).replace(/&/g,"&").replace(//g,">")).length>1e6?`${t}`:t.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,t=>{let e="number";return/^"/.test(t)?e=/:$/.test(t)?"key":"string":/true|false/.test(t)?e="boolean":/null/.test(t)&&(e="null"),`${t}`})}function getCoinCookie(){return document.cookie.split("; ").find(t=>t.startsWith("secondary_coin="))?.split("=")}function changeCSSStyle(t,e,l){let a=document.all?"rules":"cssRules";for(i=0,len=document.styleSheets[1][a].length;i`;if(a){let n=a.getAttribute("tm");n||(n="now"),r+=`${n}${a.outerHTML}
`}if(s&&(r+=`now${s.outerHTML}
`),e){let o=e.getAttribute("tm");o||(o="now"),r+=`${o}${e.outerHTML}
`}return l&&(r+=`now${l.outerHTML}
`),`${r}`}function addressAliasTooltip(){let t=this.getAttribute("alias-type"),e=this.getAttribute("cc");return`${t}
${e}
`}window.addEventListener("DOMContentLoaded",()=>{let t=getCoinCookie();t?.length===3&&("true"===t[2]&&(changeCSSStyle(".prim-amt","display","none"),changeCSSStyle(".sec-amt","display","initial")),document.querySelectorAll(".amt").forEach(t=>new bootstrap.Tooltip(t,{title:amountTooltip,html:!0}))),document.querySelectorAll("[alias-type]").forEach(t=>new bootstrap.Tooltip(t,{title:addressAliasTooltip,html:!0})),document.querySelectorAll("[tt]").forEach(t=>new bootstrap.Tooltip(t,{title:t.getAttribute("tt")})),document.querySelectorAll("#header .bb-group>.btn-check").forEach(t=>t.addEventListener("click",t=>{let e=getCoinCookie(),l="secondary-coin"===t.target.id;e?.length===3&&"true"===e[2]!==l&&(document.cookie=`${e[0]}=${e[1]}=${l}; Path=/`,changeCSSStyle(".prim-amt","display",l?"none":"initial"),changeCSSStyle(".sec-amt","display",l?"initial":"none"))})),document.querySelectorAll(".copyable").forEach(t=>t.addEventListener("click",t=>{if(t.clientXt.target.className=t.target.className.replace("copied","copyable"),1e3),t.preventDefault()}}))}); \ No newline at end of file From fed5fb3dac3e83d62fab57dcbfa67bf4b2276e37 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 27 Feb 2023 19:50:05 +0100 Subject: [PATCH 136/530] Store new contract info including the type of contract --- api/worker.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/worker.go b/api/worker.go index 3024a5e34f..c07d9f8a7a 100644 --- a/api/worker.go +++ b/api/worker.go @@ -609,6 +609,9 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom validContract = false } else { + if typeFromContext != bchain.UnknownTokenType && contractInfo.Type == bchain.UnknownTokenType { + contractInfo.Type = typeFromContext + } if err = w.db.StoreContractInfo(contractInfo); err != nil { glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) } From 4b3c82162956fb1f4114f2c457e5ddeb6f144280 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 28 Feb 2023 18:25:38 +0100 Subject: [PATCH 137/530] Compute values of internal transactions in balance history --- api/worker.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/api/worker.go b/api/worker.go index c07d9f8a7a..5f0873a3b8 100644 --- a/api/worker.go +++ b/api/worker.go @@ -1465,6 +1465,35 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s } } } + // process internal transactions + if eth.ProcessInternalTransactions { + internalData, err := w.db.GetEthereumInternalData(txid) + if err != nil { + return nil, err + } + if internalData != nil { + for i := range internalData.Transfers { + f := &internalData.Transfers[i] + txAddrDesc, err := w.chainParser.GetAddrDescFromAddress(f.From) + if err != nil { + return nil, err + } + if bytes.Equal(addrDesc, txAddrDesc) { + (*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &f.Value) + if f.From == f.To { + (*big.Int)(bh.SentToSelfSat).Add((*big.Int)(bh.SentToSelfSat), &f.Value) + } + } + txAddrDesc, err = w.chainParser.GetAddrDescFromAddress(f.To) + if err != nil { + return nil, err + } + if bytes.Equal(addrDesc, txAddrDesc) { + (*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &f.Value) + } + } + } + } } for i := range bchainTx.Vin { bchainVin := &bchainTx.Vin[i] From f237b82761d082d08a2be8c5549c24ec6485c24b Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 28 Feb 2023 19:04:58 +0100 Subject: [PATCH 138/530] Set Ethereum Goerli testnet symbol to tETH --- configs/coins/ethereum_testnet_goerli.json | 2 +- configs/coins/ethereum_testnet_goerli_archive.json | 2 +- configs/coins/ethereum_testnet_goerli_archive_consensus.json | 2 +- configs/coins/ethereum_testnet_goerli_consensus.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index b4bc2a5fe3..5335e63bb3 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli", - "shortcut": "tGOR", + "shortcut": "tETH", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli" }, diff --git a/configs/coins/ethereum_testnet_goerli_archive.json b/configs/coins/ethereum_testnet_goerli_archive.json index b444b33564..ff73353cf0 100644 --- a/configs/coins/ethereum_testnet_goerli_archive.json +++ b/configs/coins/ethereum_testnet_goerli_archive.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli Archive", - "shortcut": "tGOR", + "shortcut": "tETH", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli_archive" }, diff --git a/configs/coins/ethereum_testnet_goerli_archive_consensus.json b/configs/coins/ethereum_testnet_goerli_archive_consensus.json index 72ac980769..f93d016d65 100644 --- a/configs/coins/ethereum_testnet_goerli_archive_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_archive_consensus.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli Archive", - "shortcut": "tGOR", + "shortcut": "tETH", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli_archive_consensus", "execution_alias": "ethereum_testnet_goerli_archive" diff --git a/configs/coins/ethereum_testnet_goerli_consensus.json b/configs/coins/ethereum_testnet_goerli_consensus.json index 49d0fc821e..585cf2d25d 100644 --- a/configs/coins/ethereum_testnet_goerli_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_consensus.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli", - "shortcut": "tGOR", + "shortcut": "tETH", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli_consensus", "execution_alias": "ethereum_testnet_goerli" From d75680b3690f28b03b759414adbc1c606ffee65a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 28 Feb 2023 19:23:38 +0100 Subject: [PATCH 139/530] Security: Bump golang.org/x/net from 0.1.0 to 0.7.0 --- go.mod | 8 ++++---- go.sum | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index cf1a81bb93..aa12c96fc2 100644 --- a/go.mod +++ b/go.mod @@ -97,11 +97,11 @@ require ( go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 // indirect - golang.org/x/net v0.1.0 // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.1.0 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect diff --git a/go.sum b/go.sum index 8395980953..548d00ef43 100644 --- a/go.sum +++ b/go.sum @@ -542,6 +542,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -617,10 +619,14 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -631,6 +637,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 5652ca6b2ed862108f5e989e2c091a6ff93a4012 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 1 Mar 2023 17:29:03 +0100 Subject: [PATCH 140/530] Set Ethereum Goerli testnet symbol to tGOR for backward compatibility --- configs/coins/ethereum_testnet_goerli.json | 2 +- configs/coins/ethereum_testnet_goerli_archive.json | 2 +- configs/coins/ethereum_testnet_goerli_archive_consensus.json | 2 +- configs/coins/ethereum_testnet_goerli_consensus.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index 5335e63bb3..b4bc2a5fe3 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli", - "shortcut": "tETH", + "shortcut": "tGOR", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli" }, diff --git a/configs/coins/ethereum_testnet_goerli_archive.json b/configs/coins/ethereum_testnet_goerli_archive.json index ff73353cf0..b444b33564 100644 --- a/configs/coins/ethereum_testnet_goerli_archive.json +++ b/configs/coins/ethereum_testnet_goerli_archive.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli Archive", - "shortcut": "tETH", + "shortcut": "tGOR", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli_archive" }, diff --git a/configs/coins/ethereum_testnet_goerli_archive_consensus.json b/configs/coins/ethereum_testnet_goerli_archive_consensus.json index f93d016d65..72ac980769 100644 --- a/configs/coins/ethereum_testnet_goerli_archive_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_archive_consensus.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli Archive", - "shortcut": "tETH", + "shortcut": "tGOR", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli_archive_consensus", "execution_alias": "ethereum_testnet_goerli_archive" diff --git a/configs/coins/ethereum_testnet_goerli_consensus.json b/configs/coins/ethereum_testnet_goerli_consensus.json index 585cf2d25d..49d0fc821e 100644 --- a/configs/coins/ethereum_testnet_goerli_consensus.json +++ b/configs/coins/ethereum_testnet_goerli_consensus.json @@ -1,7 +1,7 @@ { "coin": { "name": "Ethereum Testnet Goerli", - "shortcut": "tETH", + "shortcut": "tGOR", "label": "Ethereum Goerli", "alias": "ethereum_testnet_goerli_consensus", "execution_alias": "ethereum_testnet_goerli" From a81420fc945aaea3e154dc049177f69d4c0826a0 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 2 Mar 2023 00:37:33 +0100 Subject: [PATCH 141/530] Improve parsing of ETH input data --- bchain/coins/eth/dataparser.go | 16 ++++--- bchain/coins/eth/dataparser_test.go | 67 ++++++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/bchain/coins/eth/dataparser.go b/bchain/coins/eth/dataparser.go index 060fcfca76..8182692658 100644 --- a/bchain/coins/eth/dataparser.go +++ b/bchain/coins/eth/dataparser.go @@ -76,7 +76,8 @@ func decamel(s string) string { b.WriteByte(' ') } b.WriteRune(v) - splittable = unicode.IsLower(v) || unicode.IsNumber(v) + // special handling of ETH to be able to convert "addETHToContract" to "Add ETH To Contract" + splittable = unicode.IsLower(v) || unicode.IsNumber(v) || (i >= 2 && s[i-2:i+1] == "ETH") } } return b.String() @@ -98,7 +99,7 @@ func GetSignatureFromData(data string) uint32 { const ErrorTy byte = 255 -func processParam(data string, index int, t *abi.Type, processed []bool) ([]string, int, bool) { +func processParam(data string, index int, dataOffset int, t *abi.Type, processed []bool) ([]string, int, bool) { var retval []string d := index << 6 if d+64 > len(data) { @@ -140,7 +141,7 @@ func processParam(data string, index int, t *abi.Type, processed []bool) ([]stri for i := 0; i < t.Size; i++ { var r []string var ok bool - r, index, ok = processParam(data, index, t.Elem, processed) + r, index, ok = processParam(data, index, dataOffset, t.Elem, processed) if !ok { return nil, 0, false } @@ -156,7 +157,7 @@ func processParam(data string, index int, t *abi.Type, processed []bool) ([]stri processed[index] = true index++ offset <<= 1 - d = int(offset) + d = int(offset) + dataOffset dynIndex := d >> 6 if d+64 > len(data) || d < 0 { return nil, 0, false @@ -195,10 +196,11 @@ func processParam(data string, index int, t *abi.Type, processed []bool) ([]stri } } } else { + newOffset := dataOffset + dynIndex<<6 for i := 0; i < count; i++ { var r []string var ok bool - r, dynIndex, ok = processParam(data, dynIndex, t.Elem, processed) + r, dynIndex, ok = processParam(data, dynIndex, newOffset, t.Elem, processed) if !ok { return nil, 0, false } @@ -222,7 +224,7 @@ func tryParseParams(data string, params []string, parsedParams []abi.Type) []bch var ok bool for i := range params { t := &parsedParams[i] - values, index, ok = processParam(data, index, t, processed) + values, index, ok = processParam(data, index, 0, t, processed) if !ok { return nil } @@ -244,7 +246,7 @@ func ParseInputData(signatures *[]bchain.FourByteSignature, data string) *bchain if len(data) <= 2 { // data is empty or 0x return &bchain.EthereumParsedInputData{Name: "Transfer"} } - if len(data) < 10 || (len(data)-10)%64 != 0 { + if len(data) < 10 { return nil } parsed := bchain.EthereumParsedInputData{ diff --git a/bchain/coins/eth/dataparser_test.go b/bchain/coins/eth/dataparser_test.go index 745e6b6262..b13ecd167b 100644 --- a/bchain/coins/eth/dataparser_test.go +++ b/bchain/coins/eth/dataparser_test.go @@ -112,7 +112,7 @@ func TestParseInputData(t *testing.T) { Parameters: []string{"address"}, }, { - Name: "addLiquidityETH", + Name: "addLiquidityETHToContract", Parameters: []string{"address", "uint256", "uint256", "uint256", "address", "uint256"}, }, { @@ -131,6 +131,10 @@ func TestParseInputData(t *testing.T) { Name: "transmitAndSellTokenForEth", Parameters: []string{"address", "uint256", "uint256", "uint256", "address", "(uint8,bytes32,bytes32)", "bytes"}, }, + { + Name: "execute", + Parameters: []string{"bytes", "bytes[]", "uint256"}, + }, } tests := []struct { name string @@ -191,13 +195,13 @@ func TestParseInputData(t *testing.T) { }, }, { - name: "addLiquidityETH", + name: "addLiquidityETHToContract", signatures: &signatures, data: "0xf305d719000000000000000000000000b80e5aaa2131c07568128f68b8538ed3c8951234000000000000000000000000000000000000007e37be2022c0914b2680000000000000000000000000000000000000000000007e37be2022c0914b26800000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000009f64b014ca26f2def573246543dd1115b229e4f400000000000000000000000000000000000000000000000000000000623f56f8", want: &bchain.EthereumParsedInputData{ MethodId: "0xf305d719", - Name: "Add Liquidity ETH", - Function: "addLiquidityETH(address, uint256, uint256, uint256, address, uint256)", + Name: "Add Liquidity ETH To Contract", + Function: "addLiquidityETHToContract(address, uint256, uint256, uint256, address, uint256)", Params: []bchain.EthereumParsedInputParam{ { Type: "address", @@ -227,7 +231,7 @@ func TestParseInputData(t *testing.T) { }, }, { - name: "addLiquidityETH data don't match - too long", + name: "addLiquidityETHToContract data don't match - too long", signatures: &signatures, data: "0xf305d719000000000000000000000000b80e5aaa2131c07568128f68b8538ed3c8951234000000000000000000000000000000000000007e37be2022c0914b2680000000000000000000000000000000000000000000007e37be2022c0914b26800000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000009f64b014ca26f2def573246543dd1115b229e4f400000000000000000000000000000000000000000000000000000000623f56f800000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", want: &bchain.EthereumParsedInputData{ @@ -235,7 +239,7 @@ func TestParseInputData(t *testing.T) { }, }, { - name: "addLiquidityETH data don't match - too short", + name: "addLiquidityETHToContract data don't match - too short", signatures: &signatures, data: "0xf305d719000000000000000000000000b80e5aaa2131c07568128f68b8538ed3c8951234000000000000000000000000000000000000007e37be2022c0914b2680000000000000000000000000000000000000000000007e37be2022c0914b26800000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000009f64b014ca26f2def573246543dd1115b229e4f4", want: &bchain.EthereumParsedInputData{ @@ -362,6 +366,57 @@ func TestParseInputData(t *testing.T) { }, }, }, + { + name: "execute", + signatures: &signatures, + data: "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000063fd167b00000000000000000000000000000000000000000000000000000000000000010800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000021e19e0c9bab2400000000000000000000000000000000000000000000000000000000000002fa5e9a300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cda4e840411c00a614ad9205caec807c7458a0e3000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + want: &bchain.EthereumParsedInputData{ + MethodId: "0x3593564c", + Name: "Execute", + Function: "execute(bytes, bytes[], uint256)", + Params: []bchain.EthereumParsedInputParam{ + { + Type: "bytes", + Values: []string{"0x08"}, + }, + { + Type: "bytes[]", + Values: []string{"0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000021e19e0c9bab2400000000000000000000000000000000000000000000000000000000000002fa5e9a300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cda4e840411c00a614ad9205caec807c7458a0e3000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"}, + }, + { + Type: "uint256", + Values: []string{"1677530747"}, + }, + }, + }, + }, + { + name: "execute2", + signatures: &signatures, + data: "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000063ffd82300000000000000000000000000000000000000000000000000000000000000020b080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000006f05b59d3b20000000000000000000000000000000000000000000000491478480c282e75df8b5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000f0f9d895aca5c8678f706fb8216fa22957685a13", + want: &bchain.EthereumParsedInputData{ + MethodId: "0x3593564c", + Name: "Execute", + Function: "execute(bytes, bytes[], uint256)", + Params: []bchain.EthereumParsedInputParam{ + { + Type: "bytes", + Values: []string{"0x0b08"}, + }, + { + Type: "bytes[]", + Values: []string{ + "0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000006f05b59d3b20000", + "0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000006f05b59d3b20000000000000000000000000000000000000000000000491478480c282e75df8b5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000f0f9d895aca5c8678f706fb8216fa22957685a13", + }, + }, + { + Type: "uint256", + Values: []string{"1677711395"}, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 86168f4c5e9357152572506eb46df0dec667114c Mon Sep 17 00:00:00 2001 From: blondfrogs <8285518+blondfrogs@users.noreply.github.com> Date: Wed, 1 Mar 2023 08:20:10 -0700 Subject: [PATCH 142/530] Update flux binary to latest release --- configs/coins/flux.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/coins/flux.json b/configs/coins/flux.json index 887f8fa8a9..e9f2d9257b 100644 --- a/configs/coins/flux.json +++ b/configs/coins/flux.json @@ -23,9 +23,9 @@ "package_revision": "satoshilabs-1", "system_user": "flux", "version": "6.0.0", - "binary_url": "https://github.com/RunOnFlux/fluxd/releases/download/v6.0.0/Flux-amd64-v6.0.0.tar.gz", + "binary_url": "https://github.com/RunOnFlux/fluxd/releases/download/v6.1.0/Flux-amd64-v6.1.0.tar.gz", "verification_type": "sha256", - "verification_source": "28717246a383018de8f6099a26afc3a4877da32f2d9531a3253b1664c22145e7", + "verification_source": "39461407a1b85b9dafc181e88dbd5274002ca36fcd976a8eeb735a900df6ad9a", "extract_command": "tar -C backend -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/fluxd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", From 10da32b24953004d175161cfc092b0595556180c Mon Sep 17 00:00:00 2001 From: justanwar <42809091+justanwar@users.noreply.github.com> Date: Fri, 3 Mar 2023 20:22:12 +0800 Subject: [PATCH 143/530] =?UTF-8?q?firo=200.14.11.1=20=E2=86=92=200.14.12.?= =?UTF-8?q?0=20(#855)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ara --- bchain/coins/firo/firoparser.go | 46 +++++++++++++++++++++++++-------- configs/coins/firo.json | 6 ++--- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/bchain/coins/firo/firoparser.go b/bchain/coins/firo/firoparser.go index 4bb800e2df..d2b9e1f0bd 100644 --- a/bchain/coins/firo/firoparser.go +++ b/bchain/coins/firo/firoparser.go @@ -21,6 +21,7 @@ const ( OpLelantusMint = 0xc5 OpLelantusJMint = 0xc6 OpLelantusJoinSplit = 0xc7 + OpLelantusJoinSplitPayload = 0xc9 MainnetMagic wire.BitcoinNet = 0xe3d9fef1 TestnetMagic wire.BitcoinNet = 0xcffcbeea @@ -122,6 +123,8 @@ func (p *FiroParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) return []string{"LelantusJMint"}, false, nil case OpLelantusJoinSplit: return []string{"LelantusJoinSplit"}, false, nil + case OpLelantusJoinSplitPayload: + return []string{"LelantusJoinSplit"}, false, nil } } @@ -170,7 +173,7 @@ func (p *FiroParser) ParseBlock(b []byte) (*bchain.Block, error) { } else { if isMTP(header) { mtpHeader := MTPBlockHeader{} - mtpHashData := MTPHashData{} + mtpHashDataRoot := MTPHashDataRoot{} // header err = binary.Read(reader, binary.LittleEndian, &mtpHeader) @@ -178,28 +181,46 @@ func (p *FiroParser) ParseBlock(b []byte) (*bchain.Block, error) { return nil, err } - // hash data - err = binary.Read(reader, binary.LittleEndian, &mtpHashData) + // hash data root + err = binary.Read(reader, binary.LittleEndian, &mtpHashDataRoot) if err != nil { return nil, err } - // proof - for i := 0; i < MTPL*3; i++ { - var numberProofBlocks uint8 + isAllZero := true + for i := 0; i < 16; i++ { + if mtpHashDataRoot.HashRootMTP[i] != 0 { + isAllZero = false + break + } + } + - err = binary.Read(reader, binary.LittleEndian, &numberProofBlocks) + if !isAllZero { + // hash data + mtpHashData := MTPHashData{} + err = binary.Read(reader, binary.LittleEndian, &mtpHashData) if err != nil { return nil, err } - for j := uint8(0); j < numberProofBlocks; j++ { - var mtpData [16]uint8 + // proof + for i := 0; i < MTPL*3; i++ { + var numberProofBlocks uint8 - err = binary.Read(reader, binary.LittleEndian, mtpData[:]) + err = binary.Read(reader, binary.LittleEndian, &numberProofBlocks) if err != nil { return nil, err } + + for j := uint8(0); j < numberProofBlocks; j++ { + var mtpData [16]uint8 + + err = binary.Read(reader, binary.LittleEndian, mtpData[:]) + if err != nil { + return nil, err + } + } } } } @@ -318,8 +339,11 @@ func isProgPow(h *wire.BlockHeader, isTestNet bool) bool { return isTestNet && epoch >= SwitchToProgPowBlockHeaderTestnet || !isTestNet && epoch >= SwitchToProgPowBlockHeaderMainnet } -type MTPHashData struct { +type MTPHashDataRoot struct { HashRootMTP [16]uint8 +} + +type MTPHashData struct { BlockMTP [128][128]uint64 } diff --git a/configs/coins/firo.json b/configs/coins/firo.json index 1e29446998..02b7d5629b 100644 --- a/configs/coins/firo.json +++ b/configs/coins/firo.json @@ -22,10 +22,10 @@ "package_name": "backend-firo", "package_revision": "satoshilabs-1", "system_user": "firo", - "version": "0.14.11.1", - "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.11.1/firo-0.14.11.1-linux64.tar.gz", + "version": "0.14.12.0", + "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.12.0/firo-0.14.12.0-linux64.tar.gz", "verification_type": "sha256", - "verification_source": "8669ae8ce3356deee2512a4da133eab347c704cf47c865caf9ea10b46ba8b477", + "verification_source": "47c7ae07f85189b6b11068848a5c8f930528e6edfff14fd3c6e6305a01e8da77", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/firo-qt", From 78cf3c264782e60a147031c6ae80b3ab1f704783 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Thu, 2 Mar 2023 14:04:10 +0000 Subject: [PATCH 144/530] ltc (+testnet) 0.21.2.1 -> 0.21.2.2 --- configs/coins/litecoin.json | 6 +++--- configs/coins/litecoin_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/litecoin.json b/configs/coins/litecoin.json index f965e4e813..2c785528b0 100644 --- a/configs/coins/litecoin.json +++ b/configs/coins/litecoin.json @@ -22,10 +22,10 @@ "package_name": "backend-litecoin", "package_revision": "satoshilabs-1", "system_user": "litecoin", - "version": "0.21.2.1", - "binary_url": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-x86_64-linux-gnu.tar.gz", + "version": "0.21.2.2", + "binary_url": "https://download.litecoin.org/litecoin-0.21.2.2/linux/litecoin-0.21.2.2-x86_64-linux-gnu.tar.gz", "verification_type": "gpg", - "verification_source": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-x86_64-linux-gnu.tar.gz.asc", + "verification_source": "https://download.litecoin.org/litecoin-0.21.2.2/linux/litecoin-0.21.2.2-x86_64-linux-gnu.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/litecoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/litecoind -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/litecoin_testnet.json b/configs/coins/litecoin_testnet.json index fb23dbde04..b239c9dd9c 100644 --- a/configs/coins/litecoin_testnet.json +++ b/configs/coins/litecoin_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-litecoin-testnet", "package_revision": "satoshilabs-1", "system_user": "litecoin", - "version": "0.21.2.1", - "binary_url": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-x86_64-linux-gnu.tar.gz", + "version": "0.21.2.2", + "binary_url": "https://download.litecoin.org/litecoin-0.21.2.2/linux/litecoin-0.21.2.2-x86_64-linux-gnu.tar.gz", "verification_type": "gpg", - "verification_source": "https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-x86_64-linux-gnu.tar.gz.asc", + "verification_source": "https://download.litecoin.org/litecoin-0.21.2.2/linux/litecoin-0.21.2.2-x86_64-linux-gnu.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/litecoin-qt" From 1bcdcc6a0a29eb839a9b08cb51653bc68edf2a4c Mon Sep 17 00:00:00 2001 From: kevin <35275952+kaladinlight@users.noreply.github.com> Date: Mon, 6 Mar 2023 15:26:55 -0700 Subject: [PATCH 145/530] fix: avalanche blst dependency issue (#883) --- bchain/coins/avalanche/avalancherpc.go | 25 ++++-- go.mod | 29 +------ go.sum | 110 ------------------------- 3 files changed, 19 insertions(+), 145 deletions(-) diff --git a/bchain/coins/avalanche/avalancherpc.go b/bchain/coins/avalanche/avalancherpc.go index c7f3d7ec1d..8e102529f6 100644 --- a/bchain/coins/avalanche/avalancherpc.go +++ b/bchain/coins/avalanche/avalancherpc.go @@ -6,7 +6,7 @@ import ( "fmt" "net/url" - "github.com/ava-labs/avalanchego/api/info" + jsontypes "github.com/ava-labs/avalanchego/utils/json" "github.com/ava-labs/coreth/core/types" "github.com/ava-labs/coreth/ethclient" "github.com/ava-labs/coreth/interfaces" @@ -27,7 +27,7 @@ const ( // AvalancheRPC is an interface to JSON-RPC avalanche service. type AvalancheRPC struct { *eth.EthereumRPC - info info.Client + info *rpc.Client } // NewAvalancheRPC returns new AvalancheRPC instance. @@ -56,6 +56,11 @@ func (b *AvalancheRPC) Initialize() error { return rc, c, nil } + rpcClient, client, err := b.OpenRPC(b.ChainConfig.RPCURL) + if err != nil { + return err + } + rpcUrl, err := url.Parse(b.ChainConfig.RPCURL) if err != nil { return err @@ -66,7 +71,7 @@ func (b *AvalancheRPC) Initialize() error { scheme = "https" } - rpcClient, client, err := b.OpenRPC(b.ChainConfig.RPCURL) + infoClient, err := rpc.DialHTTP(fmt.Sprintf("%s://%s/ext/info", scheme, rpcUrl.Host)) if err != nil { return err } @@ -74,7 +79,7 @@ func (b *AvalancheRPC) Initialize() error { // set chain specific b.Client = client b.RPC = rpcClient - b.info = info.NewClient(fmt.Sprintf("%s://%s", scheme, rpcUrl.Host)) + b.info = infoClient b.MainNetChainID = MainNet b.NewBlock = &AvalancheNewBlock{channel: make(chan *types.Header)} b.NewTx = &AvalancheNewTx{channel: make(chan common.Hash)} @@ -112,9 +117,15 @@ func (b *AvalancheRPC) GetChainInfo() (*bchain.ChainInfo, error) { ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) defer cancel() - v, err := b.info.GetNodeVersion(ctx) - if err != nil { - fmt.Println("here", err) + var v struct { + Version string `json:"version"` + DatabaseVersion string `json:"databaseVersion"` + RPCProtocolVersion jsontypes.Uint32 `json:"rpcProtocolVersion"` + GitCommit string `json:"gitCommit"` + VMVersions map[string]string `json:"vmVersions"` + } + + if err := b.info.CallContext(ctx, &v, "info.getNodeVersion"); err != nil { return nil, err } diff --git a/go.mod b/go.mod index aa12c96fc2..8426e518d0 100644 --- a/go.mod +++ b/go.mod @@ -40,14 +40,12 @@ require ( ) require ( - github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29 // indirect github.com/VictoriaMetrics/fastcache v1.10.0 // indirect github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dchest/siphash v1.2.1 // indirect @@ -59,54 +57,29 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/decred/dcrd/wire v1.4.0 // indirect github.com/decred/slog v1.1.0 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.2.0 // indirect - github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/rpc v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0 // indirect github.com/holiman/uint256 v1.2.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d // indirect + github.com/onsi/ginkgo v1.16.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/rjeczalik/notify v0.9.2 // indirect - github.com/rs/cors v1.7.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/stretchr/testify v1.8.1 // indirect - github.com/supranational/blst v0.3.11-0.20220920110316-f72618070295 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opentelemetry.io/otel v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0 // indirect - go.opentelemetry.io/otel/sdk v1.11.0 // indirect - go.opentelemetry.io/otel/trace v1.11.0 // indirect - go.opentelemetry.io/proto/otlp v0.19.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - go.uber.org/zap v1.24.0 // indirect golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 // indirect golang.org/x/net v0.7.0 // indirect - golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect - gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect - google.golang.org/grpc v1.50.1 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 548d00ef43..812d5af7f6 100644 --- a/go.sum +++ b/go.sum @@ -31,14 +31,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c h1:8bYNmjELeCj7DEh/dN7zFzkJ0upK3GkbOC/0u1HMQ5s= github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c/go.mod h1:DwgC62sAn4RgH4L+O8REgcE7f0XplHPNeRYFy+ffy1M= -github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29 h1:B11BryeZQ1LrAzzM0lCpblwleB7SyxPfvN2AsNbyvQc= github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29/go.mod h1:+39XiGr9m9TPY49sG4XIH5CVaRxHGFWT0U4MOY6dy3o= github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY= @@ -53,12 +49,10 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/ava-labs/avalanchego v1.9.7 h1:f2vS8jUBZmrqPcfU5NEa7dSHXbKfTB0EyjcCyvqxqPw= github.com/ava-labs/avalanchego v1.9.7/go.mod h1:ckdSQHeoRN6PmQ3TLgWAe6Kh9tFpU4Lu6MgDW4GrU/Q= github.com/ava-labs/coreth v0.11.6 h1:kMCHfb37k4UyxkHwoUuciXC92eyIeowB/EKv15XKQ6s= github.com/ava-labs/coreth v0.11.6/go.mod h1:xgjjJdl50zhHlWPP+3Ux5LxfvFcbSG60tGK6QUkFDhI= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -79,11 +73,8 @@ github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -92,12 +83,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -144,19 +129,14 @@ github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s= github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -168,11 +148,6 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= @@ -225,7 +200,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -237,23 +211,15 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0 h1:kr3j8iIMR4ywO/O0rvksXaJvauGGCMg2zAZIiNZ9uIQ= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0/go.mod h1:ummNFgdgLhhX7aIiy35vVmQNS0rWXknfPE0qe6fmFXg= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -264,7 +230,6 @@ github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -325,23 +290,17 @@ github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E= -github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= github.com/pebbe/zmq4 v1.2.1 h1:jrXQW3mD8Si2mcSY/8VBs2nNkK/sKCOEM0rHAfxyc8c= github.com/pebbe/zmq4 v1.2.1/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM= @@ -383,10 +342,8 @@ github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0ua github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic= github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a h1:q2+wHBv8gDQRRPfxvRez8etJUp9VNnBDQhiUW4W5AKg= github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a/go.mod h1:FdhEqBlgflrdbBs+Wh94EXSNJT+s6DTVvsHGMo0+u80= @@ -395,7 +352,6 @@ github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMT github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 h1:Oo2KZNP70KE0+IUJSidPj/BFS/RXNHmKIJOdckzml2E= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -405,16 +361,11 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/supranational/blst v0.3.11-0.20220920110316-f72618070295 h1:rVKS9JjtqE4/PscoIsP46sRnJhfq8YFbjlk0fUJTRnY= -github.com/supranational/blst v0.3.11-0.20220920110316-f72618070295/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= -github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= @@ -434,31 +385,6 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/otel v1.11.0 h1:kfToEGMDq6TrVrJ9Vht84Y8y9enykSZzDDZglV0kIEk= -go.opentelemetry.io/otel v1.11.0/go.mod h1:H2KtuEphyMvlhZ+F7tg9GRhAOe60moNx61Ex+WmiKkk= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0 h1:0dly5et1i/6Th3WHn0M6kYiJfFNzhhxanrJ0bOfnjEo= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0/go.mod h1:+Lq4/WkdCkjbGcBMVHHg2apTbv8oMBf29QCnyCCJjNQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0 h1:eyJ6njZmH16h9dOKCi7lMswAnGsSOwgTqWzfxqcuNr8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0/go.mod h1:FnDp7XemjN3oZ3xGunnfOUTVwd2XcvLbtRAuOSU3oc8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0 h1:j2RFV0Qdt38XQ2Jvi4WIsQ56w8T7eSirYbMw19VXRDg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0/go.mod h1:pILgiTEtrqvZpoiuGdblDgS5dbIaTgDrkIuKfEFkt+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0 h1:v29I/NbVp7LXQYMFZhU6q17D0jSEbYOAVONlrO1oH5s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0/go.mod h1:/RpLsmbQLDO1XCbWAM4S6TSwj8FKwwgyKKyqtvVfAnw= -go.opentelemetry.io/otel/sdk v1.11.0 h1:ZnKIL9V9Ztaq+ME43IUi/eo22mNsb6a7tGfzaOWB5fo= -go.opentelemetry.io/otel/sdk v1.11.0/go.mod h1:REusa8RsyKaq0OlyangWXaw97t2VogoO4SSEeKkSTAk= -go.opentelemetry.io/otel/trace v1.11.0 h1:20U/Vj42SX+mASlXLmSGBg6jpI1jQtv682lZtTAOVFI= -go.opentelemetry.io/otel/trace v1.11.0/go.mod h1:nyYjis9jy0gytE9LXGU+/m1sHTKbRY0fX0hulNNDP1U= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -535,13 +461,9 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -550,7 +472,6 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -564,7 +485,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -615,30 +535,18 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -690,9 +598,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -738,16 +643,12 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c h1:QgY/XxIAIeccR+Ca/rDdKubLIU9rcJ3xfy1DC/Wd2Oo= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -760,12 +661,6 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -778,7 +673,6 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -790,8 +684,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -799,14 +691,12 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 9761ad744519b457f14e21e8d41847aabfd6c793 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Wed, 8 Mar 2023 09:39:56 +0000 Subject: [PATCH 146/530] =?UTF-8?q?eth=20(+testnets)=201.11.2=20=E2=86=92?= =?UTF-8?q?=201.11.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 6 +++--- configs/coins/ethereum_archive.json | 6 +++--- configs/coins/ethereum_testnet_goerli.json | 6 +++--- configs/coins/ethereum_testnet_goerli_archive.json | 6 +++--- configs/coins/ethereum_testnet_sepolia.json | 6 +++--- configs/coins/ethereum_testnet_sepolia_archive.json | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index e32587656b..aa32d2194d 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.2-73b01f40", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", + "version": "1.11.3-5ed08c47", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 4ba2ac43fb..984d5123a5 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.2-73b01f40", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", + "version": "1.11.3-5ed08c47", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli.json b/configs/coins/ethereum_testnet_goerli.json index b4bc2a5fe3..93c787b58b 100644 --- a/configs/coins/ethereum_testnet_goerli.json +++ b/configs/coins/ethereum_testnet_goerli.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-goerli", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.2-73b01f40", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", + "version": "1.11.3-5ed08c47", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_goerli_archive.json b/configs/coins/ethereum_testnet_goerli_archive.json index b444b33564..391ddade51 100644 --- a/configs/coins/ethereum_testnet_goerli_archive.json +++ b/configs/coins/ethereum_testnet_goerli_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-goerli-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.2-73b01f40", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", + "version": "1.11.3-5ed08c47", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --goerli --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 268fac2e43..f99da612ea 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.2-73b01f40", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", + "version": "1.11.3-5ed08c47", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --sepolia --syncmode full --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 8eb272bf45..02e9e71f6f 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "1.11.2-73b01f40", - "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz", + "version": "1.11.3-5ed08c47", + "binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz", "verification_type": "gpg", - "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.2-73b01f40.tar.gz.asc", + "verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --sepolia --syncmode full --gcmode archive --txlookuplimit 0 --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port {{.Ports.BackendP2P}} --ws --ws.addr 127.0.0.1 --ws.port {{.Ports.BackendRPC}} --ws.origins \"*\" --ws.api \"eth,net,web3,debug,txpool\" --http --http.port {{.Ports.BackendHttp}} -http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", From a83d0734985f511dbc99b23c6ca2c0bdd833e852 Mon Sep 17 00:00:00 2001 From: kevin <35275952+kaladinlight@users.noreply.github.com> Date: Wed, 8 Mar 2023 02:58:48 -0700 Subject: [PATCH 147/530] upgrade avalanche v1.9.7 -> v1.9.11 (#884) --- build/docker/bin/Makefile | 1 + configs/coins/avalanche.json | 10 ++--- configs/coins/avalanche_archive.json | 10 ++--- go.mod | 24 ++++++------ go.sum | 57 ++++++++++------------------ 5 files changed, 44 insertions(+), 58 deletions(-) diff --git a/build/docker/bin/Makefile b/build/docker/bin/Makefile index 9111e24e0b..cc3b888e19 100644 --- a/build/docker/bin/Makefile +++ b/build/docker/bin/Makefile @@ -39,3 +39,4 @@ prepare-sources: cp -r /src $(BLOCKBOOK_SRC) cd $(BLOCKBOOK_SRC) && go mod download sed -i 's/wsMessageSizeLimit\ =\ 15\ \*\ 1024\ \*\ 1024/wsMessageSizeLimit = 50 * 1024 * 1024/g' $(GOPATH)/pkg/mod/github.com/ethereum/go-ethereum*/rpc/websocket.go + sed -i 's/wsMessageSizeLimit\ =\ 15\ \*\ 1024\ \*\ 1024/wsMessageSizeLimit = 50 * 1024 * 1024/g' $(GOPATH)/pkg/mod/github.com/ava-labs/coreth*/rpc/websocket.go diff --git a/configs/coins/avalanche.json b/configs/coins/avalanche.json index 110c16700d..d3b9c9b9d2 100644 --- a/configs/coins/avalanche.json +++ b/configs/coins/avalanche.json @@ -19,10 +19,10 @@ "package_name": "backend-avalanche", "package_revision": "satoshilabs-1", "system_user": "avalanche", - "version": "1.9.7", - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.7/avalanchego-linux-amd64-v1.9.7.tar.gz", + "version": "1.9.11", + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz", "verification_type": "gpg", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.7/avalanchego-linux-amd64-v1.9.7.tar.gz.sig", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz.sig", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMEtmUT09IgogIH0KfQ==", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.7/avalanchego-linux-arm64-v1.9.7.tar.gz", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.7/avalanchego-linux-arm64-v1.9.7.tar.gz.sig" + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz.sig" } } }, diff --git a/configs/coins/avalanche_archive.json b/configs/coins/avalanche_archive.json index ca1db11a8c..58cc42e8ba 100644 --- a/configs/coins/avalanche_archive.json +++ b/configs/coins/avalanche_archive.json @@ -19,10 +19,10 @@ "package_name": "backend-avalanche-archive", "package_revision": "satoshilabs-1", "system_user": "avalanche", - "version": "1.9.7", - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.7/avalanchego-linux-amd64-v1.9.7.tar.gz", + "version": "1.9.11", + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz", "verification_type": "gpg", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.7/avalanchego-linux-amd64-v1.9.7.tar.gz.sig", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz.sig", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMHNDaUFnSW5CeWRXNXBibWN0Wlc1aFlteGxaQ0k2Wm1Gc2MyVUtmUT09IgogIH0KfQ==", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.7/avalanchego-linux-arm64-v1.9.7.tar.gz", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.7/avalanchego-linux-arm64-v1.9.7.tar.gz.sig" + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz.sig" } } }, diff --git a/go.mod b/go.mod index 8426e518d0..a47d5548bd 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,9 @@ module github.com/trezor/blockbook go 1.19 require ( - github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c // indirect - github.com/ava-labs/avalanchego v1.9.7 - github.com/ava-labs/coreth v0.11.6 + github.com/ava-labs/avalanchego v1.9.11 + github.com/ava-labs/coreth v0.11.8 github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e - github.com/dchest/blake256 v1.0.0 // indirect github.com/deckarep/golang-set v1.8.0 github.com/decred/dcrd/chaincfg/chainhash v1.0.2 github.com/decred/dcrd/chaincfg/v3 v3.0.0 @@ -21,14 +19,11 @@ require ( github.com/golang/protobuf v1.5.2 github.com/gorilla/websocket v1.4.2 github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 - github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect - github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect github.com/linxGnu/grocksdb v1.7.7 github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe github.com/martinboehm/btcd v0.0.0-20221101112928-408689e15809 github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde - github.com/mr-tron/base58 v1.2.0 // indirect github.com/pebbe/zmq4 v1.2.1 github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29 github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e @@ -36,10 +31,10 @@ require ( github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d google.golang.org/protobuf v1.28.1 - gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect ) require ( + github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c // indirect github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29 // indirect github.com/VictoriaMetrics/fastcache v1.10.0 // indirect github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect @@ -48,13 +43,14 @@ require ( github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dchest/blake256 v1.0.0 // indirect github.com/dchest/siphash v1.2.1 // indirect github.com/decred/base58 v1.0.3 // indirect github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect github.com/decred/dcrd/crypto/ripemd160 v1.0.1 // indirect github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/decred/dcrd/wire v1.4.0 // indirect github.com/decred/slog v1.1.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect @@ -63,23 +59,27 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.2.0 // indirect github.com/gorilla/rpc v1.2.0 // indirect + github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e // indirect github.com/holiman/uint256 v1.2.0 // indirect + github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect + github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/rjeczalik/notify v0.9.2 // indirect + github.com/rjeczalik/notify v0.9.3 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/stretchr/testify v1.8.1 // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 // indirect - golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 812d5af7f6..d7bff8ffe4 100644 --- a/go.sum +++ b/go.sum @@ -49,10 +49,10 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/ava-labs/avalanchego v1.9.7 h1:f2vS8jUBZmrqPcfU5NEa7dSHXbKfTB0EyjcCyvqxqPw= -github.com/ava-labs/avalanchego v1.9.7/go.mod h1:ckdSQHeoRN6PmQ3TLgWAe6Kh9tFpU4Lu6MgDW4GrU/Q= -github.com/ava-labs/coreth v0.11.6 h1:kMCHfb37k4UyxkHwoUuciXC92eyIeowB/EKv15XKQ6s= -github.com/ava-labs/coreth v0.11.6/go.mod h1:xgjjJdl50zhHlWPP+3Ux5LxfvFcbSG60tGK6QUkFDhI= +github.com/ava-labs/avalanchego v1.9.11 h1:5hXHJMvErfaolWD7Hw9gZaVylck2shBaV/2NTHA0BfA= +github.com/ava-labs/avalanchego v1.9.11/go.mod h1:nNc+4JCIJMaEt2xRmeMVAUyQwDIap7RvnMrfWD2Tpo8= +github.com/ava-labs/coreth v0.11.8 h1:YFyDs3EwkzkSlgHF2gdsX5gFvY0EcwgZ81aPcXb5BXs= +github.com/ava-labs/coreth v0.11.8/go.mod h1:pc44yvJD4jTPIwkPI64pUXyJDvQ/UAqkbmhXOx78PXA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -84,6 +84,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -110,8 +111,8 @@ github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1 h1:V6eqU1crZzuoFT4KG2LhaU5xDSdkHu github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1/go.mod h1:d0H8xGMWbiIQP7gN3v2rByWUcuZPm9YsgmnfoxgbINc= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 h1:sgNeV1VRMDzs6rzyPpxyM0jp317hnwiq58Filgag2xw= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/decred/dcrd/dcrjson/v3 v3.0.1 h1:b9cpplNJG+nutE2jS8K/BtSGIJihEQHhFjFAsvJF/iI= github.com/decred/dcrd/dcrjson/v3 v3.0.1/go.mod h1:fnTHev/ABGp8IxFudDhjGi9ghLiXRff1qZz/wvq12Mg= github.com/decred/dcrd/dcrutil/v3 v3.0.0 h1:n6uQaTQynIhCY89XsoDk2WQqcUcnbD+zUM9rnZcIOZo= @@ -133,9 +134,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s= github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -152,7 +150,6 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -224,9 +221,12 @@ github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpx github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= +github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw= +github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -255,11 +255,13 @@ github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc/go.mod h1:J+Gs4SYgM6 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/linxGnu/grocksdb v1.7.7 h1:b6o8gagb4FL+P55qUzPchBR/C0u1lWjJOWQSWbhvTWg= github.com/linxGnu/grocksdb v1.7.7/go.mod h1:0hTf+iA+GOr0jDX4CgIYyJZxqOH9XlBh6KVj8+zmF34= github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe h1:khZWpHuxJNh2EGzBbaS6EQ2d6KxgK31WeG0TnlTMUD4= @@ -290,17 +292,10 @@ github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= github.com/pebbe/zmq4 v1.2.1 h1:jrXQW3mD8Si2mcSY/8VBs2nNkK/sKCOEM0rHAfxyc8c= github.com/pebbe/zmq4 v1.2.1/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM= @@ -308,6 +303,7 @@ github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29 h1:awILOe github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29/go.mod h1:ATZjpmb9u55Kcrd5M/ca/40H73BZLhduMzCmGwpfWw0= github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e h1:WrnL52yXO0jNpHC7UbthJl9mnHPHY7bW3xzmWIuWzh8= github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e/go.mod h1:y/B3gomTdd1s23RvcBij/X738fcTobeupT30EhV6nPE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -340,9 +336,11 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic= -github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= -github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= +github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY= +github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a h1:q2+wHBv8gDQRRPfxvRez8etJUp9VNnBDQhiUW4W5AKg= @@ -360,7 +358,6 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= @@ -376,7 +373,6 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= @@ -431,7 +427,6 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -454,18 +449,15 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -481,13 +473,11 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -499,11 +489,8 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -522,9 +509,7 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -592,7 +577,6 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -681,6 +665,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= From 425c8a5d12a3c5dd55807080f551fd1832225d7b Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 4 Feb 2023 23:13:53 +0100 Subject: [PATCH 148/530] Enable subscription to all mempool txs for Bitcoin --- configs/coins/bitcoin.json | 2 +- configs/coins/bitcoin_testnet.json | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index 996b5f1e53..4a9cf0c107 100644 --- a/configs/coins/bitcoin.json +++ b/configs/coins/bitcoin.json @@ -53,7 +53,7 @@ "internal_binding_template": ":{{.Ports.BlockbookInternal}}", "public_binding_template": ":{{.Ports.BlockbookPublic}}", "explorer_url": "", - "additional_params": "-dbcache=1073741824", + "additional_params": "-dbcache=1073741824 -enablesubnewtx", "block_chain": { "parse": true, "mempool_workers": 8, diff --git a/configs/coins/bitcoin_testnet.json b/configs/coins/bitcoin_testnet.json index 13546f6b2f..ca1bad51b1 100644 --- a/configs/coins/bitcoin_testnet.json +++ b/configs/coins/bitcoin_testnet.json @@ -27,9 +27,7 @@ "verification_type": "sha256", "verification_source": "49df6e444515d457ea0b885d66f521f2a26ca92ccf73d5296082e633544253bf", "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/bitcoin-qt" - ], + "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -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/testnet3/*.log", "postinst_script_template": "", @@ -55,7 +53,7 @@ "internal_binding_template": ":{{.Ports.BlockbookInternal}}", "public_binding_template": ":{{.Ports.BlockbookPublic}}", "explorer_url": "", - "additional_params": "", + "additional_params": "-enablesubnewtx", "block_chain": { "parse": true, "mempool_workers": 8, From 6626f330e9b7cb9b32a117bc44b5a4ac6b6e3c4d Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 5 Feb 2023 18:16:49 +0100 Subject: [PATCH 149/530] Add getBlock websocket endpoint --- server/public_test.go | 10 ++++++++++ server/websocket.go | 18 ++++++++++++++++++ static/test-websocket.html | 24 ++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/server/public_test.go b/server/public_test.go index 7957f5d5d6..ad4ec906cd 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -1435,6 +1435,16 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { }, want: `{"id":"39","data":{"subscribed":false,"message":"unsubscribeNewTransaction not enabled, use -enablesubnewtx flag to enable."}}`, }, + { + name: "websocket getBlock", + req: websocketReq{ + Method: "getBlock", + Params: map[string]interface{}{ + "id": "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6", + }, + }, + want: `{"id":"40","data":{"page":1,"totalPages":1,"itemsOnPage":100000,"hash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","previousBlockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","height":225494,"confirmations":1,"size":2345678,"time":1521595678,"version":0,"merkleRoot":"","nonce":"","bits":"","difficulty":"","txCount":4,"txs":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","vin":[{"n":0,"addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true,"value":"1234567890123"},{"n":1,"addresses":["mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"],"isAddress":true,"value":"12345"}],"vout":[{"value":"317283951061","n":0,"spent":true,"addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true},{"value":"917283951061","n":1,"addresses":["mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL"],"isAddress":true},{"value":"0","n":2,"addresses":["OP_RETURN 2020f1686f6a20"],"isAddress":false}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"1234567902122","valueIn":"1234567902468","fees":"346"},{"txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","vin":[{"n":0,"addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true,"value":"317283951061"},{"n":1,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"value":"1"}],"vout":[{"value":"118641975500","n":0,"addresses":["2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu"],"isAddress":true},{"value":"198641975500","n":1,"addresses":["mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"317283951000","valueIn":"317283951062","fees":"62"},{"txid":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","vin":[{"n":0,"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true,"value":"9876"}],"vout":[{"value":"9000","n":0,"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"9000","valueIn":"9876","fees":"876"},{"txid":"fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db","vin":[{"n":0,"isAddress":false,"value":"0"}],"vout":[{"value":"1360030331","n":0,"addresses":["mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj"],"isAddress":true},{"value":"0","n":1,"addresses":[],"isAddress":false}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"1360030331","valueIn":"0","fees":"0"}]}}`, + }, } // send all requests at once diff --git a/server/websocket.go b/server/websocket.go index facaaa42df..1afac62e36 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -291,6 +291,16 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs } return }, + "getBlock": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { + r := struct { + Id string `json:"id"` + }{} + err = json.Unmarshal(req.Params, &r) + if err == nil { + rv, err = s.getBlock(r.Id) + } + return + }, "getAccountUtxo": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { r := struct { Descriptor string `json:"descriptor"` @@ -616,6 +626,14 @@ func (s *WebsocketServer) getBlockHash(height int) (interface{}, error) { }, nil } +func (s *WebsocketServer) getBlock(id string) (interface{}, error) { + block, err := s.api.GetBlock(id, 0, 100000) + if err != nil { + return nil, err + } + return block, nil +} + func (s *WebsocketServer) estimateFee(c *websocketChannel, params []byte) (interface{}, error) { type estimateFeeReq struct { Blocks []int `json:"blocks"` diff --git a/static/test-websocket.html b/static/test-websocket.html index 99d3bc3fcc..ac8d573304 100644 --- a/static/test-websocket.html +++ b/static/test-websocket.html @@ -133,6 +133,17 @@ }); } + function getBlock() { + const method = "getBlock"; + const id = document.getElementById("getBlockId").value; + const params = { + id, + }; + send(method, params, function (result) { + document.getElementById("getBlockResult").innerText = JSON.stringify(result).replace(/,/g, ", "); + }); + } + function getAccountInfo() { const descriptor = document.getElementById('getAccountInfoDescriptor').value.trim(); const selectDetails = document.getElementById('getAccountInfoDetails'); @@ -456,6 +467,19 @@

Blockbook Websocket Test Page

+
+
+ +
+
+ +
+
+
+
+
+
+
From d52832f6f7e8cfce0812bfe6c64673d017d7f5a1 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 6 Feb 2023 00:04:35 +0100 Subject: [PATCH 150/530] Add extended index option - spendingTxid --- api/worker.go | 32 +++++++-- blockbook.go | 4 +- common/internalstate.go | 3 +- db/rocksdb.go | 97 +++++++++++++++++++-------- db/rocksdb_test.go | 101 +++++++++++++++++++++++++++-- fiat/fiat_rates_test.go | 2 +- server/public_ethereumtype_test.go | 2 +- server/public_test.go | 96 +++++++++++++++++++++++++-- tests/sync/sync.go | 2 +- 9 files changed, 291 insertions(+), 48 deletions(-) diff --git a/api/worker.go b/api/worker.go index 5f0873a3b8..005481a1e9 100644 --- a/api/worker.go +++ b/api/worker.go @@ -101,6 +101,19 @@ func (w *Worker) setSpendingTxToVout(vout *Vout, txid string, height uint32) err // GetSpendingTxid returns transaction id of transaction that spent given output func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) { + if w.db.HasExtendedIndex() { + tsp, err := w.db.GetTxAddresses(txid) + if err != nil { + return "", err + } else if tsp == nil { + glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses") + return "", NewAPIError(fmt.Sprintf("Txid %v not found", txid), false) + } + if n >= len(tsp.Outputs) || n < 0 { + return "", NewAPIError(fmt.Sprintf("Passed incorrect vout index %v for tx %v, len vout %v", n, txid, len(tsp.Outputs)), false) + } + return tsp.Outputs[n].SpentTxid, nil + } start := time.Now() tx, err := w.getTransaction(txid, false, false, nil) if err != nil { @@ -368,10 +381,16 @@ func (w *Worker) getTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe aggregateAddresses(addresses, vout.Addresses, vout.IsAddress) if ta != nil { vout.Spent = ta.Outputs[i].Spent - if spendingTxs && vout.Spent { - err = w.setSpendingTxToVout(vout, bchainTx.Txid, uint32(height)) - if err != nil { - glog.Errorf("setSpendingTxToVout error %v, %v, output %v", err, vout.AddrDesc, vout.N) + if vout.Spent { + if w.db.HasExtendedIndex() { + vout.SpentTxID = ta.Outputs[i].SpentTxid + vout.SpentIndex = int(ta.Outputs[i].SpentIndex) + vout.SpentHeight = int(ta.Outputs[i].SpentHeight) + } else if spendingTxs { + err = w.setSpendingTxToVout(vout, bchainTx.Txid, uint32(height)) + if err != nil { + glog.Errorf("setSpendingTxToVout error %v, %v, output %v", err, vout.AddrDesc, vout.N) + } } } } @@ -839,6 +858,11 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn glog.Errorf("tai.Addresses error %v, tx %v, output %v, tao %+v", err, txid, i, tao) } vout.Spent = tao.Spent + if vout.Spent && w.db.HasExtendedIndex() { + vout.SpentTxID = tao.SpentTxid + vout.SpentIndex = int(tao.SpentIndex) + vout.SpentHeight = int(tao.SpentHeight) + } aggregateAddresses(addresses, vout.Addresses, vout.IsAddress) } // for coinbase transactions valIn is 0 diff --git a/blockbook.go b/blockbook.go index aac9696498..e9442f3b76 100644 --- a/blockbook.go +++ b/blockbook.go @@ -84,6 +84,8 @@ var ( // resync mempool at least each resyncMempoolPeriodMs (could be more often if invoked by message from ZeroMQ) resyncMempoolPeriodMs = flag.Int("resyncmempoolperiod", 60017, "resync mempool period in milliseconds") + + extendedIndex = flag.Bool("extendedindex", false, "if true, create index of input txids and spending transactions") ) var ( @@ -172,7 +174,7 @@ func mainWithExitCode() int { return exitCodeFatal } - index, err = db.NewRocksDB(*dbPath, *dbCache, *dbMaxOpenFiles, chain.GetChainParser(), metrics) + index, err = db.NewRocksDB(*dbPath, *dbCache, *dbMaxOpenFiles, chain.GetChainParser(), metrics, *extendedIndex) if err != nil { glog.Error("rocksDB: ", err) return exitCodeFatal diff --git a/common/internalstate.go b/common/internalstate.go index 3e65d9a38e..f12f472944 100644 --- a/common/internalstate.go +++ b/common/internalstate.go @@ -58,7 +58,8 @@ type InternalState struct { CoinLabel string `json:"coinLabel"` Host string `json:"host"` - DbState uint32 `json:"dbState"` + DbState uint32 `json:"dbState"` + ExtendedIndex bool `json:"extendedIndex"` LastStore time.Time `json:"lastStore"` diff --git a/db/rocksdb.go b/db/rocksdb.go index 783cdd120e..72aeebf923 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -59,17 +59,18 @@ const ( // RocksDB handle type RocksDB struct { - path string - db *grocksdb.DB - wo *grocksdb.WriteOptions - ro *grocksdb.ReadOptions - cfh []*grocksdb.ColumnFamilyHandle - chainParser bchain.BlockChainParser - is *common.InternalState - metrics *common.Metrics - cache *grocksdb.Cache - maxOpenFiles int - cbs connectBlockStats + path string + db *grocksdb.DB + wo *grocksdb.WriteOptions + ro *grocksdb.ReadOptions + cfh []*grocksdb.ColumnFamilyHandle + chainParser bchain.BlockChainParser + is *common.InternalState + metrics *common.Metrics + cache *grocksdb.Cache + maxOpenFiles int + cbs connectBlockStats + extendedIndex bool } const ( @@ -126,7 +127,7 @@ func openDB(path string, c *grocksdb.Cache, openFiles int) (*grocksdb.DB, []*gro // NewRocksDB opens an internal handle to RocksDB environment. Close // needs to be called to release it. -func NewRocksDB(path string, cacheSize, maxOpenFiles int, parser bchain.BlockChainParser, metrics *common.Metrics) (d *RocksDB, err error) { +func NewRocksDB(path string, cacheSize, maxOpenFiles int, parser bchain.BlockChainParser, metrics *common.Metrics, extendedIndex bool) (d *RocksDB, err error) { glog.Infof("rocksdb: opening %s, required data version %v, cache size %v, max open files %v", path, dbVersion, cacheSize, maxOpenFiles) cfNames = append([]string{}, cfBaseNames...) @@ -135,6 +136,7 @@ func NewRocksDB(path string, cacheSize, maxOpenFiles int, parser bchain.BlockCha cfNames = append(cfNames, cfNamesBitcoinType...) } else if chainType == bchain.ChainEthereumType { cfNames = append(cfNames, cfNamesEthereumType...) + extendedIndex = false } else { return nil, errors.New("Unknown chain type") } @@ -146,7 +148,7 @@ func NewRocksDB(path string, cacheSize, maxOpenFiles int, parser bchain.BlockCha } wo := grocksdb.NewDefaultWriteOptions() ro := grocksdb.NewDefaultReadOptions() - return &RocksDB{path, db, wo, ro, cfh, parser, nil, metrics, c, maxOpenFiles, connectBlockStats{}}, nil + return &RocksDB{path, db, wo, ro, cfh, parser, nil, metrics, c, maxOpenFiles, connectBlockStats{}, extendedIndex}, nil } func (d *RocksDB) closeDB() error { @@ -204,6 +206,11 @@ func (d *RocksDB) WriteBatch(wb *grocksdb.WriteBatch) error { return d.db.Write(d.wo, wb) } +// HasExtendedIndex returns true if the DB indexes input txids and spending data +func (d *RocksDB) HasExtendedIndex() bool { + return d.extendedIndex +} + // GetMemoryStats returns memory usage statistics as reported by RocksDB func (d *RocksDB) GetMemoryStats() string { var total, indexAndFilter, memtable uint64 @@ -417,9 +424,12 @@ func (ti *TxInput) Addresses(p bchain.BlockChainParser) ([]string, bool, error) // TxOutput holds output data of the transaction in TxAddresses type TxOutput struct { - AddrDesc bchain.AddressDescriptor - Spent bool - ValueSat big.Int + AddrDesc bchain.AddressDescriptor + Spent bool + ValueSat big.Int + SpentTxid string + SpentIndex uint32 + SpentHeight uint32 } // Addresses converts AddressDescriptor of the output to array of strings @@ -681,6 +691,11 @@ func (d *RocksDB) processAddressesBitcoinType(block *bchain.Block, addresses add tai.ValueSat = spentOutput.ValueSat // mark the output as spent in tx spentOutput.Spent = true + if d.extendedIndex { + spentOutput.SpentTxid = tx.Txid + spentOutput.SpentIndex = uint32(i) + spentOutput.SpentHeight = block.Height + } if len(spentOutput.AddrDesc) == 0 { if !logged { glog.V(1).Infof("rocksdb: height %d, tx %v, input tx %v vout %v skipping empty address", block.Height, tx.Txid, input.Txid, input.Vout) @@ -757,7 +772,7 @@ func (d *RocksDB) storeTxAddresses(wb *grocksdb.WriteBatch, am map[string]*TxAdd varBuf := make([]byte, maxPackedBigintBytes) buf := make([]byte, 1024) for txID, ta := range am { - buf = packTxAddresses(ta, buf, varBuf) + buf = d.packTxAddresses(ta, buf, varBuf) wb.PutCF(d.cfh[cfTxAddresses], []byte(txID), buf) } return nil @@ -901,7 +916,7 @@ func (d *RocksDB) getTxAddresses(btxID []byte) (*TxAddresses, error) { if len(buf) < 3 { return nil, nil } - return unpackTxAddresses(buf) + return d.unpackTxAddresses(buf) } // GetTxAddresses returns TxAddresses for given txid or nil if not found @@ -932,7 +947,7 @@ func (d *RocksDB) AddrDescForOutpoint(outpoint bchain.Outpoint) (bchain.AddressD return ta.Outputs[outpoint.Vout].AddrDesc, &ta.Outputs[outpoint.Vout].ValueSat } -func packTxAddresses(ta *TxAddresses, buf []byte, varBuf []byte) []byte { +func (d *RocksDB) packTxAddresses(ta *TxAddresses, buf []byte, varBuf []byte) []byte { buf = buf[:0] l := packVaruint(uint(ta.Height), varBuf) buf = append(buf, varBuf[:l]...) @@ -944,7 +959,7 @@ func packTxAddresses(ta *TxAddresses, buf []byte, varBuf []byte) []byte { l = packVaruint(uint(len(ta.Outputs)), varBuf) buf = append(buf, varBuf[:l]...) for i := range ta.Outputs { - buf = appendTxOutput(&ta.Outputs[i], buf, varBuf) + buf = d.appendTxOutput(&ta.Outputs[i], buf, varBuf) } return buf } @@ -959,7 +974,7 @@ func appendTxInput(txi *TxInput, buf []byte, varBuf []byte) []byte { return buf } -func appendTxOutput(txo *TxOutput, buf []byte, varBuf []byte) []byte { +func (d *RocksDB) appendTxOutput(txo *TxOutput, buf []byte, varBuf []byte) []byte { la := len(txo.AddrDesc) if txo.Spent { la = ^la @@ -969,6 +984,20 @@ func appendTxOutput(txo *TxOutput, buf []byte, varBuf []byte) []byte { buf = append(buf, txo.AddrDesc...) l = packBigint(&txo.ValueSat, varBuf) buf = append(buf, varBuf[:l]...) + if d.extendedIndex && txo.Spent { + btxID, err := d.chainParser.PackTxid(txo.SpentTxid) + if err != nil { + if err != bchain.ErrTxidMissing { + glog.Error("Cannot pack txid ", txo.SpentTxid) + } + btxID = make([]byte, d.chainParser.PackedTxidLen()) + } + buf = append(buf, btxID...) + l = packVaruint(uint(txo.SpentIndex), varBuf) + buf = append(buf, varBuf[:l]...) + l = packVaruint(uint(txo.SpentHeight), varBuf) + buf = append(buf, varBuf[:l]...) + } return buf } @@ -1034,7 +1063,7 @@ func packAddrBalance(ab *AddrBalance, buf, varBuf []byte) []byte { return buf } -func unpackTxAddresses(buf []byte) (*TxAddresses, error) { +func (d *RocksDB) unpackTxAddresses(buf []byte) (*TxAddresses, error) { ta := TxAddresses{} height, l := unpackVaruint(buf) ta.Height = uint32(height) @@ -1048,7 +1077,7 @@ func unpackTxAddresses(buf []byte) (*TxAddresses, error) { l += ll ta.Outputs = make([]TxOutput, outputs) for i := uint(0); i < outputs; i++ { - l += unpackTxOutput(&ta.Outputs[i], buf[l:]) + l += d.unpackTxOutput(&ta.Outputs[i], buf[l:]) } return &ta, nil } @@ -1061,7 +1090,7 @@ func unpackTxInput(ti *TxInput, buf []byte) int { return l + int(al) } -func unpackTxOutput(to *TxOutput, buf []byte) int { +func (d *RocksDB) unpackTxOutput(to *TxOutput, buf []byte) int { al, l := unpackVarint(buf) if al < 0 { to.Spent = true @@ -1070,7 +1099,20 @@ func unpackTxOutput(to *TxOutput, buf []byte) int { to.AddrDesc = append([]byte(nil), buf[l:l+al]...) al += l to.ValueSat, l = unpackBigint(buf[al:]) - return l + al + al += l + if d.extendedIndex && to.Spent { + l = d.chainParser.PackedTxidLen() + to.SpentTxid, _ = d.chainParser.UnpackTxid(buf[al : al+l]) + al += l + var i uint + i, l = unpackVaruint(buf[al:]) + al += l + to.SpentIndex = uint32(i) + i, l = unpackVaruint(buf[al:]) + to.SpentHeight = uint32(i) + al += l + } + return al } func (d *RocksDB) packTxIndexes(txi []txIndexes) []byte { @@ -1682,7 +1724,7 @@ func (d *RocksDB) LoadInternalState(rpcCoin string) (*common.InternalState, erro data := val.Data() var is *common.InternalState if len(data) == 0 { - is = &common.InternalState{Coin: rpcCoin, UtxoChecked: true} + is = &common.InternalState{Coin: rpcCoin, UtxoChecked: true, ExtendedIndex: d.extendedIndex} } else { is, err = common.UnpackInternalState(data) if err != nil { @@ -1695,6 +1737,9 @@ func (d *RocksDB) LoadInternalState(rpcCoin string) (*common.InternalState, erro } else if is.Coin != rpcCoin { return nil, errors.Errorf("Coins do not match. DB coin %v, RPC coin %v", is.Coin, rpcCoin) } + if is.ExtendedIndex != d.extendedIndex { + return nil, errors.Errorf("ExtendedIndex setting does not match. DB extendedIndex %v, extendedIndex in options %v", is.ExtendedIndex, d.extendedIndex) + } } nc, err := d.checkColumns(is) if err != nil { diff --git a/db/rocksdb_test.go b/db/rocksdb_test.go index 8a287403fd..4138976d52 100644 --- a/db/rocksdb_test.go +++ b/db/rocksdb_test.go @@ -47,7 +47,7 @@ func setupRocksDB(t *testing.T, p bchain.BlockChainParser) *RocksDB { if err != nil { t.Fatal(err) } - d, err := NewRocksDB(tmp, 100000, -1, p, nil) + d, err := NewRocksDB(tmp, 100000, -1, p, nil, false) if err != nil { t.Fatal(err) } @@ -903,9 +903,10 @@ func addressToAddrDesc(addr string, parser bchain.BlockChainParser) []byte { func Test_packTxAddresses_unpackTxAddresses(t *testing.T) { parser := bitcoinTestnetParser() tests := []struct { - name string - hex string - data *TxAddresses + name string + hex string + data *TxAddresses + rocksDB *RocksDB }{ { name: "1", @@ -930,6 +931,7 @@ func Test_packTxAddresses_unpackTxAddresses(t *testing.T) { }, }, }, + rocksDB: &RocksDB{chainParser: parser, extendedIndex: false}, }, { name: "2", @@ -976,6 +978,7 @@ func Test_packTxAddresses_unpackTxAddresses(t *testing.T) { }, }, }, + rocksDB: &RocksDB{chainParser: parser, extendedIndex: false}, }, { name: "empty address", @@ -1000,6 +1003,7 @@ func Test_packTxAddresses_unpackTxAddresses(t *testing.T) { }, }, }, + rocksDB: &RocksDB{chainParser: parser, extendedIndex: false}, }, { name: "empty", @@ -1008,18 +1012,103 @@ func Test_packTxAddresses_unpackTxAddresses(t *testing.T) { Inputs: []TxInput{}, Outputs: []TxOutput{}, }, + rocksDB: &RocksDB{chainParser: parser, extendedIndex: false}, + }, + { + name: "extendedIndex 1", + hex: "e0390317a9149eb21980dc9d413d8eac27314938b9da920ee53e8705021918f2c017a91409f70b896169c37981d2b54b371df0d81a136a2c870501dd7e28c017a914e371782582a4addb541362c55565d2cdf56f6498870501a1e35ec0052fa9141d9ca71efa36d814424ea6ca1437e67287aebe348705012aadcac000b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa38400081ce8685592ea91424fbc77cdc62702ade74dcf989c15e5d3f9240bc870501664894c02fa914afbfb74ee994c7d45f6698738bc4226d065266f7870501a1e35ec0effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75ef17a1f4233276a914d2a37ce20ac9ec4f15dd05a7c6e8e9fbdb99850e88ac043b9943603376a9146b2044146a4438e6e5bfbc65f147afeb64d14fbb88ac05012a05f2007c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25a9956d8396f32a", + data: &TxAddresses{ + Height: 12345, + Inputs: []TxInput{ + { + AddrDesc: addressToAddrDesc("2N7iL7AvS4LViugwsdjTB13uN4T7XhV1bCP", parser), + ValueSat: *big.NewInt(9011000000), + }, + { + AddrDesc: addressToAddrDesc("2Mt9v216YiNBAzobeNEzd4FQweHrGyuRHze", parser), + ValueSat: *big.NewInt(8011000000), + }, + { + AddrDesc: addressToAddrDesc("2NDyqJpHvHnqNtL1F9xAeCWMAW8WLJmEMyD", parser), + ValueSat: *big.NewInt(7011000000), + }, + }, + Outputs: []TxOutput{ + { + AddrDesc: addressToAddrDesc("2MuwoFGwABMakU7DCpdGDAKzyj2nTyRagDP", parser), + ValueSat: *big.NewInt(5011000000), + Spent: true, + SpentTxid: dbtestdata.TxidB1T1, + SpentIndex: 0, + SpentHeight: 432112345, + }, + { + AddrDesc: addressToAddrDesc("2Mvcmw7qkGXNWzkfH1EjvxDcNRGL1Kf2tEM", parser), + ValueSat: *big.NewInt(6011000000), + }, + { + AddrDesc: addressToAddrDesc("2N9GVuX3XJGHS5MCdgn97gVezc6EgvzikTB", parser), + ValueSat: *big.NewInt(7011000000), + Spent: true, + SpentTxid: dbtestdata.TxidB1T2, + SpentIndex: 14231, + SpentHeight: 555555, + }, + { + AddrDesc: addressToAddrDesc("mzii3fuRSpExMLJEHdHveW8NmiX8MPgavk", parser), + ValueSat: *big.NewInt(999900000), + }, + { + AddrDesc: addressToAddrDesc("mqHPFTRk23JZm9W1ANuEFtwTYwxjESSgKs", parser), + ValueSat: *big.NewInt(5000000000), + Spent: true, + SpentTxid: dbtestdata.TxidB2T1, + SpentIndex: 674541, + SpentHeight: 6666666, + }, + }, + }, + rocksDB: &RocksDB{chainParser: parser, extendedIndex: true}, + }, + { + name: "extendedIndex empty address", + hex: "baef9a1501000204d2020002162e010162fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db03e039", + data: &TxAddresses{ + Height: 123456789, + Inputs: []TxInput{ + { + AddrDesc: []byte(nil), + ValueSat: *big.NewInt(1234), + }, + }, + Outputs: []TxOutput{ + { + AddrDesc: []byte(nil), + ValueSat: *big.NewInt(5678), + }, + { + AddrDesc: []byte(nil), + ValueSat: *big.NewInt(98), + Spent: true, + SpentTxid: dbtestdata.TxidB2T4, + SpentIndex: 3, + SpentHeight: 12345, + }, + }, + }, + rocksDB: &RocksDB{chainParser: parser, extendedIndex: true}, }, } varBuf := make([]byte, maxPackedBigintBytes) buf := make([]byte, 1024) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - b := packTxAddresses(tt.data, buf, varBuf) + b := tt.rocksDB.packTxAddresses(tt.data, buf, varBuf) hex := hex.EncodeToString(b) if !reflect.DeepEqual(hex, tt.hex) { t.Errorf("packTxAddresses() = %v, want %v", hex, tt.hex) } - got1, err := unpackTxAddresses(b) + got1, err := tt.rocksDB.unpackTxAddresses(b) if err != nil { t.Errorf("unpackTxAddresses() error = %v", err) return diff --git a/fiat/fiat_rates_test.go b/fiat/fiat_rates_test.go index 417c960be1..f80d9efac2 100644 --- a/fiat/fiat_rates_test.go +++ b/fiat/fiat_rates_test.go @@ -36,7 +36,7 @@ func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *c if err != nil { t.Fatal(err) } - d, err := db.NewRocksDB(tmp, 100000, -1, parser, nil) + d, err := db.NewRocksDB(tmp, 100000, -1, parser, nil, false) if err != nil { t.Fatal(err) } diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 9094ca1751..26b6b49c96 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -211,7 +211,7 @@ func Test_PublicServer_EthereumType(t *testing.T) { glog.Fatal("fakechain: ", err) } - s, dbpath := setupPublicHTTPServer(parser, chain, t) + s, dbpath := setupPublicHTTPServer(parser, chain, t, false) defer closeAndDestroyPublicServer(t, s, dbpath) s.ConnectFullPublicInterface() // take the handler of the public server and pass it to the test server diff --git a/server/public_test.go b/server/public_test.go index ad4ec906cd..f8e76d7b00 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -39,12 +39,12 @@ func TestMain(m *testing.M) { os.Exit(c) } -func setupRocksDB(parser bchain.BlockChainParser, chain bchain.BlockChain, t *testing.T) (*db.RocksDB, *common.InternalState, string) { +func setupRocksDB(parser bchain.BlockChainParser, chain bchain.BlockChain, t *testing.T, extendedIndex bool) (*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) + d, err := db.NewRocksDB(tmp, 100000, -1, parser, nil, extendedIndex) if err != nil { t.Fatal(err) } @@ -95,8 +95,8 @@ func setupRocksDB(parser bchain.BlockChainParser, chain bchain.BlockChain, t *te var metrics *common.Metrics -func setupPublicHTTPServer(parser bchain.BlockChainParser, chain bchain.BlockChain, t *testing.T) (*PublicServer, string) { - d, is, path := setupRocksDB(parser, chain, t) +func setupPublicHTTPServer(parser bchain.BlockChainParser, chain bchain.BlockChain, t *testing.T, extendedIndex bool) (*PublicServer, string) { + d, is, path := setupRocksDB(parser, chain, t, extendedIndex) // setup internal state and match BestHeight to test data is.Coin = "Fakecoin" is.CoinLabel = "Fake Coin" @@ -105,7 +105,7 @@ func setupPublicHTTPServer(parser bchain.BlockChainParser, chain bchain.BlockCha var err error // metrics can be setup only once if metrics == nil { - metrics, err = common.GetMetrics("Fakecoin") + metrics, err = common.GetMetrics("Fakecoin" + strconv.FormatBool(extendedIndex)) if err != nil { glog.Fatal("metrics: ", err) } @@ -1499,7 +1499,7 @@ func fixedTimeNow() time.Time { return time.Date(2022, 9, 15, 12, 43, 56, 0, time.UTC) } -func Test_PublicServer_BitcoinType(t *testing.T) { +func setupChain(t *testing.T) (bchain.BlockChainParser, bchain.BlockChain) { timeNow = fixedTimeNow parser := btc.NewBitcoinParser( btc.GetChainParams("test"), @@ -1515,8 +1515,13 @@ func Test_PublicServer_BitcoinType(t *testing.T) { if err != nil { glog.Fatal("fakechain: ", err) } + return parser, chain +} - s, dbpath := setupPublicHTTPServer(parser, chain, t) +func Test_PublicServer_BitcoinType(t *testing.T) { + parser, chain := setupChain(t) + + s, dbpath := setupPublicHTTPServer(parser, chain, t, false) defer closeAndDestroyPublicServer(t, s, dbpath) s.ConnectFullPublicInterface() // take the handler of the public server and pass it to the test server @@ -1528,6 +1533,83 @@ func Test_PublicServer_BitcoinType(t *testing.T) { websocketTestsBitcoinType(t, ts) } +func httpTestsExtendedIndex(t *testing.T, ts *httptest.Server) { + tests := []struct { + name string + r *http.Request + status int + contentType string + body []string + }{ + { + name: "apiTx v2", + r: newGetRequest(ts.URL + "/api/v2/tx/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","vin":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","n":0,"addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true,"value":"1234567890123"},{"txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","vout":1,"n":1,"addresses":["mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"],"isAddress":true,"value":"12345"}],"vout":[{"value":"317283951061","n":0,"spent":true,"spentTxId":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","spentHeight":225494,"hex":"76a914ccaaaf374e1b06cb83118453d102587b4273d09588ac","addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true},{"value":"917283951061","n":1,"hex":"76a9148d802c045445df49613f6a70ddd2e48526f3701f88ac","addresses":["mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL"],"isAddress":true},{"value":"0","n":2,"hex":"6a072020f1686f6a20","addresses":["OP_RETURN 2020f1686f6a20"],"isAddress":false}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"1234567902122","valueIn":"1234567902468","fees":"346"}`, + }, + }, + { + name: "apiAddress v2 details=txs", + r: newGetRequest(ts.URL + "/api/v2/address/mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw?details=txs"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw","balance":"0","totalReceived":"1234567890123","totalSent":"1234567890123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","vin":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","n":0,"addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true,"isOwn":true,"value":"1234567890123"},{"txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","vout":1,"n":1,"addresses":["mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"],"isAddress":true,"value":"12345"}],"vout":[{"value":"317283951061","n":0,"spent":true,"spentTxId":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","spentHeight":225494,"hex":"76a914ccaaaf374e1b06cb83118453d102587b4273d09588ac","addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true},{"value":"917283951061","n":1,"hex":"76a9148d802c045445df49613f6a70ddd2e48526f3701f88ac","addresses":["mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL"],"isAddress":true},{"value":"0","n":2,"hex":"6a072020f1686f6a20","addresses":["OP_RETURN 2020f1686f6a20"],"isAddress":false}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"1234567902122","valueIn":"1234567902468","fees":"346"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vin":[],"vout":[{"value":"1234567890123","n":0,"spent":true,"spentTxId":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","spentHeight":225494,"hex":"76a914a08eae93007f22668ab5e4a9c83c8cd1c325e3e088ac","addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true,"isOwn":true},{"value":"1","n":1,"spent":true,"spentTxId":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","spentIndex":1,"spentHeight":225494,"hex":"a91452724c5178682f70e0ba31c6ec0633755a3b41d987","addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true},{"value":"9876","n":2,"spent":true,"spentTxId":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","spentHeight":225494,"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"1234567900000","valueIn":"0","fees":"0"}]}`, + }, + }, + { + name: "apiGetBlock", + r: newGetRequest(ts.URL + "/api/v2/block/225493"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"page":1,"totalPages":1,"itemsOnPage":1000,"hash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","nextBlockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","height":225493,"confirmations":2,"size":1234567,"time":1521515026,"version":0,"merkleRoot":"","nonce":"","bits":"","difficulty":"","txCount":2,"txs":[{"txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","vin":[],"vout":[{"value":"100000000","n":0,"addresses":["mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti"],"isAddress":true},{"value":"12345","n":1,"spent":true,"spentTxId":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","spentIndex":1,"spentHeight":225494,"addresses":["mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"],"isAddress":true},{"value":"12345","n":2,"addresses":["mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"100024690","valueIn":"0","fees":"0"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vin":[],"vout":[{"value":"1234567890123","n":0,"spent":true,"spentTxId":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","spentHeight":225494,"addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true},{"value":"1","n":1,"spent":true,"spentTxId":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","spentIndex":1,"spentHeight":225494,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true},{"value":"9876","n":2,"spent":true,"spentTxId":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","spentHeight":225494,"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"1234567900000","valueIn":"0","fees":"0"}]}`, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resp, err := http.DefaultClient.Do(tt.r) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + if resp.StatusCode != tt.status { + t.Errorf("StatusCode = %v, want %v", resp.StatusCode, tt.status) + } + if resp.Header["Content-Type"][0] != tt.contentType { + t.Errorf("Content-Type = %v, want %v", resp.Header["Content-Type"][0], tt.contentType) + } + bb, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + b := string(bb) + for _, c := range tt.body { + if !strings.Contains(b, c) { + t.Errorf("got %v, want to contain %v", b, c) + break + } + } + }) + } +} + +func Test_PublicServer_BitcoinType_ExtendedIndex(t *testing.T) { + parser, chain := setupChain(t) + + s, dbpath := setupPublicHTTPServer(parser, chain, t, true) + defer closeAndDestroyPublicServer(t, s, dbpath) + s.ConnectFullPublicInterface() + // take the handler of the public server and pass it to the test server + ts := httptest.NewServer(s.https.Handler) + defer ts.Close() + + httpTestsExtendedIndex(t, ts) +} + func Test_formatInt64(t *testing.T) { tests := []struct { name string diff --git a/tests/sync/sync.go b/tests/sync/sync.go index e822f19486..b3e1642187 100644 --- a/tests/sync/sync.go +++ b/tests/sync/sync.go @@ -145,7 +145,7 @@ func makeRocksDB(parser bchain.BlockChainParser, m *common.Metrics, is *common.I return nil, nil, err } - d, err := db.NewRocksDB(p, 1<<17, 1<<14, parser, m) + d, err := db.NewRocksDB(p, 1<<17, 1<<14, parser, m, false) if err != nil { return nil, nil, err } From 708f96cf57744c719c124a8f05b25aec86aa7e3d Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 8 Feb 2023 00:19:34 +0100 Subject: [PATCH 151/530] Add extended index option - vin.txid --- api/worker.go | 9 +++ db/rocksdb.go | 116 +++++++++++++++++++++++++++------ db/rocksdb_test.go | 12 +++- static/templates/txdetail.html | 4 +- 4 files changed, 116 insertions(+), 25 deletions(-) diff --git a/api/worker.go b/api/worker.go index 005481a1e9..74a607df19 100644 --- a/api/worker.go +++ b/api/worker.go @@ -844,6 +844,10 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn if err != nil { glog.Errorf("tai.Addresses error %v, tx %v, input %v, tai %+v", err, txid, i, tai) } + if w.db.HasExtendedIndex() { + vin.Txid = tai.Txid + vin.Vout = tai.Vout + } aggregateAddresses(addresses, vin.Addresses, vin.IsAddress) } vouts := make([]Vout, len(ta.Outputs)) @@ -882,6 +886,11 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn Vin: vins, Vout: vouts, } + if w.chainParser.SupportsVSize() { + r.VSize = int(ta.VSize) + } else { + r.Size = int(ta.VSize) + } return r } diff --git a/db/rocksdb.go b/db/rocksdb.go index 72aeebf923..f911cb1f8c 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -415,6 +415,9 @@ type outpoint struct { type TxInput struct { AddrDesc bchain.AddressDescriptor ValueSat big.Int + // extended index properties + Txid string + Vout uint32 } // Addresses converts AddressDescriptor of the input to array of strings @@ -424,9 +427,10 @@ func (ti *TxInput) Addresses(p bchain.BlockChainParser) ([]string, bool, error) // TxOutput holds output data of the transaction in TxAddresses type TxOutput struct { - AddrDesc bchain.AddressDescriptor - Spent bool - ValueSat big.Int + AddrDesc bchain.AddressDescriptor + Spent bool + ValueSat big.Int + // extended index properties SpentTxid string SpentIndex uint32 SpentHeight uint32 @@ -442,6 +446,8 @@ type TxAddresses struct { Height uint32 Inputs []TxInput Outputs []TxOutput + // extended index properties + VSize uint32 } // Utxo holds information about unspent transaction output @@ -596,13 +602,21 @@ func (d *RocksDB) processAddressesBitcoinType(block *bchain.Block, addresses add } blockTxIDs[txi] = btxID ta := TxAddresses{Height: block.Height} + if d.extendedIndex { + if tx.VSize > 0 { + ta.VSize = uint32(tx.VSize) + } else { + ta.VSize = uint32(len(tx.Hex)) + } + } ta.Outputs = make([]TxOutput, len(tx.Vout)) txAddressesMap[string(btxID)] = &ta blockTxAddresses[txi] = &ta - for i, output := range tx.Vout { + for i := range tx.Vout { + output := &tx.Vout[i] tao := &ta.Outputs[i] tao.ValueSat = output.ValueSat - addrDesc, err := d.chainParser.GetAddrDescFromVout(&output) + addrDesc, err := d.chainParser.GetAddrDescFromVout(output) if err != nil || len(addrDesc) == 0 || len(addrDesc) > maxAddrDescLen { if err != nil { // do not log ErrAddressMissing, transactions can be without to address (for example eth contracts) @@ -652,7 +666,8 @@ func (d *RocksDB) processAddressesBitcoinType(block *bchain.Block, addresses add ta := blockTxAddresses[txi] ta.Inputs = make([]TxInput, len(tx.Vin)) logged := false - for i, input := range tx.Vin { + for i := range tx.Vin { + input := &tx.Vin[i] tai := &ta.Inputs[i] btxID, err := d.chainParser.PackTxid(input.Txid) if err != nil { @@ -695,6 +710,8 @@ func (d *RocksDB) processAddressesBitcoinType(block *bchain.Block, addresses add spentOutput.SpentTxid = tx.Txid spentOutput.SpentIndex = uint32(i) spentOutput.SpentHeight = block.Height + tai.Txid = input.Txid + tai.Vout = input.Vout } if len(spentOutput.AddrDesc) == 0 { if !logged { @@ -951,10 +968,14 @@ func (d *RocksDB) packTxAddresses(ta *TxAddresses, buf []byte, varBuf []byte) [] buf = buf[:0] l := packVaruint(uint(ta.Height), varBuf) buf = append(buf, varBuf[:l]...) + if d.extendedIndex { + l = packVaruint(uint(ta.VSize), varBuf) + buf = append(buf, varBuf[:l]...) + } l = packVaruint(uint(len(ta.Inputs)), varBuf) buf = append(buf, varBuf[:l]...) for i := range ta.Inputs { - buf = appendTxInput(&ta.Inputs[i], buf, varBuf) + buf = d.appendTxInput(&ta.Inputs[i], buf, varBuf) } l = packVaruint(uint(len(ta.Outputs)), varBuf) buf = append(buf, varBuf[:l]...) @@ -964,13 +985,38 @@ func (d *RocksDB) packTxAddresses(ta *TxAddresses, buf []byte, varBuf []byte) [] return buf } -func appendTxInput(txi *TxInput, buf []byte, varBuf []byte) []byte { +func (d *RocksDB) appendTxInput(txi *TxInput, buf []byte, varBuf []byte) []byte { la := len(txi.AddrDesc) - l := packVaruint(uint(la), varBuf) - buf = append(buf, varBuf[:l]...) - buf = append(buf, txi.AddrDesc...) - l = packBigint(&txi.ValueSat, varBuf) - buf = append(buf, varBuf[:l]...) + var l int + if d.extendedIndex { + if txi.Txid == "" { + // coinbase transaction + la = ^la + } + l = packVarint(la, varBuf) + buf = append(buf, varBuf[:l]...) + buf = append(buf, txi.AddrDesc...) + l = packBigint(&txi.ValueSat, varBuf) + buf = append(buf, varBuf[:l]...) + if la >= 0 { + btxID, err := d.chainParser.PackTxid(txi.Txid) + if err != nil { + if err != bchain.ErrTxidMissing { + glog.Error("Cannot pack txid ", txi.Txid) + } + btxID = make([]byte, d.chainParser.PackedTxidLen()) + } + buf = append(buf, btxID...) + l = packVaruint(uint(txi.Vout), varBuf) + buf = append(buf, varBuf[:l]...) + } + } else { + l = packVaruint(uint(la), varBuf) + buf = append(buf, varBuf[:l]...) + buf = append(buf, txi.AddrDesc...) + l = packBigint(&txi.ValueSat, varBuf) + buf = append(buf, varBuf[:l]...) + } return buf } @@ -1049,7 +1095,7 @@ func packAddrBalance(ab *AddrBalance, buf, varBuf []byte) []byte { l = packBigint(&ab.BalanceSat, varBuf) buf = append(buf, varBuf[:l]...) for _, utxo := range ab.Utxos { - // if Vout < 0, utxo is marked as spent + // if Vout < 0, utxo is marked as spent and removed from the entry if utxo.Vout >= 0 { buf = append(buf, utxo.BtxID...) l = packVaruint(uint(utxo.Vout), varBuf) @@ -1067,11 +1113,16 @@ func (d *RocksDB) unpackTxAddresses(buf []byte) (*TxAddresses, error) { ta := TxAddresses{} height, l := unpackVaruint(buf) ta.Height = uint32(height) + if d.extendedIndex { + vsize, ll := unpackVaruint(buf[l:]) + ta.VSize = uint32(vsize) + l += ll + } inputs, ll := unpackVaruint(buf[l:]) l += ll ta.Inputs = make([]TxInput, inputs) for i := uint(0); i < inputs; i++ { - l += unpackTxInput(&ta.Inputs[i], buf[l:]) + l += d.unpackTxInput(&ta.Inputs[i], buf[l:]) } outputs, ll := unpackVaruint(buf[l:]) l += ll @@ -1082,12 +1133,35 @@ func (d *RocksDB) unpackTxAddresses(buf []byte) (*TxAddresses, error) { return &ta, nil } -func unpackTxInput(ti *TxInput, buf []byte) int { - al, l := unpackVaruint(buf) - ti.AddrDesc = append([]byte(nil), buf[l:l+int(al)]...) - al += uint(l) - ti.ValueSat, l = unpackBigint(buf[al:]) - return l + int(al) +func (d *RocksDB) unpackTxInput(ti *TxInput, buf []byte) int { + if d.extendedIndex { + al, l := unpackVarint(buf) + var coinbase bool + if al < 0 { + coinbase = true + al = ^al + } + ti.AddrDesc = append([]byte(nil), buf[l:l+al]...) + al += l + ti.ValueSat, l = unpackBigint(buf[al:]) + al += l + if !coinbase { + l = d.chainParser.PackedTxidLen() + ti.Txid, _ = d.chainParser.UnpackTxid(buf[al : al+l]) + al += l + var i uint + i, l = unpackVaruint(buf[al:]) + ti.Vout = uint32(i) + al += l + } + return al + } else { + al, l := unpackVaruint(buf) + ti.AddrDesc = append([]byte(nil), buf[l:l+int(al)]...) + al += uint(l) + ti.ValueSat, l = unpackBigint(buf[al:]) + return l + int(al) + } } func (d *RocksDB) unpackTxOutput(to *TxOutput, buf []byte) int { diff --git a/db/rocksdb_test.go b/db/rocksdb_test.go index 4138976d52..bb05106222 100644 --- a/db/rocksdb_test.go +++ b/db/rocksdb_test.go @@ -1016,21 +1016,28 @@ func Test_packTxAddresses_unpackTxAddresses(t *testing.T) { }, { name: "extendedIndex 1", - hex: "e0390317a9149eb21980dc9d413d8eac27314938b9da920ee53e8705021918f2c017a91409f70b896169c37981d2b54b371df0d81a136a2c870501dd7e28c017a914e371782582a4addb541362c55565d2cdf56f6498870501a1e35ec0052fa9141d9ca71efa36d814424ea6ca1437e67287aebe348705012aadcac000b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa38400081ce8685592ea91424fbc77cdc62702ade74dcf989c15e5d3f9240bc870501664894c02fa914afbfb74ee994c7d45f6698738bc4226d065266f7870501a1e35ec0effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75ef17a1f4233276a914d2a37ce20ac9ec4f15dd05a7c6e8e9fbdb99850e88ac043b9943603376a9146b2044146a4438e6e5bfbc65f147afeb64d14fbb88ac05012a05f2007c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25a9956d8396f32a", + hex: "e0398241032ea9149eb21980dc9d413d8eac27314938b9da920ee53e8705021918f2c0c50c7ce2f5670fd52de738288299bd854a85ef1bb304f62f35ced1bd49a8a810002ea91409f70b896169c37981d2b54b371df0d81a136a2c870501dd7e28c0e96672c7fcc8da131427fcea7e841028614813496a56c11e8a6185c16861c495012ea914e371782582a4addb541362c55565d2cdf56f6498870501a1e35ec0ed308c72f9804dfeefdbb483ef8fd1e638180ad81d6b33f4b58d36d19162fa6d8106052fa9141d9ca71efa36d814424ea6ca1437e67287aebe348705012aadcac000b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa38400081ce8685592ea91424fbc77cdc62702ade74dcf989c15e5d3f9240bc870501664894c02fa914afbfb74ee994c7d45f6698738bc4226d065266f7870501a1e35ec0effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75ef17a1f4233276a914d2a37ce20ac9ec4f15dd05a7c6e8e9fbdb99850e88ac043b9943603376a9146b2044146a4438e6e5bfbc65f147afeb64d14fbb88ac05012a05f2007c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25a9956d8396f32a", data: &TxAddresses{ Height: 12345, + VSize: 321, Inputs: []TxInput{ { AddrDesc: addressToAddrDesc("2N7iL7AvS4LViugwsdjTB13uN4T7XhV1bCP", parser), ValueSat: *big.NewInt(9011000000), + Txid: "c50c7ce2f5670fd52de738288299bd854a85ef1bb304f62f35ced1bd49a8a810", + Vout: 0, }, { AddrDesc: addressToAddrDesc("2Mt9v216YiNBAzobeNEzd4FQweHrGyuRHze", parser), ValueSat: *big.NewInt(8011000000), + Txid: "e96672c7fcc8da131427fcea7e841028614813496a56c11e8a6185c16861c495", + Vout: 1, }, { AddrDesc: addressToAddrDesc("2NDyqJpHvHnqNtL1F9xAeCWMAW8WLJmEMyD", parser), ValueSat: *big.NewInt(7011000000), + Txid: "ed308c72f9804dfeefdbb483ef8fd1e638180ad81d6b33f4b58d36d19162fa6d", + Vout: 134, }, }, Outputs: []TxOutput{ @@ -1072,9 +1079,10 @@ func Test_packTxAddresses_unpackTxAddresses(t *testing.T) { }, { name: "extendedIndex empty address", - hex: "baef9a1501000204d2020002162e010162fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db03e039", + hex: "baef9a152d01010204d2020002162e010162fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db03e039", data: &TxAddresses{ Height: 123456789, + VSize: 45, Inputs: []TxInput{ { AddrDesc: []byte(nil), diff --git a/static/templates/txdetail.html b/static/templates/txdetail.html index a5f53758fd..3472323e6d 100644 --- a/static/templates/txdetail.html +++ b/static/templates/txdetail.html @@ -59,8 +59,8 @@
Blockbook Websocket Test Page

class="form-control" placeholder="data" style="width: 100%" - id="ethCallData" + id="rpcCallData" value="0x2fec7966000000000000000000000000ce66a9577f4e2589c1d1547b75b7a2b0807ce0ed" />
-
+
diff --git a/tests/dbtestdata/fakechain_ethereumtype.go b/tests/dbtestdata/fakechain_ethereumtype.go index 09abdfe988..a846927100 100644 --- a/tests/dbtestdata/fakechain_ethereumtype.go +++ b/tests/dbtestdata/fakechain_ethereumtype.go @@ -134,8 +134,8 @@ func (c *fakeBlockChainEthereumType) EthereumTypeGetErc20ContractBalance(addrDes return big.NewInt(1000000000 + int64(addrDesc[0])*1000 + int64(contractDesc[0])), nil } -// EthereumTypeEthCall calls eth_call with given data and to address -func (c *fakeBlockChainEthereumType) EthereumTypeEthCall(data, to, from string) (string, error) { +// EthereumTypeRpcCall calls eth_call with given data and to address +func (c *fakeBlockChainEthereumType) EthereumTypeRpcCall(data, to, from string) (string, error) { return data + "abcd", nil } From 0cc953fccaba90f0becebf01ef392326db7ccc68 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 11 Oct 2024 11:50:07 +0200 Subject: [PATCH 373/530] btc (+testnet) 27.1 -> 28.0 --- build/templates/backend/config/bitcoin.conf | 2 ++ build/templates/backend/config/bitcoin_regtest.conf | 2 ++ .../{bitcoin-signet.conf => bitcoin_signet.conf} | 2 ++ configs/coins/bitcoin.json | 10 +++++----- configs/coins/bitcoin_regtest.json | 10 +++++----- configs/coins/bitcoin_signet.json | 12 ++++++------ configs/coins/bitcoin_testnet.json | 10 +++++----- configs/coins/groestlcoin_signet.json | 10 +++++----- 8 files changed, 32 insertions(+), 26 deletions(-) rename build/templates/backend/config/{bitcoin-signet.conf => bitcoin_signet.conf} (96%) diff --git a/build/templates/backend/config/bitcoin.conf b/build/templates/backend/config/bitcoin.conf index c6f94c7392..619f678536 100644 --- a/build/templates/backend/config/bitcoin.conf +++ b/build/templates/backend/config/bitcoin.conf @@ -16,6 +16,8 @@ mempoolfullrbf=1 dbcache=1000 +deprecatedrpc=warnings + {{- if .Backend.AdditionalParams}} # generated from additional_params {{- range $name, $value := .Backend.AdditionalParams}} diff --git a/build/templates/backend/config/bitcoin_regtest.conf b/build/templates/backend/config/bitcoin_regtest.conf index 0fb7aef215..3bdfc3dcc2 100644 --- a/build/templates/backend/config/bitcoin_regtest.conf +++ b/build/templates/backend/config/bitcoin_regtest.conf @@ -12,6 +12,8 @@ rpcworkqueue=1100 maxmempool=2000 dbcache=1000 +deprecatedrpc=warnings + {{- if .Backend.AdditionalParams}} # generated from additional_params {{- range $name, $value := .Backend.AdditionalParams}} diff --git a/build/templates/backend/config/bitcoin-signet.conf b/build/templates/backend/config/bitcoin_signet.conf similarity index 96% rename from build/templates/backend/config/bitcoin-signet.conf rename to build/templates/backend/config/bitcoin_signet.conf index c26fa574e1..e88a0fd50e 100644 --- a/build/templates/backend/config/bitcoin-signet.conf +++ b/build/templates/backend/config/bitcoin_signet.conf @@ -13,6 +13,8 @@ rpcworkqueue=1100 maxmempool=2000 dbcache=1000 +deprecatedrpc=warnings + {{- if .Backend.AdditionalParams}} # generated from additional_params {{- range $name, $value := .Backend.AdditionalParams}} diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index 61fbb924e1..9e4b09ecbb 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": "27.1", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-27.1/bitcoin-27.1-x86_64-linux-gnu.tar.gz", + "version": "28.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "c9840607d230d65f6938b81deaec0b98fe9cb14c3a41a5b13b2c05d044a48422", + "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -43,8 +43,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-27.1/bitcoin-27.1-aarch64-linux-gnu.tar.gz", - "verification_source": "bb878df4f8ff8fb8acfb94207c50f959c462c39e652f507c2a2db20acc6a1eee" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz", + "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e" } } }, diff --git a/configs/coins/bitcoin_regtest.json b/configs/coins/bitcoin_regtest.json index 96a6c89836..42f9483367 100644 --- a/configs/coins/bitcoin_regtest.json +++ b/configs/coins/bitcoin_regtest.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-regtest", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "27.1", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-27.1/bitcoin-27.1-x86_64-linux-gnu.tar.gz", + "version": "28.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "c9840607d230d65f6938b81deaec0b98fe9cb14c3a41a5b13b2c05d044a48422", + "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-27.1/bitcoin-27.1-aarch64-linux-gnu.tar.gz", - "verification_source": "bb878df4f8ff8fb8acfb94207c50f959c462c39e652f507c2a2db20acc6a1eee" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz", + "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e" } } }, diff --git a/configs/coins/bitcoin_signet.json b/configs/coins/bitcoin_signet.json index 7a94587f06..c18e71ccbd 100644 --- a/configs/coins/bitcoin_signet.json +++ b/configs/coins/bitcoin_signet.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-signet", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "27.1", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-27.1/bitcoin-27.1-x86_64-linux-gnu.tar.gz", + "version": "28.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "c9840607d230d65f6938b81deaec0b98fe9cb14c3a41a5b13b2c05d044a48422", + "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -35,15 +35,15 @@ "service_additional_params_template": "", "protect_memory": true, "mainnet": false, - "server_config_file": "bitcoin-signet.conf", + "server_config_file": "bitcoin_signet.conf", "client_config_file": "bitcoin_client.conf", "additional_params": { "deprecatedrpc": "estimatefee" }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-27.1/bitcoin-27.1-aarch64-linux-gnu.tar.gz", - "verification_source": "bb878df4f8ff8fb8acfb94207c50f959c462c39e652f507c2a2db20acc6a1eee" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz", + "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e" } } }, diff --git a/configs/coins/bitcoin_testnet.json b/configs/coins/bitcoin_testnet.json index 3b6741e130..dc6048f616 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": "27.1", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-27.1/bitcoin-27.1-x86_64-linux-gnu.tar.gz", + "version": "28.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "c9840607d230d65f6938b81deaec0b98fe9cb14c3a41a5b13b2c05d044a48422", + "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-27.1/bitcoin-27.1-aarch64-linux-gnu.tar.gz", - "verification_source": "bb878df4f8ff8fb8acfb94207c50f959c462c39e652f507c2a2db20acc6a1eee" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz", + "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e" } } }, diff --git a/configs/coins/groestlcoin_signet.json b/configs/coins/groestlcoin_signet.json index 2fd281a20f..20a019966f 100644 --- a/configs/coins/groestlcoin_signet.json +++ b/configs/coins/groestlcoin_signet.json @@ -35,16 +35,16 @@ "service_additional_params_template": "", "protect_memory": true, "mainnet": false, - "server_config_file": "bitcoin-signet.conf", + "server_config_file": "bitcoin_signet.conf", "client_config_file": "bitcoin_client.conf", "additional_params": { "deprecatedrpc": "estimatefee" }, "platforms": { - "arm64": { - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v27.0/groestlcoin-27.0-aarch64-linux-gnu.tar.gz", - "verification_source": "95e1a4c4f4d50709df40e2d86c4b578db053d1cb475a3384862192c1298f9de6" - } + "arm64": { + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v27.0/groestlcoin-27.0-aarch64-linux-gnu.tar.gz", + "verification_source": "95e1a4c4f4d50709df40e2d86c4b578db053d1cb475a3384862192c1298f9de6" + } } }, "blockbook": { From d8c68f2b6b7d9a08d99a6a68f8215b84d3f1f8dd Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 10 Oct 2024 01:28:38 +0200 Subject: [PATCH 374/530] Add Bitcoin Testnet4 --- bchain/coins/blockchain.go | 1 + .../backend/config/bitcoin_testnet4.conf | 38 +++++++++ configs/coins/bitcoin_testnet4.json | 80 +++++++++++++++++++ docs/ports.md | 3 +- 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 build/templates/backend/config/bitcoin_testnet4.conf create mode 100644 configs/coins/bitcoin_testnet4.json diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index f5dcdc0351..f0cfa3cc9e 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -67,6 +67,7 @@ var BlockChainFactories = make(map[string]blockChainFactory) func init() { BlockChainFactories["Bitcoin"] = btc.NewBitcoinRPC BlockChainFactories["Testnet"] = btc.NewBitcoinRPC + BlockChainFactories["Testnet4"] = btc.NewBitcoinRPC BlockChainFactories["Signet"] = btc.NewBitcoinRPC BlockChainFactories["Regtest"] = btc.NewBitcoinRPC BlockChainFactories["Zcash"] = zec.NewZCashRPC diff --git a/build/templates/backend/config/bitcoin_testnet4.conf b/build/templates/backend/config/bitcoin_testnet4.conf new file mode 100644 index 0000000000..46a5370b8c --- /dev/null +++ b/build/templates/backend/config/bitcoin_testnet4.conf @@ -0,0 +1,38 @@ +{{define "main" -}} +daemon=1 +server=1 +{{if .Backend.Mainnet}}mainnet=1{{else}}testnet4=1{{end}} +nolisten=1 +txindex=1 +disablewallet=1 + +zmqpubhashtx={{template "IPC.MessageQueueBindingTemplate" .}} +zmqpubhashblock={{template "IPC.MessageQueueBindingTemplate" .}} + +rpcworkqueue=1100 +maxmempool=4096 +mempoolexpiry=8760 +mempoolfullrbf=1 + +dbcache=1000 + +deprecatedrpc=warnings + +{{- 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}} + +{{if .Backend.Mainnet}}[main]{{else}}[testnet4]{{end}} +{{generateRPCAuth .IPC.RPCUser .IPC.RPCPass -}} +rpcport={{.Ports.BackendRPC}} + +{{end}} diff --git a/configs/coins/bitcoin_testnet4.json b/configs/coins/bitcoin_testnet4.json new file mode 100644 index 0000000000..75bf73e848 --- /dev/null +++ b/configs/coins/bitcoin_testnet4.json @@ -0,0 +1,80 @@ +{ + "coin": { + "name": "Testnet4", + "shortcut": "TEST", + "label": "Bitcoin Testnet4", + "alias": "bitcoin_testnet4" + }, + "ports": { + "backend_rpc": 18029, + "backend_message_queue": 48329, + "blockbook_internal": 19029, + "blockbook_public": 19129 + }, + "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-bitcoin-testnet4", + "package_revision": "satoshilabs-1", + "system_user": "bitcoin", + "version": "28.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz", + "verification_type": "sha256", + "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": ["bin/bitcoin-qt"], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -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/testnet4/*.log", + "postinst_script_template": "", + "service_type": "forking", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "bitcoin_testnet4.conf", + "client_config_file": "bitcoin_client.conf", + "additional_params": { + "deprecatedrpc": "estimatefee" + }, + "platforms": { + "arm64": { + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz", + "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e" + } + } + }, + "blockbook": { + "package_name": "blockbook-bitcoin-testnet4", + "system_user": "blockbook-bitcoin", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "-enablesubnewtx -extendedindex", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 10000, + "xpub_magic": 70617039, + "xpub_magic_segwit_p2sh": 71979618, + "xpub_magic_segwit_native": 73342198, + "slip44": 1, + "additional_params": { + "block_golomb_filter_p": 20, + "block_filter_scripts": "taproot-noordinals", + "block_filter_use_zeroed_key": true, + "mempool_golomb_filter_p": 20, + "mempool_filter_scripts": "taproot", + "mempool_filter_use_zeroed_key": false + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/docs/ports.md b/docs/ports.md index b18bfb46c4..bbb1bac1e8 100644 --- a/docs/ports.md +++ b/docs/ports.md @@ -1,7 +1,7 @@ # Registry of ports | coin | blockbook public | blockbook internal | backend rpc | backend service ports (zmq) | -|----------------------------------|------------------|--------------------|-------------|-----------------------------------------------------| +| -------------------------------- | ---------------- | ------------------ | ----------- | --------------------------------------------------- | | Ethereum Archive | 9116 | 9016 | 8016 | 38316 p2p, 8116 http, 8516 authrpc | | Bitcoin | 9130 | 9030 | 8030 | 38330 | | Bitcoin Cash | 9131 | 9031 | 8031 | 38331 | @@ -58,6 +58,7 @@ | Ethereum Testnet Holesky | 19116 | 19016 | 18016 | 18116 http, 18516 authrpc, 48316 p2p | | Bitcoin Signet | 19120 | 19020 | 18020 | 48320 | | Bitcoin Regtest | 19121 | 19021 | 18021 | 48321 | +| Bitcoin Testnet4 | 19129 | 19029 | 18029 | 48329 | | Bitcoin Testnet | 19130 | 19030 | 18030 | 48330 | | Bitcoin Cash Testnet | 19131 | 19031 | 18031 | 48331 | | Zcash Testnet | 19132 | 19032 | 18032 | 48332 | From c3cd4445781d7a0883b02b856e894883327ea46a Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 15 Oct 2024 13:38:31 +0000 Subject: [PATCH 375/530] =?UTF-8?q?polygon-heimdall=201.0.5=20=E2=86=92=20?= =?UTF-8?q?1.0.10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/polygon_heimdall.json | 10 +++++----- configs/coins/polygon_heimdall_archive.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/configs/coins/polygon_heimdall.json b/configs/coins/polygon_heimdall.json index 6a57ea811b..1486deebab 100644 --- a/configs/coins/polygon_heimdall.json +++ b/configs/coins/polygon_heimdall.json @@ -16,16 +16,16 @@ "package_name": "backend-polygon-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.0.5", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.0.5.tar.gz", + "version": "1.0.10", + "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.0.10.tar.gz", "verification_type": "sha256", - "verification_source": "59727263cb3927dd47e5c00dc3c5754f0cd7680af6e1ae019b4b540b3442197c", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.0.5.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "9058e054de2a0090e0a8400aa23d6144d7432ac31c6b4e4b6cff684a834e612f", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.0.10.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.0.5/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.0.10/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, diff --git a/configs/coins/polygon_heimdall_archive.json b/configs/coins/polygon_heimdall_archive.json index 82114a9c2f..228f42d01a 100644 --- a/configs/coins/polygon_heimdall_archive.json +++ b/configs/coins/polygon_heimdall_archive.json @@ -16,16 +16,16 @@ "package_name": "backend-polygon-archive-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.0.5", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.0.5.tar.gz", + "version": "1.0.10", + "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.0.10.tar.gz", "verification_type": "sha256", - "verification_source": "59727263cb3927dd47e5c00dc3c5754f0cd7680af6e1ae019b4b540b3442197c", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.0.5.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "9058e054de2a0090e0a8400aa23d6144d7432ac31c6b4e4b6cff684a834e612f", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.0.10.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.0.5/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.0.10/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, From 572d7e5075741f483f416e892adeee0c6f916711 Mon Sep 17 00:00:00 2001 From: XK4MiLX <62837435+XK4MiLX@users.noreply.github.com> Date: Tue, 17 Sep 2024 09:54:33 +0200 Subject: [PATCH 376/530] Update Firo daemon 0.14.14.0 (mandatory) --- configs/coins/firo.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/firo.json b/configs/coins/firo.json index 102733fc03..14a0f65b97 100644 --- a/configs/coins/firo.json +++ b/configs/coins/firo.json @@ -22,10 +22,10 @@ "package_name": "backend-firo", "package_revision": "satoshilabs-1", "system_user": "firo", - "version": "0.14.13.3", - "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.13.3/firo-0.14.13.3-linux64.tar.gz", + "version": "0.14.14.0", + "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.14.0/firo-0.14.14.0-linux64.tar.gz", "verification_type": "sha256", - "verification_source": "39a4729fe9ab95cf3a236b95aadd53c3a18ac8737b7bfdd8934dd5524e19d2e8", + "verification_source": "0f8c914286031830d8c9eb1ab86b3e21f349917aea7bc2ab12229ab4c638cbe8", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/firo-qt", From fe676b354def59e7b2dbd74db007a34007cfbdf4 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Mon, 21 Oct 2024 12:32:21 +0000 Subject: [PATCH 377/530] =?UTF-8?q?prysm=205.1.0=20=E2=86=92=205.1.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum_archive_consensus.json | 10 +++++----- configs/coins/ethereum_consensus.json | 10 +++++----- .../ethereum_testnet_holesky_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_consensus.json | 10 +++++----- .../ethereum_testnet_sepolia_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_consensus.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 744350ca9e..4caa570a98 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-amd64", + "version": "5.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "bc59aabe40d32959692dba260003fc5004775ac0f7fa2513a66fc28dc2f4717f", + "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-arm64", - "verification_source": "74eb6c423316074641367d5655bb84bc7ae117ff6e58d95ef9d48d065eef00ef" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", + "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" } } }, diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 8a80eca9b3..9ec1e5a291 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-amd64", + "version": "5.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "bc59aabe40d32959692dba260003fc5004775ac0f7fa2513a66fc28dc2f4717f", + "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-arm64", - "verification_source": "74eb6c423316074641367d5655bb84bc7ae117ff6e58d95ef9d48d065eef00ef" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", + "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive_consensus.json b/configs/coins/ethereum_testnet_holesky_archive_consensus.json index d9d6e325b8..88859fd74f 100644 --- a/configs/coins/ethereum_testnet_holesky_archive_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-amd64", + "version": "5.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "bc59aabe40d32959692dba260003fc5004775ac0f7fa2513a66fc28dc2f4717f", + "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13636 --p2p-udp-port=12636 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-arm64", - "verification_source": "74eb6c423316074641367d5655bb84bc7ae117ff6e58d95ef9d48d065eef00ef" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", + "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_consensus.json b/configs/coins/ethereum_testnet_holesky_consensus.json index 94314490c3..19774f19b0 100644 --- a/configs/coins/ethereum_testnet_holesky_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-amd64", + "version": "5.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "bc59aabe40d32959692dba260003fc5004775ac0f7fa2513a66fc28dc2f4717f", + "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-arm64", - "verification_source": "74eb6c423316074641367d5655bb84bc7ae117ff6e58d95ef9d48d065eef00ef" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", + "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json index 1faa700c63..0bdbe87b11 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-amd64", + "version": "5.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "bc59aabe40d32959692dba260003fc5004775ac0f7fa2513a66fc28dc2f4717f", + "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-arm64", - "verification_source": "74eb6c423316074641367d5655bb84bc7ae117ff6e58d95ef9d48d065eef00ef" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", + "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json index 106b7a30d7..9ca379efd0 100644 --- a/configs/coins/ethereum_testnet_sepolia_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-amd64", + "version": "5.1.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "bc59aabe40d32959692dba260003fc5004775ac0f7fa2513a66fc28dc2f4717f", + "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.0/beacon-chain-v5.1.0-linux-arm64", - "verification_source": "74eb6c423316074641367d5655bb84bc7ae117ff6e58d95ef9d48d065eef00ef" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", + "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" } } }, From f373a73beaf31bd7bb33343946ac94bf2ddad8b8 Mon Sep 17 00:00:00 2001 From: wakiyamap Date: Sat, 7 Sep 2024 02:41:46 +0900 Subject: [PATCH 378/530] add tests for bitcoin testnet4 --- bchain/coins/btc/bitcoinparser.go | 19 ++ bchain/coins/btc/bitcoinparser_test.go | 55 ++++- tests/rpc/testdata/bitcoin_testnet4.json | 105 +++++++++ tests/sync/testdata/bitcoin_testnet4.json | 266 ++++++++++++++++++++++ tests/tests.json | 5 + 5 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 tests/rpc/testdata/bitcoin_testnet4.json create mode 100644 tests/sync/testdata/bitcoin_testnet4.json diff --git a/bchain/coins/btc/bitcoinparser.go b/bchain/coins/btc/bitcoinparser.go index 77cbcf267e..a022c18e0d 100644 --- a/bchain/coins/btc/bitcoinparser.go +++ b/bchain/coins/btc/bitcoinparser.go @@ -4,11 +4,28 @@ import ( "encoding/json" "math/big" + "github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcutil/chaincfg" "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/common" ) +// temp params for signet(wait btcd commit) +// magic numbers +const ( + Testnet4Magic wire.BitcoinNet = 0x283f161c +) + +// chain parameters +var ( + TestNet4Params chaincfg.Params +) + +func init() { + TestNet4Params = chaincfg.TestNet3Params + TestNet4Params.Net = Testnet4Magic +} + // BitcoinParser handle type BitcoinParser struct { *BitcoinLikeParser @@ -33,6 +50,8 @@ func GetChainParams(chain string) *chaincfg.Params { switch chain { case "test": return &chaincfg.TestNet3Params + case "testnet4": + return &TestNet4Params case "regtest": return &chaincfg.RegressionNetParams case "signet": diff --git a/bchain/coins/btc/bitcoinparser_test.go b/bchain/coins/btc/bitcoinparser_test.go index 1ca0d4ec43..a697cbfd1d 100644 --- a/bchain/coins/btc/bitcoinparser_test.go +++ b/bchain/coins/btc/bitcoinparser_test.go @@ -467,11 +467,12 @@ func TestGetAddressesFromAddrDescTestnet(t *testing.T) { } var ( - testTx1, testTx2, testTx3 bchain.Tx + testTx1, testTx2, testTx3, testTx4 bchain.Tx testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700" testTxPacked2 = "0007c91a899ab7da6a010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000" testTxPacked3 = "00003d818bfda9aa3e02000000000102deb1999a857ab0a13d6b12fbd95ea75b409edde5f2ff747507ce42d9986a8b9d0000000000fdffffff9fd2d3361e203b2375eba6438efbef5b3075531e7e583c7cc76b7294fe7f22980000000000fdffffff02a0860100000000001600148091746745464e7555c31e9a5afceac14a02978ae7fc1c0000000000160014565ea9ff4589d3e05ba149ae6e257752bfdc2a1e0247304402207d67d320a8e813f986b35e9791935fcb736754812b7038686f5de6cfdcda99cd02201c3bb2c178e0056016437ecfe365a7eef84aa9d293ebdc566177af82e22fcdd3012103abb30c1bbe878b07b58dc169b1d061d48c60be8107f632a59778b38bf7ceea5a02473044022044f54a478cfe086e870cb026c9dcd4e14e63778bef569a4d55a6332725cd9a9802202f0e94c04e6f328fc64ad9efe552888c299750d1b8d033324825a3ff29920e030121036fcd433428aa7dc65c4f5408fa31f208c54fe4b4c6c1ae9c39a825ed4f1ac039813d0000" + testTxPacked4 = "0000a2b98ced82b6400300000000010148f8f93ebb12407809920d2ab9cc1bf01289b314eb23028c83fdab21e5fefa690100000000fdffffff0150c3000000000000160014cb888de3c89670a3061fb6ef6590f187649cca060247304402206a9db8d7157e4b0a06a1f090b9de88cdc616028b431b80617a055117877e479a02202937d6d1658d4a8afde86b245325c3bb0e769a87cb09d802bcefaa21550065e201210374aa8f312de4ebccbef55609700a39764387aa4ff5d76f1ccb4d2382e454f05b00000000" ) func init() { @@ -595,6 +596,37 @@ func init() { }, }, } + + testTx4 = bchain.Tx{ + Hex: "0300000000010148f8f93ebb12407809920d2ab9cc1bf01289b314eb23028c83fdab21e5fefa690100000000fdffffff0150c3000000000000160014cb888de3c89670a3061fb6ef6590f187649cca060247304402206a9db8d7157e4b0a06a1f090b9de88cdc616028b431b80617a055117877e479a02202937d6d1658d4a8afde86b245325c3bb0e769a87cb09d802bcefaa21550065e201210374aa8f312de4ebccbef55609700a39764387aa4ff5d76f1ccb4d2382e454f05b00000000", + Blocktime: 1724927392, + Txid: "8e3f38bf6854dd3c358be8d4f9a40a6dccc50de49616125d27af9fdbe65287eb", + LockTime: 0, + VSize: 110, + Version: 3, + Vin: []bchain.Vin{ + { + ScriptSig: bchain.ScriptSig{ + Hex: "", + }, + Txid: "69fafee521abfd838c0223eb14b38912f01bccb92a0d9209784012bb3ef9f848", + Vout: 1, + Sequence: 4294967293, + }, + }, + Vout: []bchain.Vout{ + { + ValueSat: *big.NewInt(50000), + N: 0, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "0014cb888de3c89670a3061fb6ef6590f187649cca06", + Addresses: []string{ + "tb1qewygmc7gjec2xpslkmhkty83sajfejsxqmy5dq", + }, + }, + }, + }, + } } func TestPackTx(t *testing.T) { @@ -643,6 +675,17 @@ func TestPackTx(t *testing.T) { want: testTxPacked3, wantErr: false, }, + { + name: "testnet4-1", + args: args{ + tx: testTx4, + height: 41657, + blockTime: 1724927392, + parser: NewBitcoinParser(GetChainParams("testnet4"), &Configuration{}), + }, + want: testTxPacked4, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -701,6 +744,16 @@ func TestUnpackTx(t *testing.T) { want1: 15745, wantErr: false, }, + { + name: "testnet4-1", + args: args{ + packedTx: testTxPacked4, + parser: NewBitcoinParser(GetChainParams("testnet4"), &Configuration{}), + }, + want: &testTx4, + want1: 41657, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/tests/rpc/testdata/bitcoin_testnet4.json b/tests/rpc/testdata/bitcoin_testnet4.json new file mode 100644 index 0000000000..e61c5d87e6 --- /dev/null +++ b/tests/rpc/testdata/bitcoin_testnet4.json @@ -0,0 +1,105 @@ +{ + "blockHeight": 41500, + "blockHash": "000000000000000466119d6e5eb24802dcc14605f4050ac586f45eaa61da2719", + "blockTime": 1724848265, + "blockTxs": [ + "3d40148138492c4c0b91207acc2ec1cb3942e1cb51713e6851f01450452314d1", + "38924e01871d5fb25dca1bc9d17ae8cb65155fcb12a70984fc65ec85d48efd2a", + "8b77d1e7b5d7c528a59917c13f42787fa1988db744c1e9bc58f024f15fbb2ebb", + "06a9373ca11293ec51d15c5c142118fd46ceec33c0a46a865448f9916337b2ef" + ], + "txDetails": { + "38924e01871d5fb25dca1bc9d17ae8cb65155fcb12a70984fc65ec85d48efd2a": { + "hex": "0200000002a6a8a1e0e89cc206f40efc707863510b866cd0f20487446f6373c5b136ea9ab3010000006a4730440220053c7b24201514691f67154cbfd1e2ba917b3813b44b6ed81afd75bd11f16c4f022075c24b3fc21e88071148c6daa1ca4075e55da1f3f403ceb943268016744b10d1012102d1b7b25ab15f33fc693ba6c9b80b4c35fca1708008c8afac171b33f1fef4bd59fdffffffcd227a67d359ad8aaf99d9a56fdb0604a18804d40e046d21607f95a0c263e6d1000000006a473044022029297263b9b49c5652bf2179f5c94968788dc8d63d42a268980b8b9d0bda480602206c5cae1eb7b23872e02e2967eb229d8ed9cc73331dbadbd0354b82f80937a23e012103e959e8ad180e0323105e95ceea131debdbe0d77bfd54289bad77d15164942acdfdffffff03c3b0090000000000160014de4e79ce2048a42698e04e079e94c97fd6e012cf9f770e000000000017a914d9e303986df109b001b97b45f3a00d84b6c9d7278788760200000000001600144a6a08ffbb16515133284e385b0ea29812ce99251ba20000", + "txid": "38924e01871d5fb25dca1bc9d17ae8cb65155fcb12a70984fc65ec85d48efd2a", + "blocktime": 1724848265, + "time": 1724848265, + "locktime": 41499, + "vsize": 398, + "version": 2, + "vin": [ + { + "txid": "b39aea36b1c573636f448704f2d06c860b51637870fc0ef406c29ce8e0a1a8a6", + "vout": 1, + "sequence": 4294967293, + "scriptSig": { + "hex": "4730440220053c7b24201514691f67154cbfd1e2ba917b3813b44b6ed81afd75bd11f16c4f022075c24b3fc21e88071148c6daa1ca4075e55da1f3f403ceb943268016744b10d1012102d1b7b25ab15f33fc693ba6c9b80b4c35fca1708008c8afac171b33f1fef4bd59" + } + }, + { + "txid": "d1e663c2a0957f60216d040ed40488a10406db6fa5d999af8aad59d3677a22cd", + "vout": 0, + "sequence": 4294967293, + "scriptSig": { + "hex": "473044022029297263b9b49c5652bf2179f5c94968788dc8d63d42a268980b8b9d0bda480602206c5cae1eb7b23872e02e2967eb229d8ed9cc73331dbadbd0354b82f80937a23e012103e959e8ad180e0323105e95ceea131debdbe0d77bfd54289bad77d15164942acd" + } + } + ], + "vout": [ + { + "value": 0.00635075, + "n": 0, + "scriptPubKey": { + "hex": "0014de4e79ce2048a42698e04e079e94c97fd6e012cf" + } + }, + { + "value": 0.00948127, + "n": 1, + "scriptPubKey": { + "hex": "a914c9e67d2b78a38857c786ea9a2fc3e64cb6e7756487" + } + }, + { + "value": 0.00161416, + "n": 2, + "scriptPubKey": { + "hex": "00144a6a08ffbb16515133284e385b0ea29812ce9925" + } + } + ] + }, + "8b77d1e7b5d7c528a59917c13f42787fa1988db744c1e9bc58f024f15fbb2ebb": { + "hex": "0200000001cd227a67d359ad8aaf99d9a56fdb0604a18804d40e046d21607f95a0c263e6d1020000006a473044022027687d38378d1e6c991f68815217e309f1e290a8c706159455a680457ec1545002202dd0d9fc7251a5a4f7d4b76d824981b97a4c5121ec46fee4786a283debde544501210223a0cd87e2f1958998684f6c75771a95727d310cc4d30ed34ca427affe89d4c2fdffffff038876020000000000160014f6a58ba8a373263dddcb82bd6202a1157270cb4de8b00400000000001600144237fc8335d817b911332fc9df26744215266b1794d204000000000017a914e5bd951e8d6b10fab8cea5b103c71ae3a37b95bf871ba20000", + "txid": "8b77d1e7b5d7c528a59917c13f42787fa1988db744c1e9bc58f024f15fbb2ebb", + "blocktime": 1724848265, + "time": 1724848265, + "locktime": 41499, + "vsize": 251, + "version": 2, + "vin": [ + { + "txid": "d1e663c2a0957f60216d040ed40488a10406db6fa5d999af8aad59d3677a22cd", + "vout": 2, + "sequence": 4294967293, + "scriptSig": { + "hex": "473044022027687d38378d1e6c991f68815217e309f1e290a8c706159455a680457ec1545002202dd0d9fc7251a5a4f7d4b76d824981b97a4c5121ec46fee4786a283debde544501210223a0cd87e2f1958998684f6c75771a95727d310cc4d30ed34ca427affe89d4c2" + } + } + ], + "vout": [ + { + "value": 0.00161416, + "n": 0, + "scriptPubKey": { + "hex": "0014f6a58ba8a373263dddcb82bd6202a1157270cb4d" + } + }, + { + "value": 0.00307432, + "n": 1, + "scriptPubKey": { + "hex": "00144237fc8335d817b911332fc9df26744215266b17" + } + }, + { + "value": 0.00316052, + "n": 2, + "scriptPubKey": { + "hex": "a914e5bd951e8d6b10fab8cea5b103c71ae3a37b95bf87" + } + } + ] + } + } +} diff --git a/tests/sync/testdata/bitcoin_testnet4.json b/tests/sync/testdata/bitcoin_testnet4.json new file mode 100644 index 0000000000..5bd3c8e27f --- /dev/null +++ b/tests/sync/testdata/bitcoin_testnet4.json @@ -0,0 +1,266 @@ +{ + "connectBlocks": { + "syncRanges": [ + {"lower": 41500, "upper": 41514} + ], + "blocks": { + "41500": { + "height": 41500, + "hash": "000000000000000466119d6e5eb24802dcc14605f4050ac586f45eaa61da2719", + "noTxs": 4, + "txDetails": [ + { + "hex": "0200000001cd227a67d359ad8aaf99d9a56fdb0604a18804d40e046d21607f95a0c263e6d1020000006a473044022027687d38378d1e6c991f68815217e309f1e290a8c706159455a680457ec1545002202dd0d9fc7251a5a4f7d4b76d824981b97a4c5121ec46fee4786a283debde544501210223a0cd87e2f1958998684f6c75771a95727d310cc4d30ed34ca427affe89d4c2fdffffff038876020000000000160014f6a58ba8a373263dddcb82bd6202a1157270cb4de8b00400000000001600144237fc8335d817b911332fc9df26744215266b1794d204000000000017a914e5bd951e8d6b10fab8cea5b103c71ae3a37b95bf871ba20000", + "txid": "8b77d1e7b5d7c528a59917c13f42787fa1988db744c1e9bc58f024f15fbb2ebb", + "time": 1724848265, + "blocktime": 1724848265, + "version": 2, + "vin": [ + { + "txid": "d1e663c2a0957f60216d040ed40488a10406db6fa5d999af8aad59d3677a22cd", + "vout": 2, + "scriptSig": { + "hex": "473044022027687d38378d1e6c991f68815217e309f1e290a8c706159455a680457ec1545002202dd0d9fc7251a5a4f7d4b76d824981b97a4c5121ec46fee4786a283debde544501210223a0cd87e2f1958998684f6c75771a95727d310cc4d30ed34ca427affe89d4c2" + }, + "sequence": 4294967293 + } + ], + "vout": [ + { + "value": 0.00161416, + "n": 0, + "scriptPubKey": { + "hex": "0014f6a58ba8a373263dddcb82bd6202a1157270cb4d" + } + }, + { + "value": 0.00307432, + "n": 1, + "scriptPubKey": { + "hex": "00144237fc8335d817b911332fc9df26744215266b17" + } + } + ] + }, + { + "hex": "02000000030a33417aa2c909c65225b024e4988b46810202a7a57da1505a4ac79405ac4da4020000006a47304402204da60448cf946bc3ac839df244eef7eb5b04f6707be70382647c1fe4443936e102201fa6b33200b4835c6b67f2c31e63a76d2a6b962cc576f1b1b5cf09b4ba8f452c0121030a15ad4bbb816e75e733c12666af2a04bb55b7108f9c075e12104ac2a82fa326fdffffffcd227a67d359ad8aaf99d9a56fdb0604a18804d40e046d21607f95a0c263e6d1010000006a47304402207875d1ec865e2fdff50e6923b97a19f86597077685cc6d9c3b6af255dbd5e8bc022026177e9010f43b62bf1aab96b7a02ea639ad63b3cb2231603d9b0573d6a0049d012102373d8f65f846d07e07f74057b77bd81b4531cf95fc45040802a4f160271b47ddfdffffff7e848dbf9c2911fb316c47d5da66009836cd21a7d282dc3d9f2f993bec88c5d7000000006a47304402202a5dbdef43698a1027514585e1198e5d6ffcfbc60792ee48d979bfb40cfe6840022004d5819608fda4d16c5920f7f7ed5b4eb6eb90aa1fb3f21153055b83e89dd57d012102d6dd02728abb6829736d1cb14758361a15fd848db4283df310dc2084151c3908fdffffff039f770e00000000001600146a18cc237247b14c7b8bcb23c504a60b6c073bdf9db90d0000000000160014dda8363d492ccc33ef8c2ad02de0632bde0111aadd9202000000000017a914fa793409354d909ceaf168b7b7f91a92e0b4ba85871ba20000", + "txid": "06a9373ca11293ec51d15c5c142118fd46ceec33c0a46a865448f9916337b2ef", + "time": 1724848265, + "blocktime": 1724848265, + "version": 2, + "vin": [ + { + "txid": "a44dac0594c74a5a50a17da5a7020281468b98e424b02552c609c9a27a41330a", + "vout": 2, + "scriptSig": { + "hex": "47304402204da60448cf946bc3ac839df244eef7eb5b04f6707be70382647c1fe4443936e102201fa6b33200b4835c6b67f2c31e63a76d2a6b962cc576f1b1b5cf09b4ba8f452c0121030a15ad4bbb816e75e733c12666af2a04bb55b7108f9c075e12104ac2a82fa326" + }, + "sequence": 4294967293 + }, + { + "txid": "d1e663c2a0957f60216d040ed40488a10406db6fa5d999af8aad59d3677a22cd", + "vout": 1, + "scriptSig": { + "hex": "47304402207875d1ec865e2fdff50e6923b97a19f86597077685cc6d9c3b6af255dbd5e8bc022026177e9010f43b62bf1aab96b7a02ea639ad63b3cb2231603d9b0573d6a0049d012102373d8f65f846d07e07f74057b77bd81b4531cf95fc45040802a4f160271b47dd" + }, + "sequence": 4294967293 + }, + { + "txid": "d7c588ec3b992f9f3ddc82d2a721cd36980066dad5476c31fb11299cbf8d847e", + "vout": 0, + "scriptSig": { + "hex": "47304402202a5dbdef43698a1027514585e1198e5d6ffcfbc60792ee48d979bfb40cfe6840022004d5819608fda4d16c5920f7f7ed5b4eb6eb90aa1fb3f21153055b83e89dd57d012102d6dd02728abb6829736d1cb14758361a15fd848db4283df310dc2084151c3908" + }, + "sequence": 4294967293 + } + ], + "vout": [ + { + "value": 0.00948127, + "n": 0, + "scriptPubKey": { + "hex": "00146a18cc237247b14c7b8bcb23c504a60b6c073bdf" + } + }, + { + "value": 0.00899485, + "n": 1, + "scriptPubKey": { + "hex": "0014dda8363d492ccc33ef8c2ad02de0632bde0111aa" + } + }, + { + "value": 0.00168669, + "n": 1, + "scriptPubKey": { + "hex": "a914fa793409354d909ceaf168b7b7f91a92e0b4ba8587" + } + } + ] + } + ] + }, + "41514": { + "height": 41514, + "hash": "0000000000000002b7bdc99aec6aa3637ed2bfda355a1124b55c6e73362d20e3", + "noTxs": 4, + "txDetails": [ + { + "txid": "dc733fabf5035aaae5e006ed18007a0017945800a934df4ec3ce39a91575b8e8", + "version": 2, + "vin": [ + { + "txid": "989a37280f12b604db89ada924157118988e786bd962e57b77c08382da404cb8", + "vout": 1, + "scriptSig": { + "hex": "473044022061df7eaac833f84457a3636f017c25a3718570071fad06488a1f2558f270e1d3022028cf6a7964d91b16af7a5b3d45d334865857b3ffeb963b03aec57c1605f9e88201210347db551a1dddc6b9e33514b617e5f0d1a877d289438a3c2b660eade0b2167bb7" + }, + "sequence": 4294967293 + }, + { + "txid": "87311755ace5410b892a84319a721058a004a160484c672d74efe49b84fba877", + "vout": 2, + "scriptSig": { + "hex": "473044022061e33861ff14d7578b8bf5e0acbe411c23c245cb66867fec4a00d4da1d79886b02205363bca58a7419b63afcb5242e129ca049b39260706bcdc5d812217bb8549734012102fb95500bc0cfc2989caf311093d81bf4fbeac8fd6d7595e634a665062da27ece" + }, + "sequence": 4294967293 + }, + { + "txid": "841b8c2713db90d8b6f299c5f5ace825f7a2a1b85314c3c556b6a60888257e84", + "vout": 0, + "scriptSig": { + "hex": "47304402202a975b7766809be1cb6b3639a7ae19939c7ca65d9e52fef9c0fac974c50ab6c402204e3bd3df8e229a76a4e18ddb5bbcc57f3edb17e275e5a720eff87264fca6e6540121039d7a392480bcddc3b5ae486e8f42928aea3db0b29960a6eba2a74535d3666e5f" + }, + "sequence": 4294967293 + } + ], + "vout": [ + { + "value": 0.00590901, + "n": 0, + "scriptPubKey": { + "hex": "76a914a385f9839ca1052e69add674e34a86b5e2fee49488ac" + } + }, + { + "value": 0.00169446, + "n": 1, + "scriptPubKey": { + "hex": "0014064c311089eee424ba61ad731dd2b2a24b634920" + } + }, + { + "value": 0.00955888, + "n": 2, + "scriptPubKey": { + "hex": "0014b0bc24934c98b2eb46dcf3dea49adb6d689bf640" + } + } + ], + "hex": "0200000003b84c40da8283c0777be562d96b788e9818711524a9ad89db04b6120f28379a98010000006a473044022061df7eaac833f84457a3636f017c25a3718570071fad06488a1f2558f270e1d3022028cf6a7964d91b16af7a5b3d45d334865857b3ffeb963b03aec57c1605f9e88201210347db551a1dddc6b9e33514b617e5f0d1a877d289438a3c2b660eade0b2167bb7fdffffff77a8fb849be4ef742d674c4860a104a05810729a31842a890b41e5ac55173187020000006a473044022061e33861ff14d7578b8bf5e0acbe411c23c245cb66867fec4a00d4da1d79886b02205363bca58a7419b63afcb5242e129ca049b39260706bcdc5d812217bb8549734012102fb95500bc0cfc2989caf311093d81bf4fbeac8fd6d7595e634a665062da27ecefdffffff847e258808a6b656c5c31453b8a1a2f725e8acf5c599f2b6d890db13278c1b84000000006a47304402202a975b7766809be1cb6b3639a7ae19939c7ca65d9e52fef9c0fac974c50ab6c402204e3bd3df8e229a76a4e18ddb5bbcc57f3edb17e275e5a720eff87264fca6e6540121039d7a392480bcddc3b5ae486e8f42928aea3db0b29960a6eba2a74535d3666e5ffdffffff0335040900000000001976a914a385f9839ca1052e69add674e34a86b5e2fee49488ace695020000000000160014064c311089eee424ba61ad731dd2b2a24b634920f0950e0000000000160014b0bc24934c98b2eb46dcf3dea49adb6d689bf64029a20000", + "time": 1724854426, + "blocktime": 1724854426 + }, + { + "txid": "83e4e72359aad484639376259de9ea0dab88aa7b9b2d0a8ab654fe151cb10cbb", + "version": 2, + "vin": [ + { + "txid": "a6384c1718c87b699b11e0aa7e7ff5806c6053146f530de59989695b4a770957", + "vout": 1, + "scriptSig": { + "hex": "" + }, + "sequence": 4294967293 + }, + { + "txid": "64d1c85d3a0f4dc94d280a63d08ff4cae640f504b7e65d4d07afd8aa6e56127f", + "vout": 1, + "scriptSig": { + "hex": "" + }, + "sequence": 4294967293 + }, + { + "txid": "9ad596bbe3bff49476127cdada75ba54e269cabb8fa8bf83c4ea78037998ba32", + "vout": 2, + "scriptSig": { + "hex": "" + }, + "sequence": 4294967293 + }, + { + "txid": "6cbe7386a8085f440a25c8b5eafaf251e29b5d9894d0b78f1a058155e3cbb64a", + "vout": 1, + "scriptSig": { + "hex": "" + }, + "sequence": 4294967293 + }, + { + "txid": "44a3af6673cfc9bd746ba5f8183b5ffb48259cf2473fd300daf803125ba19576", + "vout": 0, + "scriptSig": { + "hex": "" + }, + "sequence": 4294967293 + } + ], + "vout": [ + { + "value": 0.00590901, + "n": 0, + "scriptPubKey": { + "hex": "a914a42e6b2b8198b25bdeb2bb45677ab2180d4847ae87" + } + }, + { + "value": 0.00774515, + "n": 1, + "scriptPubKey": { + "hex": "0014601770808f0168c4dc0d927759407047f079a0f9" + } + } + ], + "hex": "020000000001055709774a5b698999e50d536f1453606c80f57f7eaae0119b697bc818174c38a60100000000fdffffff7f12566eaad8af074d5de6b704f540e6caf48fd0630a284dc94d0f3a5dc8d1640100000000fdffffff32ba98790378eac483bfa88fbbca69e254ba75dada7c127694f4bfe3bb96d59a0200000000fdffffff4ab6cbe35581051a8fb7d094985d9be251f2faeab5c8250a445f08a88673be6c0100000000fdffffff7695a15b1203f8da00d33f47f29c2548fb5f3b18f8a56b74bdc9cf7366afa3440000000000fdffffff02350409000000000017a914a42e6b2b8198b25bdeb2bb45677ab2180d4847ae8773d10b0000000000160014601770808f0168c4dc0d927759407047f079a0f9024730440220703d8e59902e1f2e06b343588d88e84433cea072b5004c5c4dff0dd9787b3d2f022070924c133eba86760eebc91e09dad2d0db08f684b1976ca1b4d1f3421f2c084d012103f4bf0506968dd6eb701d0e40ab6ea02d5179a6550a761801684bd4384c0e79eb02473044022057bf3d62109758c6bc278ab54c15d43c95aa9c0f11548e76a5e1ca8ae103508002201d9f503b33d930d128fe102f5b1622dc15c32ef00268e58980e2b61ce77c33200121024f8884e9ebe6bf9c6492a662bf4bb3d30a55072cee98c82b8a17a22111ef4cf602473044022072788e6901a2d27b2db22ebef67aec7704aacbe003d96f0b8bf118c6ae5c0040022029dacac606daa4017e6e8e78efb139de5fb846599b67d501ef5851c302dee3fc012102718eb94089203414331e1291f30ebc139b087fde31dde1cfad7db22327bc69f40247304402202c927e0ccb0ec9cb8ad76206ad5ed95572df8c600bb1eb03de0f101d287cb121022050ddb0e7768dfe16788f1a1b835a8ad3b5be6fef0a2093781977aebf6e5d8a9f0121027e18fe1411ba7a2296c47ff8397e92986ce0836897bb42270b6dd86b2814ea1302473044022028d03205ca7736a78feb0c0b507df77952dd3c2a6947cdd9f9be58ef9094f9a20220768e22de86fc6683cec1c12d72f8ea5d6d0583ba171a69e1791bb46b399eff22012102d24813d067d189c0d15bfe62a24ef4295100cd0f80b0286de1dfae66540dfc4b29a20000", + "time": 1724854426, + "blocktime": 1724854426 + } + ] + } + } + }, + "handleFork": { + "syncRanges": [ + {"lower": 41480, "upper": 41499} + ], + "fakeBlocks": { + "41497": { + "height": 41497, + "hash": "0000000000000016204d49115b14be42a5022d0b4fd3e955ebd93e0933e6c6a1" + }, + "41498": { + "height": 41498, + "hash": "00000000000000064cebdc2a2fdcc74810325b4de8b4297e6cdd4fbdda221192" + }, + "41499": { + "height": 41499, + "hash": "00000000a4da915dca73162b98b671dd60b5a52208abf159f2ea7b11f08b1989" + } + }, + "realBlocks": { + "41497": { + "height": 41497, + "hash": "00000000000000098b5976b60433bbbf44b73681282e2bf7ee6186e9767c2ced" + }, + "41498": { + "height": 41498, + "hash": "000000000af275137c4183636a8c3fbe1ed9f8b30345daa89dc09be08822efd6" + }, + "41499": { + "height": 41499, + "hash": "00000000da2db0a996113cf34bdaacb9ce31ebc6a172f640601d3aec6936f8b5" + } + } + } +} diff --git a/tests/tests.json b/tests/tests.json index 5c0a0592eb..f99a222d8b 100644 --- a/tests/tests.json +++ b/tests/tests.json @@ -30,6 +30,11 @@ "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"], "sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"] }, + "bitcoin_testnet4": { + "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", + "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"], + "sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"] + }, "bitcoin_signet": { "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"], From 132bd77af7dc78d8168416341dbddcc224e7f700 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 12 Nov 2024 13:11:37 +0000 Subject: [PATCH 379/530] =?UTF-8?q?ltc=20(+testnet)=200.21.3=20=E2=86=92?= =?UTF-8?q?=200.21.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/litecoin.json | 10 +++++----- configs/coins/litecoin_testnet.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/configs/coins/litecoin.json b/configs/coins/litecoin.json index 6b21224589..026665c1df 100644 --- a/configs/coins/litecoin.json +++ b/configs/coins/litecoin.json @@ -22,10 +22,10 @@ "package_name": "backend-litecoin", "package_revision": "satoshilabs-1", "system_user": "litecoin", - "version": "0.21.3", - "binary_url": "https://download.litecoin.org/litecoin-0.21.3/linux/litecoin-0.21.3-x86_64-linux-gnu.tar.gz", + "version": "0.21.4", + "binary_url": "https://download.litecoin.org/litecoin-0.21.4/linux/litecoin-0.21.4-x86_64-linux-gnu.tar.gz", "verification_type": "gpg", - "verification_source": "https://download.litecoin.org/litecoin-0.21.3/linux/litecoin-0.21.3-x86_64-linux-gnu.tar.gz.asc", + "verification_source": "https://download.litecoin.org/litecoin-0.21.4/linux/litecoin-0.21.4-x86_64-linux-gnu.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/litecoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/litecoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://download.litecoin.org/litecoin-0.21.3/linux/litecoin-0.21.3-aarch64-linux-gnu.tar.gz", - "verification_source": "https://download.litecoin.org/litecoin-0.21.3/linux/litecoin-0.21.3-aarch64-linux-gnu.tar.gz.asc" + "binary_url": "https://download.litecoin.org/litecoin-0.21.4/linux/litecoin-0.21.4-aarch64-linux-gnu.tar.gz", + "verification_source": "https://download.litecoin.org/litecoin-0.21.4/linux/litecoin-0.21.4-aarch64-linux-gnu.tar.gz.asc" } } }, diff --git a/configs/coins/litecoin_testnet.json b/configs/coins/litecoin_testnet.json index 25d2ce578e..0d0962b344 100644 --- a/configs/coins/litecoin_testnet.json +++ b/configs/coins/litecoin_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-litecoin-testnet", "package_revision": "satoshilabs-1", "system_user": "litecoin", - "version": "0.21.3", - "binary_url": "https://download.litecoin.org/litecoin-0.21.3/linux/litecoin-0.21.3-x86_64-linux-gnu.tar.gz", + "version": "0.21.4", + "binary_url": "https://download.litecoin.org/litecoin-0.21.4/linux/litecoin-0.21.4-x86_64-linux-gnu.tar.gz", "verification_type": "gpg", - "verification_source": "https://download.litecoin.org/litecoin-0.21.3/linux/litecoin-0.21.3-x86_64-linux-gnu.tar.gz.asc", + "verification_source": "https://download.litecoin.org/litecoin-0.21.4/linux/litecoin-0.21.4-x86_64-linux-gnu.tar.gz.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/litecoin-qt" @@ -44,8 +44,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://download.litecoin.org/litecoin-0.21.3/linux/litecoin-0.21.3-aarch64-linux-gnu.tar.gz", - "verification_source": "https://download.litecoin.org/litecoin-0.21.3/linux/litecoin-0.21.3-aarch64-linux-gnu.tar.gz.asc" + "binary_url": "https://download.litecoin.org/litecoin-0.21.4/linux/litecoin-0.21.4-aarch64-linux-gnu.tar.gz", + "verification_source": "https://download.litecoin.org/litecoin-0.21.4/linux/litecoin-0.21.4-aarch64-linux-gnu.tar.gz.asc" } } }, From afe4749f6d916cabb0eb9cf513db6a9cff944178 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 12 Nov 2024 12:36:43 +0000 Subject: [PATCH 380/530] =?UTF-8?q?eth=20(+testnets)=202.60.8=20=E2=86=92?= =?UTF-8?q?=202.60.10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_holesky.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_archive.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 90aab017b6..c81e86602c 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.8", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_amd64.tar.gz", + "version": "2.60.10", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "02e9f46a0689c458045db113f9b356fb23f549e4bc2815826301e874a94f52d4", + "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_arm64.tar.gz", - "verification_source": "0c19123af4bcf510e70a98de6bc882cf55c4b66cef841fdcedb8aa7c6c65d698" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", + "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index ecf662e0c5..6bab22394f 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.8", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_amd64.tar.gz", + "version": "2.60.10", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "02e9f46a0689c458045db113f9b356fb23f549e4bc2815826301e874a94f52d4", + "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_arm64.tar.gz", - "verification_source": "0c19123af4bcf510e70a98de6bc882cf55c4b66cef841fdcedb8aa7c6c65d698" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", + "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" } } }, diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index 88dbaac2b7..7dceab3677 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.8", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_amd64.tar.gz", + "version": "2.60.10", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "02e9f46a0689c458045db113f9b356fb23f549e4bc2815826301e874a94f52d4", + "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_arm64.tar.gz", - "verification_source": "0c19123af4bcf510e70a98de6bc882cf55c4b66cef841fdcedb8aa7c6c65d698" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", + "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 99191e8315..3474b63a79 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.8", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_amd64.tar.gz", + "version": "2.60.10", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "02e9f46a0689c458045db113f9b356fb23f549e4bc2815826301e874a94f52d4", + "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_arm64.tar.gz", - "verification_source": "0c19123af4bcf510e70a98de6bc882cf55c4b66cef841fdcedb8aa7c6c65d698" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", + "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 195faa55bb..dfca9bf914 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.8", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_amd64.tar.gz", + "version": "2.60.10", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "02e9f46a0689c458045db113f9b356fb23f549e4bc2815826301e874a94f52d4", + "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_arm64.tar.gz", - "verification_source": "0c19123af4bcf510e70a98de6bc882cf55c4b66cef841fdcedb8aa7c6c65d698" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", + "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 2ab9faea8a..35926e52a5 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.8", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_amd64.tar.gz", + "version": "2.60.10", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "02e9f46a0689c458045db113f9b356fb23f549e4bc2815826301e874a94f52d4", + "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/2.60.8/erigon_2.60.8_linux_arm64.tar.gz", - "verification_source": "0c19123af4bcf510e70a98de6bc882cf55c4b66cef841fdcedb8aa7c6c65d698" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", + "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" } } }, From d6aaa09e0627c8f971771abc4102499e655077cd Mon Sep 17 00:00:00 2001 From: kevin <35275952+kaladinlight@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:36:07 -0700 Subject: [PATCH 381/530] Add Arbitrum One and Arbitrum Nova Support (#1112) * add arbitrum one and arbitrum nova support * fix archive parent chain ports * fix exec script config * update nitro-node version --- Makefile | 5 +- bchain/coins/arbitrum/arbitrumrpc.go | 77 +++++++++++++++++++ bchain/coins/blockchain.go | 5 ++ build/docker/deb/Dockerfile | 10 +++ build/templates/backend/Makefile | 12 ++- build/templates/backend/scripts/arbitrum.sh | 34 ++++++++ .../backend/scripts/arbitrum_archive.sh | 35 +++++++++ .../backend/scripts/arbitrum_nova.sh | 34 ++++++++ .../backend/scripts/arbitrum_nova_archive.sh | 35 +++++++++ build/tools/templates.go | 2 + configs/coins/arbitrum.json | 65 ++++++++++++++++ configs/coins/arbitrum_archive.json | 67 ++++++++++++++++ configs/coins/arbitrum_nova.json | 65 ++++++++++++++++ configs/coins/arbitrum_nova_archive.json | 67 ++++++++++++++++ docs/ports.md | 4 + 15 files changed, 514 insertions(+), 3 deletions(-) create mode 100644 bchain/coins/arbitrum/arbitrumrpc.go create mode 100755 build/templates/backend/scripts/arbitrum.sh create mode 100755 build/templates/backend/scripts/arbitrum_archive.sh create mode 100755 build/templates/backend/scripts/arbitrum_nova.sh create mode 100755 build/templates/backend/scripts/arbitrum_nova_archive.sh create mode 100644 configs/coins/arbitrum.json create mode 100644 configs/coins/arbitrum_archive.json create mode 100644 configs/coins/arbitrum_nova.json create mode 100644 configs/coins/arbitrum_nova_archive.json diff --git a/Makefile b/Makefile index dfe5b5f395..54475015dc 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ BIN_IMAGE = blockbook-build DEB_IMAGE = blockbook-build-deb PACKAGER = $(shell id -u):$(shell id -g) +DOCKER_VERSION = $(shell docker version --format '{{.Client.Version}}') BASE_IMAGE = $$(awk -F= '$$1=="ID" { print $$2 ;}' /etc/os-release):$$(awk -F= '$$1=="VERSION_ID" { print $$2 ;}' /etc/os-release | tr -d '"') NO_CACHE = false TCMALLOC = @@ -27,7 +28,7 @@ test-all: .bin-image docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" --network="host" $(BIN_IMAGE) make test-all ARGS="$(ARGS)" deb-backend-%: .deb-image - docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh backend $* $(ARGS) + docker run -t --rm -e PACKAGER=$(PACKAGER) -v /var/run/docker.sock:/var/run/docker.sock -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh backend $* $(ARGS) deb-blockbook-%: .deb-image docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh blockbook $* $(ARGS) @@ -55,7 +56,7 @@ build-images: clean-images .deb-image: .bin-image @if [ $$(build/tools/image_status.sh $(DEB_IMAGE):latest build/docker) != "ok" ]; then \ echo "Building image $(DEB_IMAGE)..."; \ - docker build --no-cache=$(NO_CACHE) -t $(DEB_IMAGE) build/docker/deb; \ + docker build --no-cache=$(NO_CACHE) --build-arg DOCKER_VERSION=$(DOCKER_VERSION) -t $(DEB_IMAGE) build/docker/deb; \ else \ echo "Image $(DEB_IMAGE) is up to date"; \ fi diff --git a/bchain/coins/arbitrum/arbitrumrpc.go b/bchain/coins/arbitrum/arbitrumrpc.go new file mode 100644 index 0000000000..e8c2535438 --- /dev/null +++ b/bchain/coins/arbitrum/arbitrumrpc.go @@ -0,0 +1,77 @@ +package arbitrum + +import ( + "context" + "encoding/json" + + "github.com/golang/glog" + "github.com/juju/errors" + "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/bchain/coins/eth" +) + +const ( + ArbitrumOneMainNet eth.Network = 42161 + ArbitrumNovaMainNet eth.Network = 42170 +) + +// ArbitrumRPC is an interface to JSON-RPC arbitrum service. +type ArbitrumRPC struct { + *eth.EthereumRPC +} + +// NewArbitrumRPC returns new ArbitrumRPC instance. +func NewArbitrumRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { + c, err := eth.NewEthereumRPC(config, pushHandler) + if err != nil { + return nil, err + } + + s := &ArbitrumRPC{ + EthereumRPC: c.(*eth.EthereumRPC), + } + + return s, nil +} + +// Initialize arbitrum rpc interface +func (b *ArbitrumRPC) Initialize() error { + b.OpenRPC = eth.OpenRPC + + rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL) + if err != nil { + return err + } + + // set chain specific + b.Client = ec + b.RPC = rc + b.NewBlock = eth.NewEthereumNewBlock() + b.NewTx = eth.NewEthereumNewTx() + + ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) + defer cancel() + + id, err := b.Client.NetworkID(ctx) + if err != nil { + return err + } + + // parameters for getInfo request + switch eth.Network(id.Uint64()) { + case ArbitrumOneMainNet: + b.MainNetChainID = ArbitrumOneMainNet + b.Testnet = false + b.Network = "livenet" + case ArbitrumNovaMainNet: + b.MainNetChainID = ArbitrumNovaMainNet + b.Testnet = false + b.Network = "livenet" + default: + return errors.Errorf("Unknown network id %v", id) + } + + glog.Info("rpc: block chain ", b.Network) + + return nil +} diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index f0cfa3cc9e..f3dac986ff 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -11,6 +11,7 @@ import ( "github.com/juju/errors" "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/bchain/coins/arbitrum" "github.com/trezor/blockbook/bchain/coins/avalanche" "github.com/trezor/blockbook/bchain/coins/bch" "github.com/trezor/blockbook/bchain/coins/bellcoin" @@ -142,6 +143,10 @@ func init() { BlockChainFactories["Polygon Archive"] = polygon.NewPolygonRPC BlockChainFactories["Optimism"] = optimism.NewOptimismRPC BlockChainFactories["Optimism Archive"] = optimism.NewOptimismRPC + BlockChainFactories["Arbitrum"] = arbitrum.NewArbitrumRPC + BlockChainFactories["Arbitrum Archive"] = arbitrum.NewArbitrumRPC + BlockChainFactories["Arbitrum Nova"] = arbitrum.NewArbitrumRPC + BlockChainFactories["Arbitrum Nova Archive"] = arbitrum.NewArbitrumRPC } // NewBlockChain creates bchain.BlockChain and bchain.Mempool for the coin passed by the parameter coin diff --git a/build/docker/deb/Dockerfile b/build/docker/deb/Dockerfile index b6632379de..fd8fa114ef 100644 --- a/build/docker/deb/Dockerfile +++ b/build/docker/deb/Dockerfile @@ -9,6 +9,16 @@ RUN apt-get update && \ apt-get install -y devscripts debhelper make dh-exec zstd && \ apt-get clean +# install docker cli +ARG DOCKER_VERSION + +RUN if [ -z "$DOCKER_VERSION" ]; then echo "DOCKER_VERSION is a required build arg" && exit 1; fi + +RUN wget -O docker.tgz "https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz" && \ + tar -xzf docker.tgz --strip 1 -C /usr/local/bin/ && \ + rm docker.tgz && \ + docker --version + ADD gpg-keys /tmp/gpg-keys RUN gpg --batch --import /tmp/gpg-keys/* diff --git a/build/templates/backend/Makefile b/build/templates/backend/Makefile index 5b9e0bd4fa..de5440aa8f 100644 --- a/build/templates/backend/Makefile +++ b/build/templates/backend/Makefile @@ -2,6 +2,16 @@ ARCHIVE := $(shell basename {{.Backend.BinaryURL}}) all: + mkdir backend +{{- if ne .Backend.DockerImage "" }} + docker container inspect extract > /dev/null 2>&1 && docker rm extract || true + docker create --name extract {{.Backend.DockerImage}} +{{- if eq .Backend.VerificationType "docker"}} + [ "$$(docker inspect --format='{{`{{index .RepoDigests 0}}`}}' {{.Backend.DockerImage}} | sed 's/.*@sha256://')" = "{{.Backend.VerificationSource}}" ] +{{- end}} + {{.Backend.ExtractCommand}} + docker rm extract +{{- else }} wget {{.Backend.BinaryURL}} {{- if eq .Backend.VerificationType "gpg"}} wget {{.Backend.VerificationSource}} -O checksum @@ -13,8 +23,8 @@ all: {{- else if eq .Backend.VerificationType "sha256"}} [ "$$(sha256sum ${ARCHIVE} | cut -d ' ' -f 1)" = "{{.Backend.VerificationSource}}" ] {{- end}} - mkdir backend {{.Backend.ExtractCommand}} ${ARCHIVE} +{{- end}} {{- if .Backend.ExcludeFiles}} # generated from exclude_files {{- range $index, $name := .Backend.ExcludeFiles}} diff --git a/build/templates/backend/scripts/arbitrum.sh b/build/templates/backend/scripts/arbitrum.sh new file mode 100755 index 0000000000..0872739c21 --- /dev/null +++ b/build/templates/backend/scripts/arbitrum.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +{{define "main" -}} + +set -e + +INSTALL_DIR={{.Env.BackendInstallPath}}/{{.Coin.Alias}} +DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend + +NITRO_BIN=$INSTALL_DIR/nitro + +$NITRO_BIN \ + --chain.name arb1 \ + --init.latest pruned \ + --init.download-path $DATA_DIR/tmp \ + --auth.jwtsecret $DATA_DIR/jwtsecret \ + --persistent.chain $DATA_DIR \ + --parent-chain.connection.url http://127.0.0.1:8136 \ + --parent-chain.blob-client.beacon-url http://127.0.0.1:7536 \ + --http.addr 127.0.0.1 \ + --http.port {{.Ports.BackendHttp}} \ + --http.api eth,net,web3,debug,txpool,arb \ + --http.vhosts '*' \ + --http.corsdomain '*' \ + --ws.addr 127.0.0.1 \ + --ws.api eth,net,web3,debug,txpool,arb \ + --ws.port {{.Ports.BackendRPC}} \ + --ws.origins '*' \ + --file-logging.enable='false' \ + --node.staker.enable='false' \ + --execution.tx-lookup-limit 0 \ + --validation.wasm.allowed-wasm-module-roots "$INSTALL_DIR/nitro-legacy/machines,$INSTALL_DIR/target/machines" + +{{end}} \ No newline at end of file diff --git a/build/templates/backend/scripts/arbitrum_archive.sh b/build/templates/backend/scripts/arbitrum_archive.sh new file mode 100755 index 0000000000..27c7d6dabd --- /dev/null +++ b/build/templates/backend/scripts/arbitrum_archive.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +{{define "main" -}} + +set -e + +INSTALL_DIR={{.Env.BackendInstallPath}}/{{.Coin.Alias}} +DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend + +NITRO_BIN=$INSTALL_DIR/nitro + +$NITRO_BIN \ + --chain.name arb1 \ + --init.latest archive \ + --init.download-path $DATA_DIR/tmp \ + --auth.jwtsecret $DATA_DIR/jwtsecret \ + --persistent.chain $DATA_DIR \ + --parent-chain.connection.url http://127.0.0.1:8116 \ + --parent-chain.blob-client.beacon-url http://127.0.0.1:7516 \ + --http.addr 127.0.0.1 \ + --http.port {{.Ports.BackendHttp}} \ + --http.api eth,net,web3,debug,txpool,arb \ + --http.vhosts '*' \ + --http.corsdomain '*' \ + --ws.addr 127.0.0.1 \ + --ws.api eth,net,web3,debug,txpool,arb \ + --ws.port {{.Ports.BackendRPC}} \ + --ws.origins '*' \ + --file-logging.enable='false' \ + --node.staker.enable='false' \ + --execution.caching.archive \ + --execution.tx-lookup-limit 0 \ + --validation.wasm.allowed-wasm-module-roots "$INSTALL_DIR/nitro-legacy/machines,$INSTALL_DIR/target/machines" + +{{end}} \ No newline at end of file diff --git a/build/templates/backend/scripts/arbitrum_nova.sh b/build/templates/backend/scripts/arbitrum_nova.sh new file mode 100755 index 0000000000..3f15e4ef15 --- /dev/null +++ b/build/templates/backend/scripts/arbitrum_nova.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +{{define "main" -}} + +set -e + +INSTALL_DIR={{.Env.BackendInstallPath}}/{{.Coin.Alias}} +DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend + +NITRO_BIN=$INSTALL_DIR/nitro + +$NITRO_BIN \ + --chain.name nova \ + --init.latest pruned \ + --init.download-path $DATA_DIR/tmp \ + --auth.jwtsecret $DATA_DIR/jwtsecret \ + --persistent.chain $DATA_DIR \ + --parent-chain.connection.url http://127.0.0.1:8136 \ + --parent-chain.blob-client.beacon-url http://127.0.0.1:7536 \ + --http.addr 127.0.0.1 \ + --http.port {{.Ports.BackendHttp}} \ + --http.api eth,net,web3,debug,txpool,arb \ + --http.vhosts '*' \ + --http.corsdomain '*' \ + --ws.addr 127.0.0.1 \ + --ws.api eth,net,web3,debug,txpool,arb \ + --ws.port {{.Ports.BackendRPC}} \ + --ws.origins '*' \ + --file-logging.enable='false' \ + --node.staker.enable='false' \ + --execution.tx-lookup-limit 0 \ + --validation.wasm.allowed-wasm-module-roots "$INSTALL_DIR/nitro-legacy/machines,$INSTALL_DIR/target/machines" + +{{end}} \ No newline at end of file diff --git a/build/templates/backend/scripts/arbitrum_nova_archive.sh b/build/templates/backend/scripts/arbitrum_nova_archive.sh new file mode 100755 index 0000000000..eb150f79b4 --- /dev/null +++ b/build/templates/backend/scripts/arbitrum_nova_archive.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +{{define "main" -}} + +set -e + +INSTALL_DIR={{.Env.BackendInstallPath}}/{{.Coin.Alias}} +DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend + +NITRO_BIN=$INSTALL_DIR/nitro + +$NITRO_BIN \ + --chain.name nova \ + --init.latest archive \ + --init.download-path $DATA_DIR/tmp \ + --auth.jwtsecret $DATA_DIR/jwtsecret \ + --persistent.chain $DATA_DIR \ + --parent-chain.connection.url http://127.0.0.1:8116 \ + --parent-chain.blob-client.beacon-url http://127.0.0.1:7516 \ + --http.addr 127.0.0.1 \ + --http.port {{.Ports.BackendHttp}} \ + --http.api eth,net,web3,debug,txpool,arb \ + --http.vhosts '*' \ + --http.corsdomain '*' \ + --ws.addr 127.0.0.1 \ + --ws.api eth,net,web3,debug,txpool,arb \ + --ws.port {{.Ports.BackendRPC}} \ + --ws.origins '*' \ + --file-logging.enable='false' \ + --node.staker.enable='false' \ + --execution.caching.archive \ + --execution.tx-lookup-limit 0 \ + --validation.wasm.allowed-wasm-module-roots "$INSTALL_DIR/nitro-legacy/machines,$INSTALL_DIR/target/machines" + +{{end}} \ No newline at end of file diff --git a/build/tools/templates.go b/build/tools/templates.go index 8dfb9fb042..03113d2a1b 100644 --- a/build/tools/templates.go +++ b/build/tools/templates.go @@ -21,6 +21,7 @@ type Backend struct { SystemUser string `json:"system_user"` Version string `json:"version"` BinaryURL string `json:"binary_url"` + DockerImage string `json:"docker_image"` VerificationType string `json:"verification_type"` VerificationSource string `json:"verification_source"` ExtractCommand string `json:"extract_command"` @@ -204,6 +205,7 @@ func LoadConfig(configsDir, coin string) (*Config, error) { case "gpg": case "sha256": case "gpg-sha256": + case "docker": default: return nil, fmt.Errorf("Invalid verification type: %s", config.Backend.VerificationType) } diff --git a/configs/coins/arbitrum.json b/configs/coins/arbitrum.json new file mode 100644 index 0000000000..223fa6f9a3 --- /dev/null +++ b/configs/coins/arbitrum.json @@ -0,0 +1,65 @@ +{ + "coin": { + "name": "Arbitrum", + "shortcut": "ETH", + "label": "Arbitrum", + "alias": "arbitrum" + }, + "ports": { + "backend_rpc": 8205, + "backend_p2p": 38405, + "backend_http": 8305, + "blockbook_internal": 9205, + "blockbook_public": 9305 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-arbitrum", + "package_revision": "satoshilabs-1", + "system_user": "arbitrum", + "version": "3.2.1", + "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", + "verification_type": "docker", + "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", + "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "arbitrum.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-arbitrum", + "system_user": "blockbook-arbitrum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "additional_params": { + "mempoolTxTimeoutHours": 48, + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/arbitrum_archive.json b/configs/coins/arbitrum_archive.json new file mode 100644 index 0000000000..4d672f80dd --- /dev/null +++ b/configs/coins/arbitrum_archive.json @@ -0,0 +1,67 @@ +{ + "coin": { + "name": "Arbitrum Archive", + "shortcut": "ETH", + "label": "Arbitrum", + "alias": "arbitrum_archive" + }, + "ports": { + "backend_rpc": 8306, + "backend_p2p": 38406, + "blockbook_internal": 9206, + "blockbook_public": 9306 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-arbitrum-archive", + "package_revision": "satoshilabs-1", + "system_user": "arbitrum", + "version": "3.2.1", + "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", + "verification_type": "docker", + "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", + "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_archive_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "arbitrum_archive.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-arbitrum-archive", + "system_user": "blockbook-arbitrum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "-workers=16", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 600, + "additional_params": { + "address_aliases": true, + "mempoolTxTimeoutHours": 48, + "processInternalTransactions": true, + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/arbitrum_nova.json b/configs/coins/arbitrum_nova.json new file mode 100644 index 0000000000..0d0a252b2e --- /dev/null +++ b/configs/coins/arbitrum_nova.json @@ -0,0 +1,65 @@ +{ + "coin": { + "name": "Arbitrum Nova", + "shortcut": "ETH", + "label": "Arbitrum Nova", + "alias": "arbitrum_nova" + }, + "ports": { + "backend_rpc": 8207, + "backend_p2p": 38407, + "backend_http": 8307, + "blockbook_internal": 9207, + "blockbook_public": 9307 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-arbitrum-nova", + "package_revision": "satoshilabs-1", + "system_user": "arbitrum", + "version": "3.2.1", + "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", + "verification_type": "docker", + "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", + "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_nova_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "arbitrum_nova.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-arbitrum-nova", + "system_user": "blockbook-arbitrum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "additional_params": { + "mempoolTxTimeoutHours": 48, + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/arbitrum_nova_archive.json b/configs/coins/arbitrum_nova_archive.json new file mode 100644 index 0000000000..0510597649 --- /dev/null +++ b/configs/coins/arbitrum_nova_archive.json @@ -0,0 +1,67 @@ +{ + "coin": { + "name": "Arbitrum Nova Archive", + "shortcut": "ETH", + "label": "Arbitrum Nova", + "alias": "arbitrum_nova_archive" + }, + "ports": { + "backend_rpc": 8308, + "backend_p2p": 38408, + "blockbook_internal": 9208, + "blockbook_public": 9308 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-arbitrum-nova-archive", + "package_revision": "satoshilabs-1", + "system_user": "arbitrum", + "version": "3.2.1", + "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", + "verification_type": "docker", + "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", + "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_nova_archive_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "arbitrum_nova_archive.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-arbitrum-nova-archive", + "system_user": "blockbook-arbitrum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "-workers=16", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 600, + "additional_params": { + "address_aliases": true, + "mempoolTxTimeoutHours": 48, + "processInternalTransactions": true, + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/docs/ports.md b/docs/ports.md index bbb1bac1e8..fe51bbb8ef 100644 --- a/docs/ports.md +++ b/docs/ports.md @@ -55,6 +55,10 @@ | Avalanche Archive | 9199 | 9099 | 8099 | 38399 p2p | | Optimism | 9300 | 9200 | 8200 | 38400 p2p, 8300 http, 8400 authrpc | | Optimism Archive | 9302 | 9202 | 8202 | 38402 p2p, 8302 http, 8402 authrpc | +| Arbitrum | 9305 | 9205 | 8205 | 38405 p2p, 8305 http | +| Arbitrum Archive | 9306 | 9206 | 8306 | 38406 p2p | +| Arbitrum Nova | 9307 | 9207 | 8207 | 38407 p2p, 8307 http | +| Arbitrum Nova Archive | 9308 | 9208 | 8308 | 38408 p2p | | Ethereum Testnet Holesky | 19116 | 19016 | 18016 | 18116 http, 18516 authrpc, 48316 p2p | | Bitcoin Signet | 19120 | 19020 | 18020 | 48320 | | Bitcoin Regtest | 19121 | 19021 | 18021 | 48321 | From 04a5d8d95dfb6375f083b33f845c73b688de60e2 Mon Sep 17 00:00:00 2001 From: grdddj Date: Tue, 15 Oct 2024 12:07:16 +0200 Subject: [PATCH 382/530] chore: add make style target for gofmt formatting --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 54475015dc..9c01550463 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ PACKAGER = $(shell id -u):$(shell id -g) DOCKER_VERSION = $(shell docker version --format '{{.Client.Version}}') BASE_IMAGE = $$(awk -F= '$$1=="ID" { print $$2 ;}' /etc/os-release):$$(awk -F= '$$1=="VERSION_ID" { print $$2 ;}' /etc/os-release | tr -d '"') NO_CACHE = false -TCMALLOC = +TCMALLOC = PORTABLE = 0 ARGS ?= @@ -80,3 +80,6 @@ clean-bin-image: clean-deb-image: - docker rmi $(DEB_IMAGE) + +style: + find . -name "*.go" -exec gofmt -w {} \; From 66b4ddbe01d724f718e882e2387dabda190a7e44 Mon Sep 17 00:00:00 2001 From: grdddj Date: Tue, 15 Oct 2024 12:07:39 +0200 Subject: [PATCH 383/530] chore: apply make style gofmt formatting --- bchain/coins/ecash/ecashparser.go | 2 +- bchain/coins/firo/firoparser.go | 17 ++++++++--------- build/tools/trezor-common/sync-coins.go | 2 +- common/jsonnumber.go | 4 +++- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/bchain/coins/ecash/ecashparser.go b/bchain/coins/ecash/ecashparser.go index e4547ae8ce..2b4ef88184 100644 --- a/bchain/coins/ecash/ecashparser.go +++ b/bchain/coins/ecash/ecashparser.go @@ -3,11 +3,11 @@ package ecash import ( "fmt" - "github.com/pirk/ecashutil" "github.com/martinboehm/btcutil" "github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/txscript" "github.com/pirk/ecashaddr-converter/address" + "github.com/pirk/ecashutil" "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain/coins/btc" ) diff --git a/bchain/coins/firo/firoparser.go b/bchain/coins/firo/firoparser.go index d2b9e1f0bd..cfdf9c4a7f 100644 --- a/bchain/coins/firo/firoparser.go +++ b/bchain/coins/firo/firoparser.go @@ -14,13 +14,13 @@ import ( ) const ( - OpZeroCoinMint = 0xc1 - OpZeroCoinSpend = 0xc2 - OpSigmaMint = 0xc3 - OpSigmaSpend = 0xc4 - OpLelantusMint = 0xc5 - OpLelantusJMint = 0xc6 - OpLelantusJoinSplit = 0xc7 + OpZeroCoinMint = 0xc1 + OpZeroCoinSpend = 0xc2 + OpSigmaMint = 0xc3 + OpSigmaSpend = 0xc4 + OpLelantusMint = 0xc5 + OpLelantusJMint = 0xc6 + OpLelantusJoinSplit = 0xc7 OpLelantusJoinSplitPayload = 0xc9 MainnetMagic wire.BitcoinNet = 0xe3d9fef1 @@ -194,7 +194,6 @@ func (p *FiroParser) ParseBlock(b []byte) (*bchain.Block, error) { break } } - if !isAllZero { // hash data @@ -344,7 +343,7 @@ type MTPHashDataRoot struct { } type MTPHashData struct { - BlockMTP [128][128]uint64 + BlockMTP [128][128]uint64 } type MTPBlockHeader struct { diff --git a/build/tools/trezor-common/sync-coins.go b/build/tools/trezor-common/sync-coins.go index f4e90ba14e..acb5518e39 100644 --- a/build/tools/trezor-common/sync-coins.go +++ b/build/tools/trezor-common/sync-coins.go @@ -1,4 +1,4 @@ -//usr/bin/go run $0 $@ ; exit +// usr/bin/go run $0 $@ ; exit package main import ( diff --git a/common/jsonnumber.go b/common/jsonnumber.go index d209fbe29b..d6eab76c08 100644 --- a/common/jsonnumber.go +++ b/common/jsonnumber.go @@ -6,7 +6,9 @@ import ( ) // JSONNumber is used instead of json.Number after upgrade to go 1.14 -// to handle data which can be numbers in double quotes or possibly not numbers at all +// +// to handle data which can be numbers in double quotes or possibly not numbers at all +// // see https://github.com/golang/go/issues/37308 type JSONNumber string From 4ba04f119b1294ed9508fabc13ca7988a6e828d3 Mon Sep 17 00:00:00 2001 From: gruve-p Date: Mon, 25 Nov 2024 21:37:55 +0100 Subject: [PATCH 384/530] Groestlcoin: Bump to 28.0 (#1139) --- configs/coins/groestlcoin.json | 10 +++++----- configs/coins/groestlcoin_regtest.json | 10 +++++----- configs/coins/groestlcoin_signet.json | 10 +++++----- configs/coins/groestlcoin_testnet.json | 10 +++++----- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/configs/coins/groestlcoin.json b/configs/coins/groestlcoin.json index c8ae634264..18b8f5cc87 100644 --- a/configs/coins/groestlcoin.json +++ b/configs/coins/groestlcoin.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "27.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v27.0/groestlcoin-27.0-x86_64-linux-gnu.tar.gz", + "version": "28.0", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "5189f036913e2033b5fe95ba8f3fc027e9c5bd286d2150e9133cd4a2fd69a7a0", + "verification_source": "540d5d7c6bb0449763567ea7c2559e124d61b82a6b2798701d5759458d9c21d7", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v27.0/groestlcoin-27.0-aarch64-linux-gnu.tar.gz", - "verification_source": "95e1a4c4f4d50709df40e2d86c4b578db053d1cb475a3384862192c1298f9de6" + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-aarch64-linux-gnu.tar.gz", + "verification_source": "092c6ff333a3defe2603b91c55aea6415e554a2bbc6abb3ad43ac712fa9b63b1" } } }, diff --git a/configs/coins/groestlcoin_regtest.json b/configs/coins/groestlcoin_regtest.json index 0a62a9bfbf..aaa4ba27e3 100644 --- a/configs/coins/groestlcoin_regtest.json +++ b/configs/coins/groestlcoin_regtest.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin-regtest", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "27.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v27.0/groestlcoin-27.0-x86_64-linux-gnu.tar.gz", + "version": "28.0", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "5189f036913e2033b5fe95ba8f3fc027e9c5bd286d2150e9133cd4a2fd69a7a0", + "verification_source": "540d5d7c6bb0449763567ea7c2559e124d61b82a6b2798701d5759458d9c21d7", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v27.0/groestlcoin-27.0-aarch64-linux-gnu.tar.gz", - "verification_source": "95e1a4c4f4d50709df40e2d86c4b578db053d1cb475a3384862192c1298f9de6" + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-aarch64-linux-gnu.tar.gz", + "verification_source": "092c6ff333a3defe2603b91c55aea6415e554a2bbc6abb3ad43ac712fa9b63b1" } } }, diff --git a/configs/coins/groestlcoin_signet.json b/configs/coins/groestlcoin_signet.json index 20a019966f..53df59f7eb 100644 --- a/configs/coins/groestlcoin_signet.json +++ b/configs/coins/groestlcoin_signet.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin-signet", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "27.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v27.0/groestlcoin-27.0-x86_64-linux-gnu.tar.gz", + "version": "28.0", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "5189f036913e2033b5fe95ba8f3fc027e9c5bd286d2150e9133cd4a2fd69a7a0", + "verification_source": "540d5d7c6bb0449763567ea7c2559e124d61b82a6b2798701d5759458d9c21d7", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v27.0/groestlcoin-27.0-aarch64-linux-gnu.tar.gz", - "verification_source": "95e1a4c4f4d50709df40e2d86c4b578db053d1cb475a3384862192c1298f9de6" + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-aarch64-linux-gnu.tar.gz", + "verification_source": "092c6ff333a3defe2603b91c55aea6415e554a2bbc6abb3ad43ac712fa9b63b1" } } }, diff --git a/configs/coins/groestlcoin_testnet.json b/configs/coins/groestlcoin_testnet.json index 435fc1f3ef..7106edef08 100644 --- a/configs/coins/groestlcoin_testnet.json +++ b/configs/coins/groestlcoin_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-groestlcoin-testnet", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "27.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v27.0/groestlcoin-27.0-x86_64-linux-gnu.tar.gz", + "version": "28.0", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "5189f036913e2033b5fe95ba8f3fc027e9c5bd286d2150e9133cd4a2fd69a7a0", + "verification_source": "540d5d7c6bb0449763567ea7c2559e124d61b82a6b2798701d5759458d9c21d7", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v27.0/groestlcoin-27.0-aarch64-linux-gnu.tar.gz", - "verification_source": "95e1a4c4f4d50709df40e2d86c4b578db053d1cb475a3384862192c1298f9de6" + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-aarch64-linux-gnu.tar.gz", + "verification_source": "092c6ff333a3defe2603b91c55aea6415e554a2bbc6abb3ad43ac712fa9b63b1" } } }, From 6eb3ba220181f504cada096c9220eeb24bca7a00 Mon Sep 17 00:00:00 2001 From: CodeFace Date: Mon, 4 Nov 2024 11:47:46 +0800 Subject: [PATCH 385/530] bump Qtum 27.1 --- configs/coins/qtum.json | 6 +++--- configs/coins/qtum_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/qtum.json b/configs/coins/qtum.json index 5bfbae996f..7699106ac1 100644 --- a/configs/coins/qtum.json +++ b/configs/coins/qtum.json @@ -22,10 +22,10 @@ "package_name": "backend-qtum", "package_revision": "satoshilabs-1", "system_user": "qtum", - "version": "24.1", - "binary_url": "https://github.com/qtumproject/qtum/releases/download/v24.1/qtum-24.1-x86_64-linux-gnu.tar.gz", + "version": "27.1", + "binary_url": "https://github.com/qtumproject/qtum/releases/download/v27.1/qtum-27.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "13f7ca5c352732772e924bd07db0e8327e0a850edd9c89e7d191e0734990621c", + "verification_source": "0b1f612f0762184240c785c66b548f2dab8eed5e25481c635806ddf81807aa86", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/qtum-qt" diff --git a/configs/coins/qtum_testnet.json b/configs/coins/qtum_testnet.json index 1ceeef1138..ed1218de3b 100644 --- a/configs/coins/qtum_testnet.json +++ b/configs/coins/qtum_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-qtum-testnet", "package_revision": "satoshilabs-1", "system_user": "qtum", - "version": "24.1", - "binary_url": "https://github.com/qtumproject/qtum/releases/download/v24.1/qtum-24.1-x86_64-linux-gnu.tar.gz", + "version": "27.1", + "binary_url": "https://github.com/qtumproject/qtum/releases/download/v27.1/qtum-27.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "13f7ca5c352732772e924bd07db0e8327e0a850edd9c89e7d191e0734990621c", + "verification_source": "0b1f612f0762184240c785c66b548f2dab8eed5e25481c635806ddf81807aa86", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/qtum-qt" From 1fe4ee04f3dfda534c57ceeb5d8e02cacc5d786f Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 4 Sep 2024 13:36:30 +0200 Subject: [PATCH 386/530] Migration from MATIC to POL --- configs/coins/polygon.json | 4 ++-- configs/coins/polygon_archive.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/coins/polygon.json b/configs/coins/polygon.json index 39ab85aa1b..3a71d10125 100644 --- a/configs/coins/polygon.json +++ b/configs/coins/polygon.json @@ -1,8 +1,8 @@ { "coin": { "name": "Polygon", - "shortcut": "MATIC", - "network": "MATIC", + "shortcut": "POL", + "network": "POL", "label": "Polygon", "alias": "polygon_bor" }, diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index 6c26246d80..817d22bf39 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -1,8 +1,8 @@ { "coin": { "name": "Polygon Archive", - "shortcut": "MATIC", - "network": "MATIC", + "shortcut": "POL", + "network": "POL", "label": "Polygon", "alias": "polygon_archive_bor" }, From 19a902360ed93e5c2f7faa86577c64414448ecf7 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 25 Nov 2024 09:25:26 +0100 Subject: [PATCH 387/530] EthereumType: Remove fetching of contract details from sync --- api/worker.go | 33 ++++++++++++++++++++++++++------- bchain/coins/eth/ethrpc.go | 16 +++++++++------- bchain/types.go | 3 ++- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/api/worker.go b/api/worker.go index 799c90e804..6954c99b32 100644 --- a/api/worker.go +++ b/api/worker.go @@ -172,9 +172,18 @@ func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliases } for a := range addresses { if w.chainType == bchain.ChainEthereumType { - ci, err := w.db.GetContractInfoForAddress(a) - if err == nil && ci != nil && ci.Name != "" { - aliases[a] = AddressAlias{Type: "Contract", Alias: ci.Name} + addrDesc, err := w.chainParser.GetAddrDescFromAddress(a) + if err != nil || addrDesc == nil { + continue + } + ci, err := w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType) + if err == nil && ci != nil { + if ci.Type == bchain.UnhandledTokenType { + ci, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenType) + } + if err == nil && ci != nil && ci.Name != "" { + aliases[a] = AddressAlias{Type: "Contract", Alias: ci.Name} + } } } n := w.db.GetAddressAlias(a) @@ -608,7 +617,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, return r, nil } -func (w *Worker) getContractInfo(contract string, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) { +func (w *Worker) GetContractInfo(contract string, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) { cd, err := w.chainParser.GetAddrDescFromAddress(contract) if err != nil { return nil, false, err @@ -648,7 +657,7 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) } } - } else if (len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) { + } else if (contractInfo.Type == bchain.UnhandledTokenType || len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) { // fix contract name/symbol that was parsed as a string consisting of zeroes blockchainContractInfo, err := w.chain.GetContractInfo(cd) if err != nil { @@ -667,6 +676,10 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom if blockchainContractInfo != nil { contractInfo.Decimals = blockchainContractInfo.Decimals } + if contractInfo.Type == bchain.UnhandledTokenType { + glog.Infof("Contract %v %v [%s] handled", cd, typeFromContext, contractInfo.Name) + contractInfo.Type = typeFromContext + } if err = w.db.StoreContractInfo(contractInfo); err != nil { glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) } @@ -687,7 +700,7 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add if info, ok := contractCache[t.Contract]; ok { contractInfo = info } else { - info, _, err := w.getContractInfo(t.Contract, typeName) + info, _, err := w.GetContractInfo(t.Contract, typeName) if err != nil { glog.Errorf("getContractInfo error %v, contract %v", err, t.Contract) continue @@ -1124,10 +1137,16 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto d.tokens = d.tokens[:j] sort.Sort(d.tokens) } - d.contractInfo, err = w.db.GetContractInfo(addrDesc, "") + d.contractInfo, err = w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType) if err != nil { return nil, nil, err } + if d.contractInfo != nil && d.contractInfo.Type == bchain.UnhandledTokenType { + d.contractInfo, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenType) + if err != nil { + return nil, nil, err + } + } if filter.FromHeight == 0 && filter.ToHeight == 0 { // compute total results for paging if filter.Vout == AddressFilterVoutOff { diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index b9eb843ef9..c2c5b1113a 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -610,13 +610,15 @@ type rpcTraceResult struct { } func (b *EthereumRPC) getCreationContractInfo(contract string, height uint32) *bchain.ContractInfo { - ci, err := b.fetchContractInfo(contract) - if ci == nil || err != nil { - ci = &bchain.ContractInfo{ - Contract: contract, - } - } - ci.Type = bchain.UnknownTokenType + // do not fetch fetchContractInfo in sync, it slows it down + // the contract will be fetched only when asked by a client + // ci, err := b.fetchContractInfo(contract) + // if ci == nil || err != nil { + ci := &bchain.ContractInfo{ + Contract: contract, + } + // } + ci.Type = bchain.UnhandledTokenType ci.CreatedInBlock = height return ci } diff --git a/bchain/types.go b/bchain/types.go index 3fd2dcfe13..b33a583499 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -131,7 +131,8 @@ type TokenTypeName string // Token types const ( - UnknownTokenType TokenTypeName = "" + UnknownTokenType TokenTypeName = "" + UnhandledTokenType TokenTypeName = "-" // XPUBAddressTokenType is address derived from xpub XPUBAddressTokenType TokenTypeName = "XPUBAddress" From a55c69a8a1a52cb2da2c1d870a30bf5d1639feba Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 25 Nov 2024 10:30:44 +0100 Subject: [PATCH 388/530] EthereumType: admin interface to read and update contract info --- server/html_templates.go | 60 +++++++++++++++++++ server/internal.go | 63 +++++++++++++++++++- server/public.go | 57 ------------------ static/internal_templates/contract_info.html | 39 ++++++++++++ static/internal_templates/index.html | 10 +++- 5 files changed, 166 insertions(+), 63 deletions(-) create mode 100644 static/internal_templates/contract_info.html diff --git a/server/html_templates.go b/server/html_templates.go index a06ff0e67c..470134c1ac 100644 --- a/server/html_templates.go +++ b/server/html_templates.go @@ -35,6 +35,66 @@ type htmlTemplates[TD any] struct { postHtmlTemplateHandler func(data *TD, w http.ResponseWriter, r *http.Request) } +func (s *htmlTemplates[TD]) jsonHandler(handler func(r *http.Request, apiVersion int) (interface{}, error), apiVersion int) func(w http.ResponseWriter, r *http.Request) { + type jsonError struct { + Text string `json:"error"` + HTTPStatus int `json:"-"` + } + handlerName := getFunctionName(handler) + return func(w http.ResponseWriter, r *http.Request) { + var data interface{} + var err error + defer func() { + if e := recover(); e != nil { + glog.Error(handlerName, " recovered from panic: ", e) + debug.PrintStack() + if s.debug { + data = jsonError{fmt.Sprint("Internal server error: recovered from panic ", e), http.StatusInternalServerError} + } else { + data = jsonError{"Internal server error", http.StatusInternalServerError} + } + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + if e, isError := data.(jsonError); isError { + w.WriteHeader(e.HTTPStatus) + } + err = json.NewEncoder(w).Encode(data) + if err != nil { + glog.Warning("json encode ", err) + } + if s.metrics != nil { + s.metrics.ExplorerPendingRequests.With((common.Labels{"method": handlerName})).Dec() + } + }() + if s.metrics != nil { + s.metrics.ExplorerPendingRequests.With((common.Labels{"method": handlerName})).Inc() + } + data, err = handler(r, apiVersion) + if err != nil || data == nil { + if apiErr, ok := err.(*api.APIError); ok { + if apiErr.Public { + data = jsonError{apiErr.Error(), http.StatusBadRequest} + } else { + data = jsonError{apiErr.Error(), http.StatusInternalServerError} + } + } else { + if err != nil { + glog.Error(handlerName, " error: ", err) + } + if s.debug { + if data != nil { + data = jsonError{fmt.Sprintf("Internal server error: %v, data %+v", err, data), http.StatusInternalServerError} + } else { + data = jsonError{fmt.Sprintf("Internal server error: %v", err), http.StatusInternalServerError} + } + } else { + data = jsonError{"Internal server error", http.StatusInternalServerError} + } + } + } + } +} + func (s *htmlTemplates[TD]) htmlTemplateHandler(handler func(w http.ResponseWriter, r *http.Request) (tpl, *TD, error)) func(w http.ResponseWriter, r *http.Request) { handlerName := getFunctionName(handler) return func(w http.ResponseWriter, r *http.Request) { diff --git a/server/internal.go b/server/internal.go index 3560f0fa3b..2544c05bd9 100644 --- a/server/internal.go +++ b/server/internal.go @@ -5,11 +5,15 @@ import ( "encoding/json" "fmt" "html/template" + "io" "net/http" "path/filepath" "sort" + "strconv" + "strings" "github.com/golang/glog" + "github.com/juju/errors" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/trezor/blockbook/api" "github.com/trezor/blockbook/bchain" @@ -72,6 +76,8 @@ func NewInternalServer(binding, certFiles string, db *db.RocksDB, chain bchain.B serveMux.HandleFunc(path+"admin/ws-limit-exceeding-ips", s.htmlTemplateHandler(s.wsLimitExceedingIPs)) if s.chainParser.GetChainType() == bchain.ChainEthereumType { serveMux.HandleFunc(path+"admin/internal-data-errors", s.htmlTemplateHandler(s.internalDataErrors)) + serveMux.HandleFunc(path+"admin/contract-info", s.htmlTemplateHandler(s.contractInfoPage)) + serveMux.HandleFunc(path+"admin/contract-info/", s.jsonHandler(s.apiContractInfo, 0)) } return s, nil } @@ -118,7 +124,8 @@ func (s *InternalServer) index(w http.ResponseWriter, r *http.Request) { const ( adminIndexTpl = iota + errorInternalTpl + 1 adminInternalErrorsTpl - adminLimitExceedingIPS + adminLimitExceedingIPSTpl + adminContractInfoTpl internalTplCount ) @@ -173,7 +180,8 @@ func (s *InternalServer) parseTemplates() []*template.Template { t[errorInternalTpl] = createTemplate("./static/internal_templates/error.html", "./static/internal_templates/base.html") t[adminIndexTpl] = createTemplate("./static/internal_templates/index.html", "./static/internal_templates/base.html") t[adminInternalErrorsTpl] = createTemplate("./static/internal_templates/block_internal_data_errors.html", "./static/internal_templates/base.html") - t[adminLimitExceedingIPS] = createTemplate("./static/internal_templates/ws_limit_exceeding_ips.html", "./static/internal_templates/base.html") + t[adminLimitExceedingIPSTpl] = createTemplate("./static/internal_templates/ws_limit_exceeding_ips.html", "./static/internal_templates/base.html") + t[adminContractInfoTpl] = createTemplate("./static/internal_templates/contract_info.html", "./static/internal_templates/base.html") return t } @@ -213,5 +221,54 @@ func (s *InternalServer) wsLimitExceedingIPs(w http.ResponseWriter, r *http.Requ }) data.WsLimitExceedingIPs = ips data.WsGetAccountInfoLimit = s.is.WsGetAccountInfoLimit - return adminLimitExceedingIPS, data, nil + return adminLimitExceedingIPSTpl, data, nil +} + +func (s *InternalServer) contractInfoPage(w http.ResponseWriter, r *http.Request) (tpl, *InternalTemplateData, error) { + data := s.newTemplateData(r) + return adminContractInfoTpl, data, nil +} + +func (s *InternalServer) apiContractInfo(r *http.Request, apiVersion int) (interface{}, error) { + if r.Method == http.MethodPost { + return s.updateContracts(r) + } + var contractAddress string + i := strings.LastIndexByte(r.URL.Path, '/') + if i > 0 { + contractAddress = r.URL.Path[i+1:] + } + if len(contractAddress) == 0 { + return nil, api.NewAPIError("Missing contract address", true) + } + + contractInfo, valid, err := s.api.GetContractInfo(contractAddress, bchain.UnknownTokenType) + if err != nil { + return nil, api.NewAPIError(err.Error(), true) + } + if !valid { + return nil, api.NewAPIError("Not a contract", true) + } + return contractInfo, nil +} + +func (s *InternalServer) updateContracts(r *http.Request) (interface{}, error) { + data, err := io.ReadAll(r.Body) + if err != nil { + return nil, api.NewAPIError("Cannot get request body", true) + } + var contractInfos []bchain.ContractInfo + err = json.Unmarshal(data, &contractInfos) + if err != nil { + return nil, errors.Annotatef(err, "Cannot unmarshal body to array of ContractInfo objects") + } + for i := range contractInfos { + c := &contractInfos[i] + err := s.db.StoreContractInfo(c) + if err != nil { + return nil, api.NewAPIError("Error updating contract "+c.Contract+" "+err.Error(), true) + } + + } + return "{\"success\":\"Updated " + strconv.Itoa(len(contractInfos)) + " contracts\"}", nil } diff --git a/server/public.go b/server/public.go index d21da713f2..05e8f47779 100644 --- a/server/public.go +++ b/server/public.go @@ -14,7 +14,6 @@ import ( "reflect" "regexp" "runtime" - "runtime/debug" "sort" "strconv" "strings" @@ -289,62 +288,6 @@ func getFunctionName(i interface{}) string { return name } -func (s *PublicServer) jsonHandler(handler func(r *http.Request, apiVersion int) (interface{}, error), apiVersion int) func(w http.ResponseWriter, r *http.Request) { - type jsonError struct { - Text string `json:"error"` - HTTPStatus int `json:"-"` - } - handlerName := getFunctionName(handler) - return func(w http.ResponseWriter, r *http.Request) { - var data interface{} - var err error - defer func() { - if e := recover(); e != nil { - glog.Error(handlerName, " recovered from panic: ", e) - debug.PrintStack() - if s.debug { - data = jsonError{fmt.Sprint("Internal server error: recovered from panic ", e), http.StatusInternalServerError} - } else { - data = jsonError{"Internal server error", http.StatusInternalServerError} - } - } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - if e, isError := data.(jsonError); isError { - w.WriteHeader(e.HTTPStatus) - } - err = json.NewEncoder(w).Encode(data) - if err != nil { - glog.Warning("json encode ", err) - } - s.metrics.ExplorerPendingRequests.With((common.Labels{"method": handlerName})).Dec() - }() - s.metrics.ExplorerPendingRequests.With((common.Labels{"method": handlerName})).Inc() - data, err = handler(r, apiVersion) - if err != nil || data == nil { - if apiErr, ok := err.(*api.APIError); ok { - if apiErr.Public { - data = jsonError{apiErr.Error(), http.StatusBadRequest} - } else { - data = jsonError{apiErr.Error(), http.StatusInternalServerError} - } - } else { - if err != nil { - glog.Error(handlerName, " error: ", err) - } - if s.debug { - if data != nil { - data = jsonError{fmt.Sprintf("Internal server error: %v, data %+v", err, data), http.StatusInternalServerError} - } else { - data = jsonError{fmt.Sprintf("Internal server error: %v", err), http.StatusInternalServerError} - } - } else { - data = jsonError{"Internal server error", http.StatusInternalServerError} - } - } - } - } -} - func (s *PublicServer) newTemplateData(r *http.Request) *TemplateData { t := &TemplateData{ CoinName: s.is.Coin, diff --git a/static/internal_templates/contract_info.html b/static/internal_templates/contract_info.html new file mode 100644 index 0000000000..57cbfece24 --- /dev/null +++ b/static/internal_templates/contract_info.html @@ -0,0 +1,39 @@ +{{define "specific"}} {{if eq .ChainType 1}} + +
+
+
+ +
+
+ +
+
+
+
+ To update contract, use POST request to /admin/contract-info/ endpoint. Example: +
+
+            curl -k -v  \
+            'https://<internaladdress>/admin/contract-info/' \
+            -H 'Content-Type: application/json' \
+            --data '[{ContractInfo},{ContractInfo},...]'        
+        
+
+
+{{else}} Not supported {{end}}{{end}} diff --git a/static/internal_templates/index.html b/static/internal_templates/index.html index 5fef7011ff..7a94bce8f0 100644 --- a/static/internal_templates/index.html +++ b/static/internal_templates/index.html @@ -1,10 +1,14 @@ {{define "specific"}} {{if eq .ChainType 1}} -{{end}} -{{end}} \ No newline at end of file + +{{end}}{{end}} From 46156d296f02796ace474f25a65ad15aac29d219 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 7 Dec 2024 14:07:39 +0100 Subject: [PATCH 389/530] Fix refetch internal data --- api/ethereumtype.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/ethereumtype.go b/api/ethereumtype.go index b4aa944676..1f98f2eea0 100644 --- a/api/ethereumtype.go +++ b/api/ethereumtype.go @@ -62,7 +62,7 @@ func (w *Worker) RefetchInternalDataRoutine() { if block != nil { blockSpecificData, _ = block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) } - if err != nil || block == nil || blockSpecificData == nil || blockSpecificData.InternalDataError != "" { + if err != nil || block == nil || (blockSpecificData != nil && blockSpecificData.InternalDataError != "") { glog.Errorf("Refetching internal data for %d %s, error %v, retrying", ie.Height, ie.Hash, err) // try for second time to fetch the data - the 2nd attempt after the first unsuccessful has many times higher probability of success // probably something to do with data preloaded to cache on the backend From e283ac8915f001f3e32d725ea78c989be55c9bc2 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Thu, 5 Dec 2024 15:59:08 +0000 Subject: [PATCH 390/530] =?UTF-8?q?doge=20(+testnet)=201.14.7=20=E2=86=92?= =?UTF-8?q?=201.14.9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/dogecoin.json | 10 +++++----- configs/coins/dogecoin_testnet.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/configs/coins/dogecoin.json b/configs/coins/dogecoin.json index 4f3d4d5967..f120043288 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.7", - "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.7/dogecoin-1.14.7-x86_64-linux-gnu.tar.gz", + "version": "1.14.9", + "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.9/dogecoin-1.14.9-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "9cd22fb3ebba4d407c2947f4241b9e78c759f29cdf32de8863aea6aeed21cf8b", + "verification_source": "4f227117b411a7c98622c970986e27bcfc3f547a72bef65e7d9e82989175d4f8", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/dogecoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/dogecoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -45,8 +45,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.7/dogecoin-1.14.7-aarch64-linux-gnu.tar.gz", - "verification_source": "b8fb8050b19283d1ab3c261aaca96d84f2a17f93b52fcff9e252f390b0564f31", + "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.9/dogecoin-1.14.9-aarch64-linux-gnu.tar.gz", + "verification_source": "6928c895a20d0bcb6d5c7dcec753d35c884a471aaf8ad4242a89a96acb4f2985", "exclude_files": [] } } diff --git a/configs/coins/dogecoin_testnet.json b/configs/coins/dogecoin_testnet.json index d16ab58783..8103ba70db 100644 --- a/configs/coins/dogecoin_testnet.json +++ b/configs/coins/dogecoin_testnet.json @@ -22,10 +22,10 @@ "package_name": "backend-dogecoin-testnet", "package_revision": "satoshilabs-1", "system_user": "dogecoin", - "version": "1.14.7", - "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.7/dogecoin-1.14.7-x86_64-linux-gnu.tar.gz", + "version": "1.14.9", + "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.9/dogecoin-1.14.9-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "9cd22fb3ebba4d407c2947f4241b9e78c759f29cdf32de8863aea6aeed21cf8b", + "verification_source": "4f227117b411a7c98622c970986e27bcfc3f547a72bef65e7d9e82989175d4f8", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dogecoin-qt" @@ -47,8 +47,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.7/dogecoin-1.14.7-aarch64-linux-gnu.tar.gz", - "verification_source": "b8fb8050b19283d1ab3c261aaca96d84f2a17f93b52fcff9e252f390b0564f31", + "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.9/dogecoin-1.14.9-aarch64-linux-gnu.tar.gz", + "verification_source": "6928c895a20d0bcb6d5c7dcec753d35c884a471aaf8ad4242a89a96acb4f2985", "exclude_files": [] } } From a4f173036490ee722367f90725ec3b07fb203607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Musil?= Date: Mon, 9 Dec 2024 11:30:02 +0100 Subject: [PATCH 391/530] Show raw tx hex in UI (#1162) * Fix Network configuration parameter * feat: allow for showing raw transaction hex for ETH transactions * chore: remove comments from JS code to avoid parsing issues in tests * temp: comment out failing tx template tests * chore: trim text from copyable before writing it to clipboard * chore: improve the design of Transaction hex * chore: add wrap to element showing raw hex data * fixup! chore: add wrap to element showing raw hex data * chore: remove redundant style, make HTML prettier * Revert "temp: comment out failing tx template tests" This reverts commit f104ebbf5111583b46996d7527a26c08cd9e29b6. * chore: put rawTx javascript functionality into main.js * chore: modify the expected HTML for changed tx template * feat: support the raw transaction hex also for BTC-like coins * chore: add on-hover effect for active button - make the background white * Minify javascript and styles --------- Co-authored-by: Martin Boehm --- api/worker.go | 5 + bchain/basechain.go | 4 + bchain/coins/blockchain.go | 5 + bchain/coins/eth/ethrpc.go | 24 +++- bchain/coins/eth/stakingpool.go | 4 +- bchain/types.go | 1 + blockbook.go | 2 +- configs/coins/arbitrum.json | 125 ++++++++++---------- configs/coins/arbitrum_archive.json | 129 +++++++++++---------- configs/coins/bsc.json | 2 +- configs/coins/bsc_archive.json | 2 +- fiat/coingecko.go | 4 +- server/public.go | 16 ++- server/public_ethereumtype_test.go | 8 +- server/public_test.go | 32 ++--- server/websocket.go | 2 +- static/css/main.css | 6 +- static/css/main.min.3.css | 1 - static/css/main.min.4.css | 1 + static/js/main.js | 76 +++++++++++- static/js/main.min.3.js | 1 - static/js/main.min.4.js | 1 + static/templates/tx.html | 18 ++- tests/dbtestdata/fakechain_ethereumtype.go | 5 + 24 files changed, 305 insertions(+), 169 deletions(-) delete mode 100644 static/css/main.min.3.css create mode 100644 static/css/main.min.4.css delete mode 100644 static/js/main.min.3.js create mode 100644 static/js/main.min.4.js diff --git a/api/worker.go b/api/worker.go index 6954c99b32..d478f85c23 100644 --- a/api/worker.go +++ b/api/worker.go @@ -207,6 +207,11 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool return tx, nil } +// GetRawTransaction gets raw transaction data in hex format from txid +func (w *Worker) GetRawTransaction(txid string) (string, error) { + return w.chain.EthereumTypeGetRawTransaction(txid) +} + // getTransaction reads transaction data from txid func (w *Worker) getTransaction(txid string, spendingTxs bool, specificJSON bool, addresses map[string]struct{}) (*Tx, error) { bchainTx, height, err := w.txCache.GetTransaction(txid) diff --git a/bchain/basechain.go b/bchain/basechain.go index c84f907fd6..e6da4281ae 100644 --- a/bchain/basechain.go +++ b/bchain/basechain.go @@ -81,3 +81,7 @@ func (b *BaseChain) EthereumTypeGetStakingPoolsData(addrDesc AddressDescriptor) func (b *BaseChain) EthereumTypeRpcCall(data, to, from string) (string, error) { return "", errors.New("not supported") } + +func (b *BaseChain) EthereumTypeGetRawTransaction(txid string) (string, error) { + return "", errors.New("not supported") +} diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index f3dac986ff..77e7206ab7 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -354,6 +354,11 @@ func (c *blockChainWithMetrics) EthereumTypeRpcCall(data, to, from string) (v st return c.b.EthereumTypeRpcCall(data, to, from) } +func (c *blockChainWithMetrics) EthereumTypeGetRawTransaction(txid string) (v string, err error) { + defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetRawTransaction", s, err) }(time.Now()) + return c.b.EthereumTypeGetRawTransaction(txid) +} + type mempoolWithMetrics struct { mempool bchain.Mempool m *common.Metrics diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index c2c5b1113a..1412b0e724 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -40,6 +40,7 @@ const ( type Configuration struct { CoinName string `json:"coin_name"` CoinShortcut string `json:"coin_shortcut"` + Network string `json:"network"` RPCURL string `json:"rpc_url"` RPCTimeout int `json:"rpc_timeout"` BlockAddressesToKeep int `json:"block_addresses_to_keep"` @@ -159,7 +160,12 @@ func (b *EthereumRPC) Initialize() error { return errors.Errorf("Unknown network id %v", id) } - err = b.initStakingPools(b.ChainConfig.CoinShortcut) + networkConfig := b.ChainConfig.Network + if networkConfig == "" { + networkConfig = b.ChainConfig.CoinShortcut + } + + err = b.initStakingPools(networkConfig) if err != nil { return err } @@ -988,21 +994,31 @@ func (b *EthereumRPC) EthereumTypeEstimateGas(params map[string]interface{}) (ui // SendRawTransaction sends raw transaction func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) { + return b.callRpcStringResult("eth_sendRawTransaction", hex) +} + +// EthereumTypeGetRawTransaction gets raw transaction in hex format +func (b *EthereumRPC) EthereumTypeGetRawTransaction(txid string) (string, error) { + return b.callRpcStringResult("eth_getRawTransactionByHash", txid) +} + +// Helper function for calling ETH RPC with parameters and getting string result +func (b *EthereumRPC) callRpcStringResult(rpcMethod string, args ...interface{}) (string, error) { ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) defer cancel() var raw json.RawMessage - err := b.RPC.CallContext(ctx, &raw, "eth_sendRawTransaction", hex) + err := b.RPC.CallContext(ctx, &raw, rpcMethod, args...) if err != nil { return "", err } else if len(raw) == 0 { - return "", errors.New("SendRawTransaction: failed") + return "", errors.New(rpcMethod + " : failed") } var result string if err := json.Unmarshal(raw, &result); err != nil { return "", errors.Annotatef(err, "raw result %v", raw) } if result == "" { - return "", errors.New("SendRawTransaction: failed, empty result") + return "", errors.New(rpcMethod + " : failed, empty result") } return result, nil } diff --git a/bchain/coins/eth/stakingpool.go b/bchain/coins/eth/stakingpool.go index 8b7cf7b868..f773dae911 100644 --- a/bchain/coins/eth/stakingpool.go +++ b/bchain/coins/eth/stakingpool.go @@ -11,9 +11,9 @@ import ( "github.com/trezor/blockbook/bchain" ) -func (b *EthereumRPC) initStakingPools(coinShortcut string) error { +func (b *EthereumRPC) initStakingPools(network string) error { // for now only single staking pool - envVar := strings.ToUpper(coinShortcut) + "_STAKING_POOL_CONTRACT" + envVar := strings.ToUpper(network) + "_STAKING_POOL_CONTRACT" envValue := os.Getenv(envVar) if envValue != "" { parts := strings.Split(envValue, "/") diff --git a/bchain/types.go b/bchain/types.go index b33a583499..8f1c25435f 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -337,6 +337,7 @@ type BlockChain interface { EthereumTypeGetSupportedStakingPools() []string EthereumTypeGetStakingPoolsData(addrDesc AddressDescriptor) ([]StakingPoolData, error) EthereumTypeRpcCall(data, to, from string) (string, error) + EthereumTypeGetRawTransaction(txid string) (string, error) GetTokenURI(contractDesc AddressDescriptor, tokenID *big.Int) (string, error) } diff --git a/blockbook.go b/blockbook.go index a0065ec680..6675aec32d 100644 --- a/blockbook.go +++ b/blockbook.go @@ -507,7 +507,7 @@ func newInternalState(config *common.Config, d *db.RocksDB, enableSubNewTx bool) is.Host = name } - is.WsGetAccountInfoLimit, _ = strconv.Atoi(os.Getenv(strings.ToUpper(is.CoinShortcut) + "_WS_GETACCOUNTINFO_LIMIT")) + is.WsGetAccountInfoLimit, _ = strconv.Atoi(os.Getenv(strings.ToUpper(is.GetNetwork()) + "_WS_GETACCOUNTINFO_LIMIT")) if is.WsGetAccountInfoLimit > 0 { glog.Info("WsGetAccountInfoLimit enabled with limit ", is.WsGetAccountInfoLimit) is.WsLimitExceedingIPs = make(map[string]int) diff --git a/configs/coins/arbitrum.json b/configs/coins/arbitrum.json index 223fa6f9a3..b1f0171193 100644 --- a/configs/coins/arbitrum.json +++ b/configs/coins/arbitrum.json @@ -1,65 +1,66 @@ { - "coin": { - "name": "Arbitrum", - "shortcut": "ETH", - "label": "Arbitrum", - "alias": "arbitrum" - }, - "ports": { - "backend_rpc": 8205, - "backend_p2p": 38405, - "backend_http": 8305, - "blockbook_internal": 9205, - "blockbook_public": 9305 - }, - "ipc": { - "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", - "rpc_timeout": 25 - }, - "backend": { - "package_name": "backend-arbitrum", - "package_revision": "satoshilabs-1", - "system_user": "arbitrum", - "version": "3.2.1", - "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", - "verification_type": "docker", - "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", - "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", - "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", - "exec_script": "arbitrum.sh", - "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", - "service_type": "simple", - "service_additional_params_template": "", - "protect_memory": true, - "mainnet": true, - "server_config_file": "", - "client_config_file": "" - }, - "blockbook": { - "package_name": "blockbook-arbitrum", - "system_user": "blockbook-arbitrum", - "internal_binding_template": ":{{.Ports.BlockbookInternal}}", - "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "", - "additional_params": "", - "block_chain": { - "parse": true, - "mempool_workers": 8, - "mempool_sub_workers": 2, - "block_addresses_to_keep": 300, - "additional_params": { - "mempoolTxTimeoutHours": 48, - "queryBackendOnMempoolResync": false, - "fiat_rates": "coingecko", - "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" - } + "coin": { + "name": "Arbitrum", + "shortcut": "ETH", + "network": "ARB", + "label": "Arbitrum", + "alias": "arbitrum" + }, + "ports": { + "backend_rpc": 8205, + "backend_p2p": 38405, + "backend_http": 8305, + "blockbook_internal": 9205, + "blockbook_public": 9305 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-arbitrum", + "package_revision": "satoshilabs-1", + "system_user": "arbitrum", + "version": "3.2.1", + "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", + "verification_type": "docker", + "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", + "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "arbitrum.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-arbitrum", + "system_user": "blockbook-arbitrum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "additional_params": { + "mempoolTxTimeoutHours": 48, + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" } - }, - "meta": { - "package_maintainer": "IT", - "package_maintainer_email": "it@satoshilabs.com" - } } diff --git a/configs/coins/arbitrum_archive.json b/configs/coins/arbitrum_archive.json index 4d672f80dd..f01d6c6730 100644 --- a/configs/coins/arbitrum_archive.json +++ b/configs/coins/arbitrum_archive.json @@ -1,67 +1,68 @@ { - "coin": { - "name": "Arbitrum Archive", - "shortcut": "ETH", - "label": "Arbitrum", - "alias": "arbitrum_archive" - }, - "ports": { - "backend_rpc": 8306, - "backend_p2p": 38406, - "blockbook_internal": 9206, - "blockbook_public": 9306 - }, - "ipc": { - "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", - "rpc_timeout": 25 - }, - "backend": { - "package_name": "backend-arbitrum-archive", - "package_revision": "satoshilabs-1", - "system_user": "arbitrum", - "version": "3.2.1", - "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", - "verification_type": "docker", - "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", - "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", - "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_archive_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", - "exec_script": "arbitrum_archive.sh", - "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", - "service_type": "simple", - "service_additional_params_template": "", - "protect_memory": true, - "mainnet": true, - "server_config_file": "", - "client_config_file": "" - }, - "blockbook": { - "package_name": "blockbook-arbitrum-archive", - "system_user": "blockbook-arbitrum", - "internal_binding_template": ":{{.Ports.BlockbookInternal}}", - "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "", - "additional_params": "-workers=16", - "block_chain": { - "parse": true, - "mempool_workers": 8, - "mempool_sub_workers": 2, - "block_addresses_to_keep": 600, - "additional_params": { - "address_aliases": true, - "mempoolTxTimeoutHours": 48, - "processInternalTransactions": true, - "queryBackendOnMempoolResync": false, - "fiat_rates": "coingecko", - "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", - "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" - } + "coin": { + "name": "Arbitrum Archive", + "shortcut": "ETH", + "network": "ARB", + "label": "Arbitrum", + "alias": "arbitrum_archive" + }, + "ports": { + "backend_rpc": 8306, + "backend_p2p": 38406, + "blockbook_internal": 9206, + "blockbook_public": 9306 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-arbitrum-archive", + "package_revision": "satoshilabs-1", + "system_user": "arbitrum", + "version": "3.2.1", + "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", + "verification_type": "docker", + "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", + "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_archive_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "arbitrum_archive.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-arbitrum-archive", + "system_user": "blockbook-arbitrum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "-workers=16", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 600, + "additional_params": { + "address_aliases": true, + "mempoolTxTimeoutHours": 48, + "processInternalTransactions": true, + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" } - }, - "meta": { - "package_maintainer": "IT", - "package_maintainer_email": "it@satoshilabs.com" - } } diff --git a/configs/coins/bsc.json b/configs/coins/bsc.json index 8d15e80d45..db599b3ac6 100644 --- a/configs/coins/bsc.json +++ b/configs/coins/bsc.json @@ -2,7 +2,7 @@ "coin": { "name": "BNB Smart Chain", "shortcut": "BNB", - "network": "BNB", + "network": "BSC", "label": "BNB Smart Chain", "alias": "bsc" }, diff --git a/configs/coins/bsc_archive.json b/configs/coins/bsc_archive.json index df80c6d01b..0b460876df 100644 --- a/configs/coins/bsc_archive.json +++ b/configs/coins/bsc_archive.json @@ -2,7 +2,7 @@ "coin": { "name": "BNB Smart Chain Archive", "shortcut": "BNB", - "network": "BNB", + "network": "BSC", "label": "BNB Smart Chain", "alias": "bsc_archive" }, diff --git a/fiat/coingecko.go b/fiat/coingecko.go index b7e8970860..68c7722cd9 100644 --- a/fiat/coingecko.go +++ b/fiat/coingecko.go @@ -59,7 +59,7 @@ type marketChartPrices struct { } // NewCoinGeckoDownloader creates a coingecko structure that implements the RatesDownloaderInterface -func NewCoinGeckoDownloader(db *db.RocksDB, coinShortcut string, url string, coin string, platformIdentifier string, platformVsCurrency string, allowedVsCurrencies string, timeFormat string, metrics *common.Metrics, throttleDown bool) RatesDownloaderInterface { +func NewCoinGeckoDownloader(db *db.RocksDB, network string, url string, coin string, platformIdentifier string, platformVsCurrency string, allowedVsCurrencies string, timeFormat string, metrics *common.Metrics, throttleDown bool) RatesDownloaderInterface { throttlingDelayMs := 0 // No delay by default if throttleDown { throttlingDelayMs = DefaultThrottleDelayMs @@ -67,7 +67,7 @@ func NewCoinGeckoDownloader(db *db.RocksDB, coinShortcut string, url string, coi allowedVsCurrenciesMap := getAllowedVsCurrenciesMap(allowedVsCurrencies) - apiKey := os.Getenv(strings.ToUpper(coinShortcut) + "_COINGECKO_API_KEY") + apiKey := os.Getenv(strings.ToUpper(network) + "_COINGECKO_API_KEY") if apiKey == "" { apiKey = os.Getenv("COINGECKO_API_KEY") } diff --git a/server/public.go b/server/public.go index 05e8f47779..e2d8c11903 100644 --- a/server/public.go +++ b/server/public.go @@ -186,6 +186,7 @@ func (s *PublicServer) ConnectFullPublicInterface() { serveMux.HandleFunc(path+"api/block-filters/", s.jsonHandler(s.apiBlockFilters, apiDefault)) serveMux.HandleFunc(path+"api/tx-specific/", s.jsonHandler(s.apiTxSpecific, apiDefault)) serveMux.HandleFunc(path+"api/tx/", s.jsonHandler(s.apiTx, apiDefault)) + serveMux.HandleFunc(path+"api/rawtx/", s.jsonHandler(s.apiRawTx, apiDefault)) serveMux.HandleFunc(path+"api/address/", s.jsonHandler(s.apiAddress, apiDefault)) serveMux.HandleFunc(path+"api/xpub/", s.jsonHandler(s.apiXpub, apiDefault)) serveMux.HandleFunc(path+"api/utxo/", s.jsonHandler(s.apiUtxo, apiDefault)) @@ -303,7 +304,7 @@ func (s *PublicServer) newTemplateData(r *http.Request) *TemplateData { t.MultiTokenName = bchain.EthereumTokenTypeMap[bchain.MultiToken] } if !s.debug { - t.Minified = ".min.3" + t.Minified = ".min.4" } if s.is.HasFiatRates { // get the secondary coin and if it should be shown either from query parameters "secondary" and "use_secondary" @@ -1288,6 +1289,19 @@ func (s *PublicServer) apiTx(r *http.Request, apiVersion int) (interface{}, erro return tx, err } +func (s *PublicServer) apiRawTx(r *http.Request, apiVersion int) (interface{}, error) { + var txid string + i := strings.LastIndexByte(r.URL.Path, '/') + if i > 0 { + txid = r.URL.Path[i+1:] + } + if len(txid) == 0 { + return "", api.NewAPIError("Missing txid", true) + } + s.metrics.ExplorerViews.With(common.Labels{"action": "api-raw-tx"}).Inc() + return s.api.GetRawTransaction(txid) +} + func (s *PublicServer) apiTxSpecific(r *http.Request, apiVersion int) (interface{}, error) { var txid string i := strings.LastIndexByte(r.URL.Path, '/') diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 5a9535334f..a0714f4706 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -26,7 +26,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE
0.00 USD

Confirmed
Balance0.000000000123450123 FAKE0.00 USD
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers#
Contract 130.000000001000123013 S13-1
Contract 740.001000123074 S74-1
ContractTokensTransfers#
Contract 20511

Transactions

ERC721 Token Transfers
ERC20 Token Transfers
address7b.eth
 
871.180000950184 S74-
 
address7b.eth
7.674999999999991915 S13-
`, + `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE
0.00 USD

Confirmed
Balance0.000000000123450123 FAKE0.00 USD
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers#
Contract 130.000000001000123013 S13-1
Contract 740.001000123074 S74-1
ContractTokensTransfers#
Contract 20511

Transactions

ERC721 Token Transfers
ERC20 Token Transfers
address7b.eth
 
871.180000950184 S74-
 
address7b.eth
7.674999999999991915 S13-
`, }, }, { @@ -35,7 +35,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE
0.00 USD

Confirmed
Balance0.000000000123450093 FAKE0.00 USD
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers#
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE0.00 USD0.00 USD
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, + `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE
0.00 USD

Confirmed
Balance0.000000000123450093 FAKE0.00 USD
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers#
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE0.00 USD0.00 USD
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, }, }, { @@ -44,14 +44,14 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE0.00 USD0.00 USD
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE0.00 USD0.00 USD (40 Gwei)
Fees0.002081 FAKE4.16 USD18.55 USD
RBFON
Nonce208
 
0 FAKE0.00 USD0.00 USD
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE0.00 USD0.00 USD
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE0.00 USD0.00 USD (40 Gwei)
Fees0.002081 FAKE4.16 USD18.55 USD
RBFON
Nonce208
 
0 FAKE0.00 USD0.00 USD
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
`, }, }, { name: "explorerTokenDetail " + dbtestdata.EthAddr7b, r: newGetRequest(ts.URL + "/nft/" + dbtestdata.EthAddrContractCd + "/" + "1"), status: http.StatusOK, contentType: "text/html; charset=utf-8", - body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
Contract typeERC20
`}, + body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
Contract typeERC20
`}, }, { name: "apiIndex", diff --git a/server/public_test.go b/server/public_test.go index 01e34e9bab..550afefe52 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -283,7 +283,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -292,7 +292,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.00024690 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.00024690 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -301,7 +301,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input3172.83951062 FAKE
Total Output3172.83951000 FAKE
Fees0.00000062 FAKE
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input3172.83951062 FAKE
Total Output3172.83951000 FAKE
Fees0.00000062 FAKE
`, }, }, { @@ -310,7 +310,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Error

Transaction not found

`, + `Trezor Fake Coin Explorer

Error

Transaction not found

`, }, }, { @@ -319,7 +319,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Blocks

HeightHashTimestampTransactionsSize
22549400000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b61639 days 11 hours ago42345678
2254930000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e29971640 days 9 hours ago21234567
`, + `Trezor Fake Coin Explorer

Blocks

HeightHashTimestampTransactionsSize
22549400000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b61639 days 11 hours ago42345678
2254930000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e29971640 days 9 hours ago21234567
`, }, }, { @@ -328,7 +328,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -337,7 +337,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Application status

Synchronization with backend is disabled, the state of index is not up to date.

OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6

Blockbook

CoinFakecoin
Host
Version / Commit / Buildunknown / unknown / unknown
Synchronized
true
Last Block225494
Last Block Update`, + `Trezor Fake Coin Explorer

Application status

Synchronization with backend is disabled, the state of index is not up to date.

`, `

Blockbook

CoinFakecoin
Host
Version / Commit / Buildunknown / unknown / unknown
Synchronized
true
Last Block225494
Last Block Update`, `
Mempool in Sync
false
Last Mempool Update
Transactions in Mempool0
Current Fiat rates

Backend

Chainfakecoin
Version001001
Subversion/Fakecoin:0.0.1/
Last Block2
Difficulty
Blockbook - blockchain indexer for Trezor Suite https://trezor.io/trezor-suite. Do not use for any other purpose.
`, }, @@ -348,7 +348,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -357,7 +357,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, + `Trezor Fake Coin Explorer

Block

225494
00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
Transactions4
Height225494
Confirmations1
Timestamp1639 days 11 hours ago
Size (bytes)2345678
Version
Merkle Root
Nonce
Bits
Difficulty

Transactions

 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -366,7 +366,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db
Mined Time1639 days 11 hours ago
In Block00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6
In Block Height225494
Total Input0 FAKE
Total Output13.60030331 FAKE
Fees0 FAKE
No Inputs (Newly Generated Coins)
 
Unparsed address0 FAKE×
`, }, }, { @@ -375,7 +375,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.00024690 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.00024690 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -384,7 +384,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

XPUB

upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q

1186.419755 FAKE

Confirmed
Total Received1186.41975501 FAKE
Total Sent0.00000001 FAKE
Final Balance1186.41975500 FAKE
No. Transactions2
Used XPUB Addresses2
XPUB Addresses with Balance
AddressBalanceTxsPath
2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu1186.41975500 FAKE1m/49'/1'/33'/1/3

Transactions

`, + `Trezor Fake Coin Explorer

XPUB

upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q

1186.419755 FAKE

Confirmed
Total Received1186.41975501 FAKE
Total Sent0.00000001 FAKE
Final Balance1186.41975500 FAKE
No. Transactions2
Used XPUB Addresses2
XPUB Addresses with Balance
AddressBalanceTxsPath
2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu1186.41975500 FAKE1m/49'/1'/33'/1/3

Transactions

`, }, }, { @@ -393,7 +393,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

XPUB

tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej

0 FAKE

Confirmed
Total Received0 FAKE
Total Sent0 FAKE
Final Balance0 FAKE
No. Transactions0
Used XPUB Addresses0
XPUB Addresses with Balance
No addresses
`, + `Trezor Fake Coin Explorer

XPUB

tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej

0 FAKE

Confirmed
Total Received0 FAKE
Total Sent0 FAKE
Final Balance0 FAKE
No. Transactions0
Used XPUB Addresses0
XPUB Addresses with Balance
No addresses
`, }, }, { @@ -402,7 +402,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Error

No matching records found for '1234'

`, + `Trezor Fake Coin Explorer

Error

No matching records found for '1234'

`, }, }, { @@ -411,7 +411,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Send Raw Transaction

`, + `Trezor Fake Coin Explorer

Send Raw Transaction

`, }, }, { @@ -420,7 +420,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Send Raw Transaction

Invalid data
`, + `Trezor Fake Coin Explorer

Send Raw Transaction

Invalid data
`, }, }, { diff --git a/server/websocket.go b/server/websocket.go index f9093eebd5..c782adea6b 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -107,7 +107,7 @@ func NewWebsocketServer(db *db.RocksDB, chain bchain.BlockChain, mempool bchain. fiatRatesSubscriptions: make(map[string]map[*websocketChannel]string), fiatRatesTokenSubscriptions: make(map[*websocketChannel][]string), } - envRpcCall := os.Getenv(strings.ToUpper(is.CoinShortcut) + "_ALLOWED_RPC_CALL_TO") + envRpcCall := os.Getenv(strings.ToUpper(is.GetNetwork()) + "_ALLOWED_RPC_CALL_TO") if envRpcCall != "" { s.allowedRpcCallTo = make(map[string]struct{}) for _, c := range strings.Split(envRpcCall, ",") { diff --git a/static/css/main.css b/static/css/main.css index b02f915c58..e708ac188c 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -200,6 +200,10 @@ span.btn-paging:hover { color: #757575; } +.btn-paging.active:hover { + background-color: white; +} + .paging-group { border: 1px solid #e2e2e2; border-radius: 0.5rem; @@ -694,4 +698,4 @@ span.btn-paging:hover { .btn { --bs-btn-font-size: 1rem; } -} \ No newline at end of file +} diff --git a/static/css/main.min.3.css b/static/css/main.min.3.css deleted file mode 100644 index dbafe0842b..0000000000 --- a/static/css/main.min.3.css +++ /dev/null @@ -1 +0,0 @@ -@import "TTHoves/TTHoves.css";* {margin: 0;padding: 0;outline: none;font-family: "TT Hoves", -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;}html, body {height: 100%;}body {min-height: 100%;margin: 0;background: linear-gradient(to bottom, #f6f6f6 360px, #e5e5e5 0), #e5e5e5;background-repeat: no-repeat;}a {color: #00854d;text-decoration: none;}a:hover {color: #00854d;text-decoration: underline;}select {border-radius: 0.5rem;padding-left: 0.5rem;border: 1px solid #ced4da;color: var(--bs-body-color);min-height: 45px;}#header {position: fixed;top: 0;left: 0;width: 100%;margin: 0;padding-bottom: 0;padding-top: 0;background-color: white;border-bottom: 1px solid #f6f6f6;z-index: 10;}#header a {color: var(--bs-navbar-brand-color);}#header a:hover {color: var(--bs-navbar-brand-hover-color);}#header .navbar {--bs-navbar-padding-y: 0.7rem;}#header .form-control-lg {font-size: 1rem;padding: 0.75rem 1rem;}#header .container {min-height: 50px;}#header .btn.dropdown-toggle {padding-right: 0;}#header .dropdown-menu {--bs-dropdown-min-width: 13rem;}#header .dropdown-menu[data-bs-popper] {left: initial;right: 0;}#header .dropdown-menu.show {display: flex;}.form-control:focus {outline: 0;box-shadow: none;border-color: #00854d;}.base-value {color: #757575 !important;padding-left: 0.5rem;font-weight: normal;}.badge {vertical-align: middle;text-transform: uppercase;letter-spacing: 0.15em;--bs-badge-padding-x: 0.8rem;--bs-badge-font-weight: normal;--bs-badge-border-radius: 0.6rem;}.bg-secondary {background-color: #757575 !important;}.accordion {--bs-accordion-border-radius: 10px;--bs-accordion-inner-border-radius: calc(10px - 1px);--bs-accordion-color: var(--bs-body-color);--bs-accordion-active-color: var(--bs-body-color);--bs-accordion-active-bg: white;--bs-accordion-btn-active-icon: url("data:image/svg+xml,");}.accordion-button:focus {outline: 0;box-shadow: none;}.accordion-body {letter-spacing: -0.01em;}.bb-group {border: 0.6rem solid #f6f6f6;background-color: #f6f6f6;border-radius: 0.5rem;position: relative;display: inline-flex;vertical-align: middle;}.bb-group>.btn {--bs-btn-padding-x: 0.5rem;--bs-btn-padding-y: 0.22rem;--bs-btn-border-radius: 0.3rem;--bs-btn-border-width: 0;color: #545454;}.bb-group>.btn-check:checked+.btn, .bb-group .btn.active {color: black;font-weight: bold;background-color: white;}.paging {display: flex;}.paging .bb-group>.btn {min-width: 2rem;margin-left: 0.1rem;margin-right: 0.1rem;}.paging .bb-group>.btn:hover {background-color: white;}.paging a {text-decoration: none;}.btn-paging {--bs-btn-color: #757575;--bs-btn-border-color: #e2e2e2;--bs-btn-hover-color: black;--bs-btn-hover-bg: #f6f6f6;--bs-btn-hover-border-color: #e2e2e2;--bs-btn-focus-shadow-rgb: 108, 117, 125;--bs-btn-active-color: #fff;--bs-btn-active-bg: #e2e2e2;--bs-btn-active-border-color: #e2e2e2;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-gradient: none;--bs-btn-padding-y: 0.75rem;--bs-btn-padding-x: 1.1rem;--bs-btn-border-radius: 0.5rem;--bs-btn-font-weight: bold;background-color: #f6f6f6;}span.btn-paging {cursor: initial;}span.btn-paging:hover {color: #757575;}.paging-group {border: 1px solid #e2e2e2;border-radius: 0.5rem;}.paging-group>.bb-group {border: 0.53rem solid #f6f6f6;}#wrap {min-height: 100%;height: auto;padding: 112px 0 75px 0;margin: 0 auto -56px;}#footer {background-color: black;color: #757575;height: 56px;overflow: hidden;}.navbar-form {width: 60%;}.navbar-form button {margin-left: -50px;position: relative;}.search-icon {width: 16px;height: 16px;position: absolute;top: 16px;background-size: cover;background-image: url("data:image/svg+xml, %3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7.24976 12.5C10.1493 12.5 12.4998 10.1495 12.4998 7.25C12.4998 4.35051 10.1493 2 7.24976 2C4.35026 2 1.99976 4.35051 1.99976 7.25C1.99976 10.1495 4.35026 12.5 7.24976 12.5Z' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3Cpath d='M10.962 10.9625L13.9996 14.0001' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3C/svg%3E");}.navbar-form ::placeholder {color: #e2e2e2;}.ellipsis {overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}.data-table {table-layout: fixed;overflow-wrap: anywhere;margin-left: 8px;margin-top: 2rem;margin-bottom: 2rem;width: calc(100% - 16px);}.data-table thead {padding-bottom: 20px;}.table.data-table> :not(caption)>*>* {padding: 0.8rem 0.8rem;background-color: var(--bs-table-bg);border-bottom-width: 1px;box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg);}.table.data-table>thead>*>* {padding-bottom: 1.5rem;}.table.data-table>*>*:last-child>* {border-bottom: none;}.data-table thead, .data-table thead tr, .data-table thead th {color: #757575;border: none;font-weight: normal;}.data-table tbody th {color: #757575;font-weight: normal;}.data-table tbody {background: white;border-radius: 8px;box-shadow: 0 0 0 8px white;}.data-table h3, .data-table h5, .data-table h6 {margin-bottom: 0;}.data-table h3, .data-table h5 {color: var(--bs-body-color);}.accordion .table.data-table>thead>*>* {padding-bottom: 0;}.info-table tbody {display: inline-table;width: 100%;}.info-table td {font-weight: bold;}.info-table tr>td:first-child {font-weight: normal;color: #757575;}.ns:before {content: " ";}.nc:before {content: ",";}.trezor-logo {width: 128px;height: 32px;position: absolute;top: 16px;background-size: cover;background-image: url("data:image/svg+xml,%3Csvg style='width: 128px%3B' version='1.1' xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 163.7 41.9' space='preserve'%3E%3Cpolygon points='101.1 12.8 118.2 12.8 118.2 17.3 108.9 29.9 118.2 29.9 118.2 35.2 101.1 35.2 101.1 30.7 110.4 18.1 101.1 18.1'%3E%3C/polygon%3E%3Cpath d='M158.8 26.9c2.1-0.8 4.3-2.9 4.3-6.6c0-4.5-3.1-7.4-7.7-7.4h-10.5v22.3h5.8v-7.5h2.2l4.1 7.5h6.7L158.8 26.9z M154.7 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C157.2 21.6 156.2 22.5 154.7 22.5z'%3E%3C/path%3E%3Cpath d='M130.8 12.5c-6.8 0-11.6 4.9-11.6 11.5s4.9 11.5 11.6 11.5s11.7-4.9 11.7-11.5S137.6 12.5 130.8 12.5z M130.8 30.3c-3.4 0-5.7-2.6-5.7-6.3c0-3.8 2.3-6.3 5.7-6.3c3.4 0 5.8 2.6 5.8 6.3C136.6 27.7 134.2 30.3 130.8 30.3z'%3E%3C/path%3E%3Cpolygon points='82.1 12.8 98.3 12.8 98.3 18 87.9 18 87.9 21.3 98 21.3 98 26.4 87.9 26.4 87.9 30 98.3 30 98.3 35.2 82.1 35.2'%3E%3C/polygon%3E%3Cpath d='M24.6 9.7C24.6 4.4 20 0 14.4 0S4.2 4.4 4.2 9.7v3.1H0v22.3h0l14.4 6.7l14.4-6.7h0V12.9h-4.2V9.7z M9.4 9.7c0-2.5 2.2-4.5 5-4.5s5 2 5 4.5v3.1H9.4V9.7z M23 31.5l-8.6 4l-8.6-4V18.1H23V31.5z'%3E%3C/path%3E%3Cpath d='M79.4 20.3c0-4.5-3.1-7.4-7.7-7.4H61.2v22.3H67v-7.5h2.2l4.1 7.5H80l-4.9-8.3C77.2 26.1 79.4 24 79.4 20.3z M71 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C73.5 21.6 72.5 22.5 71 22.5z'%3E%3C/path%3E%3Cpolygon points='40.5 12.8 58.6 12.8 58.6 18.1 52.4 18.1 52.4 35.2 46.6 35.2 46.6 18.1 40.5 18.1'%3E%3C/polygon%3E%3C/svg%3E");}.copyable::before, .copied::before {width: 18px;height: 16px;margin: 3px -18px;content: "";position: absolute;background-size: cover;}.copyable::before {display: none;cursor: copy;background-image: url("data:image/svg+xml,%3Csvg width='18' height='16' viewBox='0 0 18 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.5 10.4996H13.5V2.49963H5.5V5.49963' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M10.4998 5.49976H2.49976V13.4998H10.4998V5.49976Z' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");}.copyable:hover::before {display: inline-block;}.copied::before {transition: all 0.4s ease;transform: scale(1.2);background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='16' viewBox='-30 -30 330 330'%3E%3Cpath d='M 30,180 90,240 240,30' style='stroke:%2300854D;stroke-width:32;fill:none'/%3E%3C/svg%3E");}.h-data {letter-spacing: 0.12em;font-weight: normal !important;}.tx-detail {background: #f6f6f6;color: #757575;border-radius: 10px;box-shadow: 0 0 0 10px white;width: calc(100% - 20px);margin-left: 10px;margin-top: 3rem;overflow-wrap: break-word;}.tx-detail:first-child {margin-top: 1rem;}.tx-detail:last-child {margin-bottom: 2rem;}.tx-detail span.ellipsis, .tx-detail a.ellipsis {display: block;float: left;max-width: 100%;}.tx-detail>.head, .tx-detail>.footer {padding: 1.5rem;--bs-gutter-x: 0;}.tx-detail>.head {border-radius: 10px 10px 0 0;}.tx-detail .txid {font-size: 106%;letter-spacing: -0.01em;}.tx-detail>.body {padding: 0 1.5rem;--bs-gutter-x: 0;letter-spacing: -0.01em;}.tx-detail>.subhead {padding: 1.5rem 1.5rem 0.4rem 1.5rem;--bs-gutter-x: 0;letter-spacing: 0.1em;text-transform: uppercase;color: var(--bs-body-color);}.tx-detail>.subhead-2 {padding: 0.3rem 1.5rem 0 1.5rem;--bs-gutter-x: 0;font-size: .875em;color: var(--bs-body-color);}.tx-in .col-12, .tx-out .col-12, .tx-addr .col-12 {background-color: white;padding: 1.2rem 1.3rem;border-bottom: 1px solid #f6f6f6;}.amt-out {padding: 1.2rem 0 1.2rem 1rem;text-align: right;overflow-wrap: break-word;}.tx-in .col-12:last-child, .tx-out .col-12:last-child {border-bottom: none;}.tx-own {background-color: #fff9e3 !important;}.tx-amt {float: right !important;}.spent {color: #dc3545 !important;}.unspent {color: #28a745 !important;}.outpoint {color: #757575 !important;}.spent, .unspent, .outpoint {display: inline-block;text-align: right;min-width: 18px;text-decoration: none !important;}.octicon {height: 24px;width: 24px;margin-left: -12px;margin-top: 19px;position: absolute;background-size: cover;background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9 4.5L16.5 12L9 19.5' stroke='%23AFAFAF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A");}.txvalue {color: var(--bs-body-color);font-weight: bold;}.txerror {color: #c51f13;}.txerror a, .txerror .txvalue {color: #c51f13;}.txerror .copyable::before, .txerror .copied::before {filter: invert(86%) sepia(43%) saturate(732%) hue-rotate(367deg) brightness(84%);}.tx-amt .amt:hover, .tx-amt.amt:hover, .amt-out>.amt:hover {color: var(--bs-body-color);}.prim-amt {display: initial;}.sec-amt {display: none;}.csec-amt {display: none;}.base-amt {display: none;}.cbase-amt {display: none;}.tooltip {--bs-tooltip-opacity: 1;--bs-tooltip-max-width: 380px;--bs-tooltip-bg: #fff;--bs-tooltip-color: var(--bs-body-color);--bs-tooltip-padding-x: 1rem;--bs-tooltip-padding-y: 0.8rem;filter: drop-shadow(0px 24px 64px rgba(22, 27, 45, 0.25));}.l-tooltip {text-align: start;display: inline-block;}.l-tooltip .prim-amt, .l-tooltip .sec-amt, .l-tooltip .csec-amt, .l-tooltip .base-amt, .l-tooltip .cbase-amt {display: initial;float: right;}.l-tooltip .amt-time {padding-right: 3rem;float: left;}.amt-dec {font-size: 95%;}.unconfirmed {color: white;background-color: #c51e13;padding: 0.7rem 1.2rem;border-radius: 1.4rem;}.json {word-wrap: break-word;font-size: smaller;background: #002b31;border-radius: 8px;}#raw {padding: 1.5rem 2rem;color: #ffffff;letter-spacing: 0.02em;}#raw .string {color: #2bca87;}#raw .number, #raw .boolean {color: #efc941;}#raw .null {color: red;}@media (max-width: 768px) {body {font-size: 0.8rem;background: linear-gradient(to bottom, #f6f6f6 500px, #e5e5e5 0), #e5e5e5;}.container {padding-left: 2px;padding-right: 2px;}.accordion-body {padding: var(--bs-accordion-body-padding-y) 0;}.octicon {scale: 60% !important;margin-top: -2px;}.unconfirmed {padding: 0.1rem 0.8rem;}.btn {--bs-btn-font-size: 0.8rem;}}@media (max-width: 991px) {#header .container {min-height: 40px;}#header .dropdown-menu[data-bs-popper] {left: 0;right: initial;}.trezor-logo {top: 10px;}.octicon {scale: 80%;}.table.data-table>:not(caption)>*>* {padding: 0.8rem 0.4rem;}.tx-in .col-12, .tx-out .col-12, .tx-addr .col-12 {padding: 0.7rem 1.1rem;}.amt-out {padding: 0.7rem 0 0.7rem 1rem }}@media (min-width: 769px) {body {font-size: 0.9rem;}.btn {--bs-btn-font-size: 0.9rem;}}@media (min-width: 1200px) {.h1, h1 {font-size: 2.4rem;}body {font-size: 1rem;}.btn {--bs-btn-font-size: 1rem;}} \ No newline at end of file diff --git a/static/css/main.min.4.css b/static/css/main.min.4.css new file mode 100644 index 0000000000..54dd88a23d --- /dev/null +++ b/static/css/main.min.4.css @@ -0,0 +1 @@ +@import "TTHoves/TTHoves.css";* {margin: 0;padding: 0;outline: none;font-family: "TT Hoves", -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;}html, body {height: 100%;}body {min-height: 100%;margin: 0;background: linear-gradient(to bottom, #f6f6f6 360px, #e5e5e5 0), #e5e5e5;background-repeat: no-repeat;}a {color: #00854d;text-decoration: none;}a:hover {color: #00854d;text-decoration: underline;}select {border-radius: 0.5rem;padding-left: 0.5rem;border: 1px solid #ced4da;color: var(--bs-body-color);min-height: 45px;}#header {position: fixed;top: 0;left: 0;width: 100%;margin: 0;padding-bottom: 0;padding-top: 0;background-color: white;border-bottom: 1px solid #f6f6f6;z-index: 10;}#header a {color: var(--bs-navbar-brand-color);}#header a:hover {color: var(--bs-navbar-brand-hover-color);}#header .navbar {--bs-navbar-padding-y: 0.7rem;}#header .form-control-lg {font-size: 1rem;padding: 0.75rem 1rem;}#header .container {min-height: 50px;}#header .btn.dropdown-toggle {padding-right: 0;}#header .dropdown-menu {--bs-dropdown-min-width: 13rem;}#header .dropdown-menu[data-bs-popper] {left: initial;right: 0;}#header .dropdown-menu.show {display: flex;}.form-control:focus {outline: 0;box-shadow: none;border-color: #00854d;}.base-value {color: #757575 !important;padding-left: 0.5rem;font-weight: normal;}.badge {vertical-align: middle;text-transform: uppercase;letter-spacing: 0.15em;--bs-badge-padding-x: 0.8rem;--bs-badge-font-weight: normal;--bs-badge-border-radius: 0.6rem;}.bg-secondary {background-color: #757575 !important;}.accordion {--bs-accordion-border-radius: 10px;--bs-accordion-inner-border-radius: calc(10px - 1px);--bs-accordion-color: var(--bs-body-color);--bs-accordion-active-color: var(--bs-body-color);--bs-accordion-active-bg: white;--bs-accordion-btn-active-icon: url("data:image/svg+xml,");}.accordion-button:focus {outline: 0;box-shadow: none;}.accordion-body {letter-spacing: -0.01em;}.bb-group {border: 0.6rem solid #f6f6f6;background-color: #f6f6f6;border-radius: 0.5rem;position: relative;display: inline-flex;vertical-align: middle;}.bb-group>.btn {--bs-btn-padding-x: 0.5rem;--bs-btn-padding-y: 0.22rem;--bs-btn-border-radius: 0.3rem;--bs-btn-border-width: 0;color: #545454;}.bb-group>.btn-check:checked+.btn, .bb-group .btn.active {color: black;font-weight: bold;background-color: white;}.paging {display: flex;}.paging .bb-group>.btn {min-width: 2rem;margin-left: 0.1rem;margin-right: 0.1rem;}.paging .bb-group>.btn:hover {background-color: white;}.paging a {text-decoration: none;}.btn-paging {--bs-btn-color: #757575;--bs-btn-border-color: #e2e2e2;--bs-btn-hover-color: black;--bs-btn-hover-bg: #f6f6f6;--bs-btn-hover-border-color: #e2e2e2;--bs-btn-focus-shadow-rgb: 108, 117, 125;--bs-btn-active-color: #fff;--bs-btn-active-bg: #e2e2e2;--bs-btn-active-border-color: #e2e2e2;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-gradient: none;--bs-btn-padding-y: 0.75rem;--bs-btn-padding-x: 1.1rem;--bs-btn-border-radius: 0.5rem;--bs-btn-font-weight: bold;background-color: #f6f6f6;}span.btn-paging {cursor: initial;}span.btn-paging:hover {color: #757575;}.btn-paging.active:hover {background-color: white;}.paging-group {border: 1px solid #e2e2e2;border-radius: 0.5rem;}.paging-group>.bb-group {border: 0.53rem solid #f6f6f6;}#wrap {min-height: 100%;height: auto;padding: 112px 0 75px 0;margin: 0 auto -56px;}#footer {background-color: black;color: #757575;height: 56px;overflow: hidden;}.navbar-form {width: 60%;}.navbar-form button {margin-left: -50px;position: relative;}.search-icon {width: 16px;height: 16px;position: absolute;top: 16px;background-size: cover;background-image: url("data:image/svg+xml, %3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7.24976 12.5C10.1493 12.5 12.4998 10.1495 12.4998 7.25C12.4998 4.35051 10.1493 2 7.24976 2C4.35026 2 1.99976 4.35051 1.99976 7.25C1.99976 10.1495 4.35026 12.5 7.24976 12.5Z' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3Cpath d='M10.962 10.9625L13.9996 14.0001' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' /%3E%3C/svg%3E");}.navbar-form ::placeholder {color: #e2e2e2;}.ellipsis {overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}.data-table {table-layout: fixed;overflow-wrap: anywhere;margin-left: 8px;margin-top: 2rem;margin-bottom: 2rem;width: calc(100% - 16px);}.data-table thead {padding-bottom: 20px;}.table.data-table> :not(caption)>*>* {padding: 0.8rem 0.8rem;background-color: var(--bs-table-bg);border-bottom-width: 1px;box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg);}.table.data-table>thead>*>* {padding-bottom: 1.5rem;}.table.data-table>*>*:last-child>* {border-bottom: none;}.data-table thead, .data-table thead tr, .data-table thead th {color: #757575;border: none;font-weight: normal;}.data-table tbody th {color: #757575;font-weight: normal;}.data-table tbody {background: white;border-radius: 8px;box-shadow: 0 0 0 8px white;}.data-table h3, .data-table h5, .data-table h6 {margin-bottom: 0;}.data-table h3, .data-table h5 {color: var(--bs-body-color);}.accordion .table.data-table>thead>*>* {padding-bottom: 0;}.info-table tbody {display: inline-table;width: 100%;}.info-table td {font-weight: bold;}.info-table tr>td:first-child {font-weight: normal;color: #757575;}.ns:before {content: " ";}.nc:before {content: ",";}.trezor-logo {width: 128px;height: 32px;position: absolute;top: 16px;background-size: cover;background-image: url("data:image/svg+xml,%3Csvg style='width: 128px%3B' version='1.1' xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 163.7 41.9' space='preserve'%3E%3Cpolygon points='101.1 12.8 118.2 12.8 118.2 17.3 108.9 29.9 118.2 29.9 118.2 35.2 101.1 35.2 101.1 30.7 110.4 18.1 101.1 18.1'%3E%3C/polygon%3E%3Cpath d='M158.8 26.9c2.1-0.8 4.3-2.9 4.3-6.6c0-4.5-3.1-7.4-7.7-7.4h-10.5v22.3h5.8v-7.5h2.2l4.1 7.5h6.7L158.8 26.9z M154.7 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C157.2 21.6 156.2 22.5 154.7 22.5z'%3E%3C/path%3E%3Cpath d='M130.8 12.5c-6.8 0-11.6 4.9-11.6 11.5s4.9 11.5 11.6 11.5s11.7-4.9 11.7-11.5S137.6 12.5 130.8 12.5z M130.8 30.3c-3.4 0-5.7-2.6-5.7-6.3c0-3.8 2.3-6.3 5.7-6.3c3.4 0 5.8 2.6 5.8 6.3C136.6 27.7 134.2 30.3 130.8 30.3z'%3E%3C/path%3E%3Cpolygon points='82.1 12.8 98.3 12.8 98.3 18 87.9 18 87.9 21.3 98 21.3 98 26.4 87.9 26.4 87.9 30 98.3 30 98.3 35.2 82.1 35.2'%3E%3C/polygon%3E%3Cpath d='M24.6 9.7C24.6 4.4 20 0 14.4 0S4.2 4.4 4.2 9.7v3.1H0v22.3h0l14.4 6.7l14.4-6.7h0V12.9h-4.2V9.7z M9.4 9.7c0-2.5 2.2-4.5 5-4.5s5 2 5 4.5v3.1H9.4V9.7z M23 31.5l-8.6 4l-8.6-4V18.1H23V31.5z'%3E%3C/path%3E%3Cpath d='M79.4 20.3c0-4.5-3.1-7.4-7.7-7.4H61.2v22.3H67v-7.5h2.2l4.1 7.5H80l-4.9-8.3C77.2 26.1 79.4 24 79.4 20.3z M71 22.5h-4V18h4c1.5 0 2.5 0.9 2.5 2.2C73.5 21.6 72.5 22.5 71 22.5z'%3E%3C/path%3E%3Cpolygon points='40.5 12.8 58.6 12.8 58.6 18.1 52.4 18.1 52.4 35.2 46.6 35.2 46.6 18.1 40.5 18.1'%3E%3C/polygon%3E%3C/svg%3E");}.copyable::before, .copied::before {width: 18px;height: 16px;margin: 3px -18px;content: "";position: absolute;background-size: cover;}.copyable::before {display: none;cursor: copy;background-image: url("data:image/svg+xml,%3Csvg width='18' height='16' viewBox='0 0 18 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.5 10.4996H13.5V2.49963H5.5V5.49963' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M10.4998 5.49976H2.49976V13.4998H10.4998V5.49976Z' stroke='%2300854D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");}.copyable:hover::before {display: inline-block;}.copied::before {transition: all 0.4s ease;transform: scale(1.2);background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='16' viewBox='-30 -30 330 330'%3E%3Cpath d='M 30,180 90,240 240,30' style='stroke:%2300854D;stroke-width:32;fill:none'/%3E%3C/svg%3E");}.h-data {letter-spacing: 0.12em;font-weight: normal !important;}.tx-detail {background: #f6f6f6;color: #757575;border-radius: 10px;box-shadow: 0 0 0 10px white;width: calc(100% - 20px);margin-left: 10px;margin-top: 3rem;overflow-wrap: break-word;}.tx-detail:first-child {margin-top: 1rem;}.tx-detail:last-child {margin-bottom: 2rem;}.tx-detail span.ellipsis, .tx-detail a.ellipsis {display: block;float: left;max-width: 100%;}.tx-detail>.head, .tx-detail>.footer {padding: 1.5rem;--bs-gutter-x: 0;}.tx-detail>.head {border-radius: 10px 10px 0 0;}.tx-detail .txid {font-size: 106%;letter-spacing: -0.01em;}.tx-detail>.body {padding: 0 1.5rem;--bs-gutter-x: 0;letter-spacing: -0.01em;}.tx-detail>.subhead {padding: 1.5rem 1.5rem 0.4rem 1.5rem;--bs-gutter-x: 0;letter-spacing: 0.1em;text-transform: uppercase;color: var(--bs-body-color);}.tx-detail>.subhead-2 {padding: 0.3rem 1.5rem 0 1.5rem;--bs-gutter-x: 0;font-size: .875em;color: var(--bs-body-color);}.tx-in .col-12, .tx-out .col-12, .tx-addr .col-12 {background-color: white;padding: 1.2rem 1.3rem;border-bottom: 1px solid #f6f6f6;}.amt-out {padding: 1.2rem 0 1.2rem 1rem;text-align: right;overflow-wrap: break-word;}.tx-in .col-12:last-child, .tx-out .col-12:last-child {border-bottom: none;}.tx-own {background-color: #fff9e3 !important;}.tx-amt {float: right !important;}.spent {color: #dc3545 !important;}.unspent {color: #28a745 !important;}.outpoint {color: #757575 !important;}.spent, .unspent, .outpoint {display: inline-block;text-align: right;min-width: 18px;text-decoration: none !important;}.octicon {height: 24px;width: 24px;margin-left: -12px;margin-top: 19px;position: absolute;background-size: cover;background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9 4.5L16.5 12L9 19.5' stroke='%23AFAFAF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A");}.txvalue {color: var(--bs-body-color);font-weight: bold;}.txerror {color: #c51f13;}.txerror a, .txerror .txvalue {color: #c51f13;}.txerror .copyable::before, .txerror .copied::before {filter: invert(86%) sepia(43%) saturate(732%) hue-rotate(367deg) brightness(84%);}.tx-amt .amt:hover, .tx-amt.amt:hover, .amt-out>.amt:hover {color: var(--bs-body-color);}.prim-amt {display: initial;}.sec-amt {display: none;}.csec-amt {display: none;}.base-amt {display: none;}.cbase-amt {display: none;}.tooltip {--bs-tooltip-opacity: 1;--bs-tooltip-max-width: 380px;--bs-tooltip-bg: #fff;--bs-tooltip-color: var(--bs-body-color);--bs-tooltip-padding-x: 1rem;--bs-tooltip-padding-y: 0.8rem;filter: drop-shadow(0px 24px 64px rgba(22, 27, 45, 0.25));}.l-tooltip {text-align: start;display: inline-block;}.l-tooltip .prim-amt, .l-tooltip .sec-amt, .l-tooltip .csec-amt, .l-tooltip .base-amt, .l-tooltip .cbase-amt {display: initial;float: right;}.l-tooltip .amt-time {padding-right: 3rem;float: left;}.amt-dec {font-size: 95%;}.unconfirmed {color: white;background-color: #c51e13;padding: 0.7rem 1.2rem;border-radius: 1.4rem;}.json {word-wrap: break-word;font-size: smaller;background: #002b31;border-radius: 8px;}#raw {padding: 1.5rem 2rem;color: #ffffff;letter-spacing: 0.02em;}#raw .string {color: #2bca87;}#raw .number, #raw .boolean {color: #efc941;}#raw .null {color: red;}@media (max-width: 768px) {body {font-size: 0.8rem;background: linear-gradient(to bottom, #f6f6f6 500px, #e5e5e5 0), #e5e5e5;}.container {padding-left: 2px;padding-right: 2px;}.accordion-body {padding: var(--bs-accordion-body-padding-y) 0;}.octicon {scale: 60% !important;margin-top: -2px;}.unconfirmed {padding: 0.1rem 0.8rem;}.btn {--bs-btn-font-size: 0.8rem;}}@media (max-width: 991px) {#header .container {min-height: 40px;}#header .dropdown-menu[data-bs-popper] {left: 0;right: initial;}.trezor-logo {top: 10px;}.octicon {scale: 80%;}.table.data-table>:not(caption)>*>* {padding: 0.8rem 0.4rem;}.tx-in .col-12, .tx-out .col-12, .tx-addr .col-12 {padding: 0.7rem 1.1rem;}.amt-out {padding: 0.7rem 0 0.7rem 1rem }}@media (min-width: 769px) {body {font-size: 0.9rem;}.btn {--bs-btn-font-size: 0.9rem;}}@media (min-width: 1200px) {.h1, h1 {font-size: 2.4rem;}body {font-size: 1rem;}.btn {--bs-btn-font-size: 1rem;}} \ No newline at end of file diff --git a/static/js/main.js b/static/js/main.js index d729efc91a..b1d31408f8 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -85,6 +85,79 @@ function addressAliasTooltip() { return `${type}
${address}
`; } +function handleTxPage(rawData, txId) { + const rawOutput = document.getElementById('raw'); + const rawButton = document.getElementById('raw-button'); + const rawHexButton = document.getElementById('raw-hex-button'); + + rawOutput.innerHTML = syntaxHighlight(rawData); + + let isShowingHexData = false; + + const memoizedResponses = {}; + + async function getTransactionHex(txId) { + // BTC-like coins have a 'hex' field in the raw data + if (rawData['hex']) { + return rawData['hex']; + } + if (memoizedResponses[txId]) { + return memoizedResponses[txId]; + } + const fetchedData = await fetchTransactionHex(txId); + memoizedResponses[txId] = fetchedData; + return fetchedData; + } + + async function fetchTransactionHex(txId) { + const response = await fetch(`/api/rawtx/${txId}`); + if (!response.ok) { + throw new Error(`Error fetching data: ${response.status}`); + } + const txHex = await response.text(); + const hexWithoutQuotes = txHex.replace(/"/g, ''); + return hexWithoutQuotes; + } + + function updateButtonStyles() { + if (isShowingHexData) { + rawButton.classList.add('active'); + rawButton.style.fontWeight = 'normal'; + rawHexButton.classList.remove('active'); + rawHexButton.style.fontWeight = 'bold'; + } else { + rawButton.classList.remove('active'); + rawButton.style.fontWeight = 'bold'; + rawHexButton.classList.add('active'); + rawHexButton.style.fontWeight = 'normal'; + } + } + + updateButtonStyles(); + + rawHexButton.addEventListener('click', async () => { + if (!isShowingHexData) { + try { + const txHex = await getTransactionHex(txId); + rawOutput.textContent = txHex; + } catch (error) { + console.error('Error fetching raw transaction hex:', error); + rawOutput.textContent = `Error fetching raw transaction hex: ${error.message}`; + } + isShowingHexData = true; + updateButtonStyles(); + } + }); + + rawButton.addEventListener('click', () => { + if (isShowingHexData) { + rawOutput.innerHTML = syntaxHighlight(rawData); + isShowingHexData = false; + updateButtonStyles(); + } + }); +} + window.addEventListener("DOMContentLoaded", () => { const a = getCoinCookie(); if (a?.length === 3) { @@ -127,7 +200,8 @@ window.addEventListener("DOMContentLoaded", () => { if (e.clientX < e.target.getBoundingClientRect().x) { let t = e.target.getAttribute("cc"); if (!t) t = e.target.innerText; - navigator.clipboard.writeText(t); + const textToCopy = t.trim(); + navigator.clipboard.writeText(textToCopy); e.target.className = e.target.className.replace("copyable", "copied"); setTimeout( () => diff --git a/static/js/main.min.3.js b/static/js/main.min.3.js deleted file mode 100644 index 72a9ffe0ad..0000000000 --- a/static/js/main.min.3.js +++ /dev/null @@ -1 +0,0 @@ -function syntaxHighlight(t){return(t=(t=JSON.stringify(t,void 0,2)).replace(/&/g,"&").replace(//g,">")).length>1e6?`${t}`:t.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,t=>{let e="number";return/^"/.test(t)?e=/:$/.test(t)?"key":"string":/true|false/.test(t)?e="boolean":/null/.test(t)&&(e="null"),`${t}`})}function getCoinCookie(){if(hasSecondary)return document.cookie.split("; ").find(t=>t.startsWith("secondary_coin="))?.split("=")}function changeCSSStyle(t,e,l){let a=document.all?"rules":"cssRules";for(i=0,len=document.styleSheets[1][a].length;i`;if(a){let n=a.getAttribute("tm");n||(n="now"),r+=`${n}${a.outerHTML}
`}if(s&&(r+=`now${s.outerHTML}
`),e){let o=e.getAttribute("tm");o||(o="now"),r+=`${o}${e.outerHTML}
`}return l&&(r+=`now${l.outerHTML}
`),`${r}`}function addressAliasTooltip(){let t=this.getAttribute("alias-type"),e=this.getAttribute("cc");return`${t}
${e}
`}window.addEventListener("DOMContentLoaded",()=>{let t=getCoinCookie();t?.length===3&&("true"===t[2]&&(changeCSSStyle(".prim-amt","display","none"),changeCSSStyle(".sec-amt","display","initial")),document.querySelectorAll(".amt").forEach(t=>new bootstrap.Tooltip(t,{title:amountTooltip,html:!0}))),document.querySelectorAll("[alias-type]").forEach(t=>new bootstrap.Tooltip(t,{title:addressAliasTooltip,html:!0})),document.querySelectorAll("[tt]").forEach(t=>new bootstrap.Tooltip(t,{title:t.getAttribute("tt")})),document.querySelectorAll("#header .bb-group>.btn-check").forEach(t=>t.addEventListener("click",t=>{let e=getCoinCookie(),l="secondary-coin"===t.target.id;e?.length===3&&"true"===e[2]!==l&&(document.cookie=`${e[0]}=${e[1]}=${l}; Path=/`,changeCSSStyle(".prim-amt","display",l?"none":"initial"),changeCSSStyle(".sec-amt","display",l?"initial":"none"))})),document.querySelectorAll(".copyable").forEach(t=>t.addEventListener("click",t=>{if(t.clientXt.target.className=t.target.className.replace("copied","copyable"),1e3),t.preventDefault()}}))}); \ No newline at end of file diff --git a/static/js/main.min.4.js b/static/js/main.min.4.js new file mode 100644 index 0000000000..5e237185ab --- /dev/null +++ b/static/js/main.min.4.js @@ -0,0 +1 @@ +function syntaxHighlight(t){return(t=(t=JSON.stringify(t,void 0,2)).replace(/&/g,"&").replace(//g,">")).length>1e6?`${t}`:t.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,(t=>{let e="number";return/^"/.test(t)?e=/:$/.test(t)?"key":"string":/true|false/.test(t)?e="boolean":/null/.test(t)&&(e="null"),`${t}`}))}function getCoinCookie(){if(hasSecondary)return document.cookie.split("; ").find((t=>t.startsWith("secondary_coin=")))?.split("=")}function changeCSSStyle(t,e,n){const a=document.all?"rules":"cssRules";for(i=0,len=document.styleSheets[1][a].length;i`;if(a){let t=a.getAttribute("tm");t||(t="now"),i+=`${t}${a.outerHTML}
`}if(o&&(i+=`now${o.outerHTML}
`),e){let t=e.getAttribute("tm");t||(t="now"),i+=`${t}${e.outerHTML}
`}return n&&(i+=`now${n.outerHTML}
`),`${i}`}function addressAliasTooltip(){return`${this.getAttribute("alias-type")}
${this.getAttribute("cc")}
`}function handleTxPage(t,e){const n=document.getElementById("raw"),a=document.getElementById("raw-button"),o=document.getElementById("raw-hex-button");n.innerHTML=syntaxHighlight(t);let i=!1;const r={};async function s(e){if(t.hex)return t.hex;if(r[e])return r[e];const n=await async function(t){const e=await fetch(`/api/rawtx/${t}`);if(!e.ok)throw new Error(`Error fetching data: ${e.status}`);const n=await e.text();return n.replace(/"/g,"")}(e);return r[e]=n,n}function l(){i?(a.classList.add("active"),a.style.fontWeight="normal",o.classList.remove("active"),o.style.fontWeight="bold"):(a.classList.remove("active"),a.style.fontWeight="bold",o.classList.add("active"),o.style.fontWeight="normal")}l(),o.addEventListener("click",(async()=>{if(!i){try{const t=await s(e);n.textContent=t}catch(t){console.error("Error fetching raw transaction hex:",t),n.textContent=`Error fetching raw transaction hex: ${t.message}`}i=!0,l()}})),a.addEventListener("click",(()=>{i&&(n.innerHTML=syntaxHighlight(t),i=!1,l())}))}window.addEventListener("DOMContentLoaded",(()=>{const t=getCoinCookie();3===t?.length&&("true"===t[2]&&(changeCSSStyle(".prim-amt","display","none"),changeCSSStyle(".sec-amt","display","initial")),document.querySelectorAll(".amt").forEach((t=>new bootstrap.Tooltip(t,{title:amountTooltip,html:!0})))),document.querySelectorAll("[alias-type]").forEach((t=>new bootstrap.Tooltip(t,{title:addressAliasTooltip,html:!0}))),document.querySelectorAll("[tt]").forEach((t=>new bootstrap.Tooltip(t,{title:t.getAttribute("tt")}))),document.querySelectorAll("#header .bb-group>.btn-check").forEach((t=>t.addEventListener("click",(t=>{const e=getCoinCookie(),n="secondary-coin"===t.target.id;3===e?.length&&"true"===e[2]!==n&&(document.cookie=`${e[0]}=${e[1]}=${n}; Path=/`,changeCSSStyle(".prim-amt","display",n?"none":"initial"),changeCSSStyle(".sec-amt","display",n?"initial":"none"))})))),document.querySelectorAll(".copyable").forEach((t=>t.addEventListener("click",(t=>{if(t.clientXt.target.className=t.target.className.replace("copied","copyable")),1e3),t.preventDefault()}}))))})); \ No newline at end of file diff --git a/static/templates/tx.html b/static/templates/tx.html index f148f984f2..5d518d7093 100644 --- a/static/templates/tx.html +++ b/static/templates/tx.html @@ -182,13 +182,19 @@
{{if $tx.EthereumSpecific.ParsedData.Name}}{{$tx.EthereumSpecif {{end}} {{end}}
-
Raw Transaction
-
-

+    
+    
+    
+

     
-
{{end}} diff --git a/tests/dbtestdata/fakechain_ethereumtype.go b/tests/dbtestdata/fakechain_ethereumtype.go index a846927100..3722ef416a 100644 --- a/tests/dbtestdata/fakechain_ethereumtype.go +++ b/tests/dbtestdata/fakechain_ethereumtype.go @@ -139,6 +139,11 @@ func (c *fakeBlockChainEthereumType) EthereumTypeRpcCall(data, to, from string) return data + "abcd", nil } +// EthereumTypeGetRawTransaction returns simulated transaction hex data +func (c *fakeBlockChainEthereumType) EthereumTypeGetRawTransaction(txid string) (string, error) { + return txid + "abcd", nil +} + // GetTokenURI returns URI derived from the input contractDesc func (c *fakeBlockChainEthereumType) GetTokenURI(contractDesc bchain.AddressDescriptor, tokenID *big.Int) (string, error) { return "https://ipfs.io/ipfs/" + contractDesc.String()[3:] + ".json", nil From c3cdf9bca4ca1e2b9e31b83e2aa849260f86901b Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 9 Dec 2024 20:57:37 +0100 Subject: [PATCH 392/530] Fix usage of Network configuration parameter --- bchain/coins/eth/ethrpc.go | 7 +------ bchain/coins/eth/stakingpool.go | 6 +++++- db/bulkconnect.go | 11 ++++++----- fiat/fiat_rates.go | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 1412b0e724..6ed4b14444 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -160,12 +160,7 @@ func (b *EthereumRPC) Initialize() error { return errors.Errorf("Unknown network id %v", id) } - networkConfig := b.ChainConfig.Network - if networkConfig == "" { - networkConfig = b.ChainConfig.CoinShortcut - } - - err = b.initStakingPools(networkConfig) + err = b.initStakingPools() if err != nil { return err } diff --git a/bchain/coins/eth/stakingpool.go b/bchain/coins/eth/stakingpool.go index f773dae911..659307c877 100644 --- a/bchain/coins/eth/stakingpool.go +++ b/bchain/coins/eth/stakingpool.go @@ -11,7 +11,11 @@ import ( "github.com/trezor/blockbook/bchain" ) -func (b *EthereumRPC) initStakingPools(network string) error { +func (b *EthereumRPC) initStakingPools() error { + network := b.ChainConfig.Network + if network == "" { + network = b.ChainConfig.CoinShortcut + } // for now only single staking pool envVar := strings.ToUpper(network) + "_STAKING_POOL_CONTRACT" envValue := os.Getenv(envVar) diff --git a/db/bulkconnect.go b/db/bulkconnect.go index 2a044bc12a..8c2095bf7e 100644 --- a/db/bulkconnect.go +++ b/db/bulkconnect.go @@ -438,6 +438,11 @@ func (b *BulkConnect) Close() error { return err } } + if err := b.d.SetInconsistentState(false); err != nil { + return err + } + glog.Info("rocksdb: bulk connect closed, db set to open state") + bt, err := b.d.loadBlockTimes() if err != nil { return err @@ -446,11 +451,7 @@ func (b *BulkConnect) Close() error { if b.d.metrics != nil { b.d.metrics.AvgBlockPeriod.Set(float64(avg)) } - - if err := b.d.SetInconsistentState(false); err != nil { - return err - } - glog.Info("rocksdb: bulk connect closed, db set to open state") + glog.Info("rocksdb: processed block times") b.d = nil return nil } diff --git a/fiat/fiat_rates.go b/fiat/fiat_rates.go index 13e7168fb3..d6ead4bb0e 100644 --- a/fiat/fiat_rates.go +++ b/fiat/fiat_rates.go @@ -108,7 +108,7 @@ func NewFiatRates(db *db.RocksDB, config *common.Config, metrics *common.Metrics // a small hack - in tests the callback is not used, therefore there is no delay slowing down the test throttle = false } - fr.downloader = NewCoinGeckoDownloader(db, db.GetInternalState().CoinShortcut, rdParams.URL, rdParams.Coin, rdParams.PlatformIdentifier, rdParams.PlatformVsCurrency, fr.allowedVsCurrencies, fr.timeFormat, metrics, throttle) + fr.downloader = NewCoinGeckoDownloader(db, db.GetInternalState().GetNetwork(), rdParams.URL, rdParams.Coin, rdParams.PlatformIdentifier, rdParams.PlatformVsCurrency, fr.allowedVsCurrencies, fr.timeFormat, metrics, throttle) if is != nil { is.HasFiatRates = true is.HasTokenFiatRates = fr.downloadTokens From f2cd67e81f6245c5291624f0e409200d42542b4a Mon Sep 17 00:00:00 2001 From: hishope Date: Sun, 1 Dec 2024 21:38:31 +0800 Subject: [PATCH 393/530] chore: fix some problematic function names Signed-off-by: hishope --- bchain/basechain.go | 2 +- bchain/coins/blockchain.go | 2 +- bchain/coins/eth/contract.go | 2 +- db/rocksdb_ethereumtype.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bchain/basechain.go b/bchain/basechain.go index e6da4281ae..5618c1d77b 100644 --- a/bchain/basechain.go +++ b/bchain/basechain.go @@ -64,7 +64,7 @@ func (b *BaseChain) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc A return nil, errors.New("not supported") } -// GetContractInfo returns URI of non fungible or multi token defined by token id +// GetTokenURI returns URI of non fungible or multi token defined by token id func (p *BaseChain) GetTokenURI(contractDesc AddressDescriptor, tokenID *big.Int) (string, error) { return "", errors.New("not supported") } diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 77e7206ab7..e694dafca8 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -333,7 +333,7 @@ func (c *blockChainWithMetrics) EthereumTypeGetErc20ContractBalance(addrDesc, co return c.b.EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc) } -// GetContractInfo returns URI of non fungible or multi token defined by token id +// GetTokenURI returns URI of non fungible or multi token defined by token id func (c *blockChainWithMetrics) GetTokenURI(contractDesc bchain.AddressDescriptor, tokenID *big.Int) (v string, err error) { defer func(s time.Time) { c.observeRPCLatency("GetTokenURI", s, err) }(time.Now()) return c.b.GetTokenURI(contractDesc, tokenID) diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go index efc7439a7c..6dbca33e3a 100644 --- a/bchain/coins/eth/contract.go +++ b/bchain/coins/eth/contract.go @@ -356,7 +356,7 @@ func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc return r, nil } -// GetContractInfo returns URI of non fungible or multi token defined by token id +// GetTokenURI returns URI of non fungible or multi token defined by token id func (b *EthereumRPC) GetTokenURI(contractDesc bchain.AddressDescriptor, tokenID *big.Int) (string, error) { address := hexutil.Encode(contractDesc) // CryptoKitties do not fully support ERC721 standard, do not have tokenURI method diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index 29bff516cd..11371a7914 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -124,7 +124,7 @@ type AddrContracts struct { Contracts []AddrContract } -// packAddrContract packs AddrContracts into a byte buffer +// packAddrContracts packs AddrContracts into a byte buffer func packAddrContracts(acs *AddrContracts) []byte { buf := make([]byte, 0, 128) varBuf := make([]byte, maxPackedBigintBytes) From 144a369f922e56fce56db954424fbfcd4e701c6c Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 9 Dec 2024 21:40:00 +0100 Subject: [PATCH 394/530] Fix Ethereum SENSO contract decimals #1026 --- configs/contract-fix/ethereum.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 configs/contract-fix/ethereum.json diff --git a/configs/contract-fix/ethereum.json b/configs/contract-fix/ethereum.json new file mode 100644 index 0000000000..5b6dc03efc --- /dev/null +++ b/configs/contract-fix/ethereum.json @@ -0,0 +1 @@ +[{"type":"ERC20","contract":"0xC19B6A4Ac7C7Cc24459F08984Bbd09664af17bD1","name":"Sensorium","symbol":"SENSO","decimals":0,"createdInBlock":11098997}] From 6f4a6aa8e3252457d28ff457f952f0a41255f9db Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 9 Dec 2024 23:10:47 +0100 Subject: [PATCH 395/530] Remove Coingecko URL from configs to enable support of API key --- configs/coins/arbitrum.json | 2 +- configs/coins/arbitrum_archive.json | 2 +- configs/coins/arbitrum_nova.json | 124 +++++++++++----------- configs/coins/arbitrum_nova_archive.json | 128 +++++++++++------------ configs/coins/optimism.json | 2 +- configs/coins/optimism_archive.json | 2 +- configs/coins/polygon.json | 2 +- configs/coins/polygon_archive.json | 2 +- 8 files changed, 132 insertions(+), 132 deletions(-) diff --git a/configs/coins/arbitrum.json b/configs/coins/arbitrum.json index b1f0171193..d4a4ff4993 100644 --- a/configs/coins/arbitrum.json +++ b/configs/coins/arbitrum.json @@ -55,7 +55,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" } } }, diff --git a/configs/coins/arbitrum_archive.json b/configs/coins/arbitrum_archive.json index f01d6c6730..4d5bfb069e 100644 --- a/configs/coins/arbitrum_archive.json +++ b/configs/coins/arbitrum_archive.json @@ -56,7 +56,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } diff --git a/configs/coins/arbitrum_nova.json b/configs/coins/arbitrum_nova.json index 0d0a252b2e..55d20d7d13 100644 --- a/configs/coins/arbitrum_nova.json +++ b/configs/coins/arbitrum_nova.json @@ -1,65 +1,65 @@ { - "coin": { - "name": "Arbitrum Nova", - "shortcut": "ETH", - "label": "Arbitrum Nova", - "alias": "arbitrum_nova" - }, - "ports": { - "backend_rpc": 8207, - "backend_p2p": 38407, - "backend_http": 8307, - "blockbook_internal": 9207, - "blockbook_public": 9307 - }, - "ipc": { - "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", - "rpc_timeout": 25 - }, - "backend": { - "package_name": "backend-arbitrum-nova", - "package_revision": "satoshilabs-1", - "system_user": "arbitrum", - "version": "3.2.1", - "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", - "verification_type": "docker", - "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", - "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", - "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_nova_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", - "exec_script": "arbitrum_nova.sh", - "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", - "service_type": "simple", - "service_additional_params_template": "", - "protect_memory": true, - "mainnet": true, - "server_config_file": "", - "client_config_file": "" - }, - "blockbook": { - "package_name": "blockbook-arbitrum-nova", - "system_user": "blockbook-arbitrum", - "internal_binding_template": ":{{.Ports.BlockbookInternal}}", - "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "", - "additional_params": "", - "block_chain": { - "parse": true, - "mempool_workers": 8, - "mempool_sub_workers": 2, - "block_addresses_to_keep": 300, - "additional_params": { - "mempoolTxTimeoutHours": 48, - "queryBackendOnMempoolResync": false, - "fiat_rates": "coingecko", - "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" - } + "coin": { + "name": "Arbitrum Nova", + "shortcut": "ETH", + "label": "Arbitrum Nova", + "alias": "arbitrum_nova" + }, + "ports": { + "backend_rpc": 8207, + "backend_p2p": 38407, + "backend_http": 8307, + "blockbook_internal": 9207, + "blockbook_public": 9307 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-arbitrum-nova", + "package_revision": "satoshilabs-1", + "system_user": "arbitrum", + "version": "3.2.1", + "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", + "verification_type": "docker", + "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", + "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_nova_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "arbitrum_nova.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-arbitrum-nova", + "system_user": "blockbook-arbitrum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "additional_params": { + "mempoolTxTimeoutHours": 48, + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" } - }, - "meta": { - "package_maintainer": "IT", - "package_maintainer_email": "it@satoshilabs.com" - } } diff --git a/configs/coins/arbitrum_nova_archive.json b/configs/coins/arbitrum_nova_archive.json index 0510597649..d0833e4536 100644 --- a/configs/coins/arbitrum_nova_archive.json +++ b/configs/coins/arbitrum_nova_archive.json @@ -1,67 +1,67 @@ { - "coin": { - "name": "Arbitrum Nova Archive", - "shortcut": "ETH", - "label": "Arbitrum Nova", - "alias": "arbitrum_nova_archive" - }, - "ports": { - "backend_rpc": 8308, - "backend_p2p": 38408, - "blockbook_internal": 9208, - "blockbook_public": 9308 - }, - "ipc": { - "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", - "rpc_timeout": 25 - }, - "backend": { - "package_name": "backend-arbitrum-nova-archive", - "package_revision": "satoshilabs-1", - "system_user": "arbitrum", - "version": "3.2.1", - "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", - "verification_type": "docker", - "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", - "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", - "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_nova_archive_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", - "exec_script": "arbitrum_nova_archive.sh", - "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", - "service_type": "simple", - "service_additional_params_template": "", - "protect_memory": true, - "mainnet": true, - "server_config_file": "", - "client_config_file": "" - }, - "blockbook": { - "package_name": "blockbook-arbitrum-nova-archive", - "system_user": "blockbook-arbitrum", - "internal_binding_template": ":{{.Ports.BlockbookInternal}}", - "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "", - "additional_params": "-workers=16", - "block_chain": { - "parse": true, - "mempool_workers": 8, - "mempool_sub_workers": 2, - "block_addresses_to_keep": 600, - "additional_params": { - "address_aliases": true, - "mempoolTxTimeoutHours": 48, - "processInternalTransactions": true, - "queryBackendOnMempoolResync": false, - "fiat_rates": "coingecko", - "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", - "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" - } + "coin": { + "name": "Arbitrum Nova Archive", + "shortcut": "ETH", + "label": "Arbitrum Nova", + "alias": "arbitrum_nova_archive" + }, + "ports": { + "backend_rpc": 8308, + "backend_p2p": 38408, + "blockbook_internal": 9208, + "blockbook_public": 9308 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-arbitrum-nova-archive", + "package_revision": "satoshilabs-1", + "system_user": "arbitrum", + "version": "3.2.1", + "docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d", + "verification_type": "docker", + "verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306", + "extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_nova_archive_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "arbitrum_nova_archive.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-arbitrum-nova-archive", + "system_user": "blockbook-arbitrum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "-workers=16", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 600, + "additional_params": { + "address_aliases": true, + "mempoolTxTimeoutHours": 48, + "processInternalTransactions": true, + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" } - }, - "meta": { - "package_maintainer": "IT", - "package_maintainer_email": "it@satoshilabs.com" - } } diff --git a/configs/coins/optimism.json b/configs/coins/optimism.json index 07ab65ec50..af77dbdb05 100644 --- a/configs/coins/optimism.json +++ b/configs/coins/optimism.json @@ -56,7 +56,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" } } }, diff --git a/configs/coins/optimism_archive.json b/configs/coins/optimism_archive.json index 46967e5e18..3c54d7bc91 100644 --- a/configs/coins/optimism_archive.json +++ b/configs/coins/optimism_archive.json @@ -58,7 +58,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } diff --git a/configs/coins/polygon.json b/configs/coins/polygon.json index 3a71d10125..19367240c4 100644 --- a/configs/coins/polygon.json +++ b/configs/coins/polygon.json @@ -55,7 +55,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"matic-network\",\"platformIdentifier\": \"polygon-pos\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}" + "fiat_rates_params": "{\"coin\": \"matic-network\",\"platformIdentifier\": \"polygon-pos\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}" } } }, diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index 817d22bf39..d7a1744dc6 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -57,7 +57,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"matic-network\",\"platformIdentifier\": \"polygon-pos\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}", + "fiat_rates_params": "{\"coin\": \"matic-network\",\"platformIdentifier\": \"polygon-pos\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}", "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } From 22fe25b9cf427b74e2410d2fc6e1669b2251cd7a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 10 Dec 2024 00:32:15 +0100 Subject: [PATCH 396/530] Fix Arbitrum and Optimism fiat rates ids --- configs/coins/arbitrum.json | 2 +- configs/coins/arbitrum_archive.json | 2 +- configs/coins/optimism.json | 2 +- configs/coins/optimism_archive.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/coins/arbitrum.json b/configs/coins/arbitrum.json index d4a4ff4993..5c3f701d4a 100644 --- a/configs/coins/arbitrum.json +++ b/configs/coins/arbitrum.json @@ -55,7 +55,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" + "fiat_rates_params": "{\"coin\": \"arbitrum\",\"platformIdentifier\": \"arbitrum-one\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}" } } }, diff --git a/configs/coins/arbitrum_archive.json b/configs/coins/arbitrum_archive.json index 4d5bfb069e..6d704bdf14 100644 --- a/configs/coins/arbitrum_archive.json +++ b/configs/coins/arbitrum_archive.json @@ -56,7 +56,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fiat_rates_params": "{\"coin\": \"arbitrum\",\"platformIdentifier\": \"arbitrum-one\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}", "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } diff --git a/configs/coins/optimism.json b/configs/coins/optimism.json index af77dbdb05..132cc77c8c 100644 --- a/configs/coins/optimism.json +++ b/configs/coins/optimism.json @@ -56,7 +56,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" + "fiat_rates_params": "{\"coin\": \"optimism\",\"platformIdentifier\": \"optimistic-ethereum\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}" } } }, diff --git a/configs/coins/optimism_archive.json b/configs/coins/optimism_archive.json index 3c54d7bc91..25c330c3ad 100644 --- a/configs/coins/optimism_archive.json +++ b/configs/coins/optimism_archive.json @@ -58,7 +58,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fiat_rates_params": "{\"coin\": \"optimism\",\"platformIdentifier\": \"optimistic-ethereum\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}", "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } From bdc477050b6f0aec480433ced3fc02995fef0f90 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 10 Dec 2024 01:02:05 +0100 Subject: [PATCH 397/530] Fix Arbitrum and Optimism fiat rates ids --- configs/coins/arbitrum.json | 2 +- configs/coins/arbitrum_archive.json | 2 +- configs/coins/optimism.json | 2 +- configs/coins/optimism_archive.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/coins/arbitrum.json b/configs/coins/arbitrum.json index 5c3f701d4a..26cf935100 100644 --- a/configs/coins/arbitrum.json +++ b/configs/coins/arbitrum.json @@ -55,7 +55,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"coin\": \"arbitrum\",\"platformIdentifier\": \"arbitrum-one\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}" + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"arbitrum-one\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" } } }, diff --git a/configs/coins/arbitrum_archive.json b/configs/coins/arbitrum_archive.json index 6d704bdf14..c85bb4cb5f 100644 --- a/configs/coins/arbitrum_archive.json +++ b/configs/coins/arbitrum_archive.json @@ -56,7 +56,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"coin\": \"arbitrum\",\"platformIdentifier\": \"arbitrum-one\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}", + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"arbitrum-one\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } diff --git a/configs/coins/optimism.json b/configs/coins/optimism.json index 132cc77c8c..bc7cc8868f 100644 --- a/configs/coins/optimism.json +++ b/configs/coins/optimism.json @@ -56,7 +56,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"coin\": \"optimism\",\"platformIdentifier\": \"optimistic-ethereum\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}" + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"optimistic-ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" } } }, diff --git a/configs/coins/optimism_archive.json b/configs/coins/optimism_archive.json index 25c330c3ad..1a642f11d5 100644 --- a/configs/coins/optimism_archive.json +++ b/configs/coins/optimism_archive.json @@ -58,7 +58,7 @@ "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", - "fiat_rates_params": "{\"coin\": \"optimism\",\"platformIdentifier\": \"optimistic-ethereum\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 900}", + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"optimistic-ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" } } From d771291ecab45bb86712ca7da1a238f6bdb9b11c Mon Sep 17 00:00:00 2001 From: JoHnY Date: Fri, 13 Dec 2024 08:37:10 +0000 Subject: [PATCH 398/530] =?UTF-8?q?dash=20(+testnet)=2020.1.0=20=E2=86=92?= =?UTF-8?q?=2022.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/dash.json | 6 +++--- configs/coins/dash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/dash.json b/configs/coins/dash.json index b77e673256..255325ef44 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": "20.1.1", - "binary_url": "https://github.com/dashpay/dash/releases/download/v20.1.1/dashcore-20.1.1-x86_64-linux-gnu.tar.gz", + "version": "22.0.0", + "binary_url": "https://github.com/dashpay/dash/releases/download/v22.0.0/dashcore-22.0.0-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v20.1.1/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v22.0.0/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/dash-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/dashd -deprecatedrpc=estimatefee -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/dash_testnet.json b/configs/coins/dash_testnet.json index 86925e0a64..081381a6f6 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": "20.1.1", - "binary_url": "https://github.com/dashpay/dash/releases/download/v20.1.1/dashcore-20.1.1-x86_64-linux-gnu.tar.gz", + "version": "22.0.0", + "binary_url": "https://github.com/dashpay/dash/releases/download/v22.0.0/dashcore-22.0.0-x86_64-linux-gnu.tar.gz", "verification_type": "gpg-sha256", - "verification_source": "https://github.com/dashpay/dash/releases/download/v20.1.1/SHA256SUMS.asc", + "verification_source": "https://github.com/dashpay/dash/releases/download/v22.0.0/SHA256SUMS.asc", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/dash-qt" From 4c5c0bd32f86b42718fbdcde8c9c60ea93078048 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 24 Sep 2024 18:19:05 +0200 Subject: [PATCH 399/530] Add option to disable sync of mempool transactions --- bchain/coins/eth/ethrpc.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 6ed4b14444..67b9a28931 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -50,6 +50,7 @@ type Configuration struct { ProcessInternalTransactions bool `json:"processInternalTransactions"` ProcessZeroInternalTransactions bool `json:"processZeroInternalTransactions"` ConsensusNodeVersionURL string `json:"consensusNodeVersion"` + DisableMempoolSync bool `json:"disableMempoolSync,omitempty"` } // EthereumRPC is an interface to JSON-RPC eth service. @@ -174,7 +175,7 @@ func (b *EthereumRPC) Initialize() error { func (b *EthereumRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) { if b.Mempool == nil { b.Mempool = bchain.NewMempoolEthereumType(chain, b.ChainConfig.MempoolTxTimeoutHours, b.ChainConfig.QueryBackendOnMempoolResync) - glog.Info("mempool created, MempoolTxTimeoutHours=", b.ChainConfig.MempoolTxTimeoutHours, ", QueryBackendOnMempoolResync=", b.ChainConfig.QueryBackendOnMempoolResync) + glog.Info("mempool created, MempoolTxTimeoutHours=", b.ChainConfig.MempoolTxTimeoutHours, ", QueryBackendOnMempoolResync=", b.ChainConfig.QueryBackendOnMempoolResync, ", DisableMempoolSync=", b.ChainConfig.DisableMempoolSync) } return b.Mempool, nil } @@ -263,21 +264,23 @@ func (b *EthereumRPC) subscribeEvents() error { } }() - // new mempool transaction subscription - if err := b.subscribe(func() (bchain.EVMClientSubscription, error) { - // invalidate the previous subscription - it is either the first one or there was an error - b.newTxSubscription = nil - ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) - defer cancel() - sub, err := b.RPC.EthSubscribe(ctx, b.NewTx.Channel(), "newPendingTransactions") - if err != nil { - return nil, errors.Annotatef(err, "EthSubscribe newPendingTransactions") + if !b.ChainConfig.DisableMempoolSync { + // new mempool transaction subscription + if err := b.subscribe(func() (bchain.EVMClientSubscription, error) { + // invalidate the previous subscription - it is either the first one or there was an error + b.newTxSubscription = nil + ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) + defer cancel() + sub, err := b.RPC.EthSubscribe(ctx, b.NewTx.Channel(), "newPendingTransactions") + if err != nil { + return nil, errors.Annotatef(err, "EthSubscribe newPendingTransactions") + } + b.newTxSubscription = sub + glog.Info("Subscribed to newPendingTransactions") + return sub, nil + }); err != nil { + return err } - b.newTxSubscription = sub - glog.Info("Subscribed to newPendingTransactions") - return sub, nil - }); err != nil { - return err } return nil From 8b05dbc9b935d106bf05a879dc8b505566fcfe8e Mon Sep 17 00:00:00 2001 From: JoHnY Date: Wed, 11 Dec 2024 14:27:42 +0000 Subject: [PATCH 400/530] =?UTF-8?q?polygon-bor=201.4.1=20=E2=86=92=201.5.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/polygon.json | 10 +++++----- configs/coins/polygon_archive.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/configs/coins/polygon.json b/configs/coins/polygon.json index 19367240c4..71e0c14387 100644 --- a/configs/coins/polygon.json +++ b/configs/coins/polygon.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.4.1", - "binary_url": "https://github.com/maticnetwork/bor/archive/refs/tags/v1.4.1.tar.gz", + "version": "1.5.3", + "binary_url": "https://github.com/maticnetwork/bor/archive/refs/tags/v1.5.3.tar.gz", "verification_type": "sha256", - "verification_source": "59fd114c572f81ccdd7ceb5ee6ece0ca03341b5bba294bceb34f9b572d7ef1e9", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.4.1.tar.gz && cd backend/source && make bor && mv build/bin/bor ../ && rm -rf ../source && echo", + "verification_source": "6dabc3306aa628f86232e96e5ec1a970bbebe38ace09447a0d2e5421dd77e4bd", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.5.3.tar.gz && cd backend/source && make bor && mv build/bin/bor ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v1.4.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v1.5.3/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index d7a1744dc6..6898e89cc5 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-archive-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.4.1", - "binary_url": "https://github.com/maticnetwork/bor/archive/refs/tags/v1.4.1.tar.gz", + "version": "1.5.3", + "binary_url": "https://github.com/maticnetwork/bor/archive/refs/tags/v1.5.3.tar.gz", "verification_type": "sha256", - "verification_source": "59fd114c572f81ccdd7ceb5ee6ece0ca03341b5bba294bceb34f9b572d7ef1e9", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.4.1.tar.gz && cd backend/source && make bor && mv build/bin/bor ../ && rm -rf ../source && echo", + "verification_source": "6dabc3306aa628f86232e96e5ec1a970bbebe38ace09447a0d2e5421dd77e4bd", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.5.3.tar.gz && cd backend/source && make bor && mv build/bin/bor ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v1.4.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v1.5.3/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, From fab4dd78caea6145ffe4db6314fdd6d704166d33 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Mon, 30 Dec 2024 14:01:53 +0000 Subject: [PATCH 401/530] =?UTF-8?q?bch=20(+testnets)=2027.1.0=20=E2=86=92?= =?UTF-8?q?=2028.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/bcash.json | 6 +++--- configs/coins/bcash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/bcash.json b/configs/coins/bcash.json index daac347bd8..1a6a4e5d4b 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": "27.1.0", - "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v27.1.0/bitcoin-cash-node-27.1.0-x86_64-linux-gnu.tar.gz", + "version": "28.0.1", + "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v28.0.1/bitcoin-cash-node-28.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "0dcc387cbaa3a039c97ddc8fb99c1fa7bff5dc6e4bd3a01d3c3095f595ad2dce", + "verification_source": "d69ee632147f886ca540cecdff5b1b85512612b4c005e86b09083a63c35b64fa", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -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/bcash_testnet.json b/configs/coins/bcash_testnet.json index 89c08f099b..fb98530cee 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": "27.1.0", - "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v27.1.0/bitcoin-cash-node-27.1.0-x86_64-linux-gnu.tar.gz", + "version": "28.0.1", + "binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v28.0.1/bitcoin-cash-node-28.0.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "0dcc387cbaa3a039c97ddc8fb99c1fa7bff5dc6e4bd3a01d3c3095f595ad2dce", + "verification_source": "d69ee632147f886ca540cecdff5b1b85512612b4c005e86b09083a63c35b64fa", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", From 40be3e7219d3bb74a5996f70d91e9d8fb17b5782 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 17 Dec 2024 14:44:47 +0000 Subject: [PATCH 402/530] =?UTF-8?q?prysm=205.1.2=20=E2=86=92=205.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum_archive_consensus.json | 10 +++++----- configs/coins/ethereum_consensus.json | 10 +++++----- .../ethereum_testnet_holesky_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_consensus.json | 10 +++++----- .../ethereum_testnet_sepolia_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_consensus.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 4caa570a98..51f757ad53 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", + "version": "5.2.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", "verification_type": "sha256", - "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", + "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", - "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", + "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" } } }, diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 9ec1e5a291..c6213955ba 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", + "version": "5.2.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", "verification_type": "sha256", - "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", + "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", - "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", + "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive_consensus.json b/configs/coins/ethereum_testnet_holesky_archive_consensus.json index 88859fd74f..848775b296 100644 --- a/configs/coins/ethereum_testnet_holesky_archive_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", + "version": "5.2.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", "verification_type": "sha256", - "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", + "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13636 --p2p-udp-port=12636 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", - "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", + "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_consensus.json b/configs/coins/ethereum_testnet_holesky_consensus.json index 19774f19b0..69583957bd 100644 --- a/configs/coins/ethereum_testnet_holesky_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", + "version": "5.2.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", "verification_type": "sha256", - "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", + "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", - "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", + "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json index 0bdbe87b11..8563a8e943 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", + "version": "5.2.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", "verification_type": "sha256", - "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", + "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", - "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", + "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json index 9ca379efd0..1a700fe70d 100644 --- a/configs/coins/ethereum_testnet_sepolia_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.1.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-amd64", + "version": "5.2.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", "verification_type": "sha256", - "verification_source": "4b0d20406aebec8e19016cddf987bf92578296dac0e41c40d99223c8166b96b9", + "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.1.2/beacon-chain-v5.1.2-linux-arm64", - "verification_source": "850d834fca3b00b3f4c508ecb96fd867aff476335ebdcd190981446a93d03e3d" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", + "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" } } }, From 1334a16d3ae092e60c7bc2bf6e8d6840f89c4139 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Mon, 6 Jan 2025 13:47:54 +0000 Subject: [PATCH 403/530] =?UTF-8?q?zcash=20(+testnet)=206.0.0=20=E2=86=92?= =?UTF-8?q?=206.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/zcash.json | 6 +++--- configs/coins/zcash_testnet.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index f1be6baf9d..be6407832f 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,10 +22,10 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "6.0.0", - "binary_url": "https://download.z.cash/downloads/zcash-6.0.0-linux64-debian-bullseye.tar.gz", + "version": "6.1.0", + "binary_url": "https://download.z.cash/downloads/zcash-6.1.0-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "3cb82f490e9c8e88007a0216b5261b33ef0fda962b9258441b2def59cb272a4d", + "verification_source": "1d17ceacb265599bb4ee690baaf2b335cfe9825df5198359c771ee1834fd4358", "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 0a185bbaad..f2a1c388b2 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -21,10 +21,10 @@ "backend": { "package_name": "backend-zcash-testnet", "package_revision": "satoshilabs-1", - "version": "6.0.0", - "binary_url": "https://download.z.cash/downloads/zcash-6.0.0-linux64-debian-bullseye.tar.gz", + "version": "6.1.0", + "binary_url": "https://download.z.cash/downloads/zcash-6.1.0-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "3cb82f490e9c8e88007a0216b5261b33ef0fda962b9258441b2def59cb272a4d", + "verification_source": "1d17ceacb265599bb4ee690baaf2b335cfe9825df5198359c771ee1834fd4358", "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 b40948a7815ca721c154b26bcb3294e973f1805d Mon Sep 17 00:00:00 2001 From: Tadeas Kmenta Date: Wed, 8 Jan 2025 04:15:37 +0800 Subject: [PATCH 404/530] update flux daemon to v7.2.0 (#1178) Co-authored-by: Tadeas Kmenta --- configs/coins/flux.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/flux.json b/configs/coins/flux.json index 24116c44bb..ec85012425 100644 --- a/configs/coins/flux.json +++ b/configs/coins/flux.json @@ -22,10 +22,10 @@ "package_name": "backend-flux", "package_revision": "satoshilabs-1", "system_user": "flux", - "version": "7.1.0", - "binary_url": "https://github.com/RunOnFlux/fluxd/releases/download/v7.1.0/Flux-amd64-v7.1.0.tar.gz", + "version": "7.2.0", + "binary_url": "https://github.com/RunOnFlux/fluxd/releases/download/v7.2.0/Flux-amd64-v7.2.0.tar.gz", "verification_type": "sha256", - "verification_source": "832fe0d7700cf74430f4b464f07706a78ec39b2ec309d3d8230b0dffe9993296", + "verification_source": "aac3a9581fb8e8f3215ddd3de9721fdb6e9d90ef65d3fa73a495d7451dd480ef", "extract_command": "tar -C backend -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/fluxd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", From 70f34cedeb49624ad9dd55217536912f308653b3 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 12 Jan 2025 18:18:38 +0100 Subject: [PATCH 405/530] Use network specific ens suffix --- bchain/coins/bsc/bscrpc.go | 1 + bchain/coins/eth/ethparser.go | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/bchain/coins/bsc/bscrpc.go b/bchain/coins/bsc/bscrpc.go index 383f7eb51b..a1fb649cd8 100644 --- a/bchain/coins/bsc/bscrpc.go +++ b/bchain/coins/bsc/bscrpc.go @@ -38,6 +38,7 @@ func NewBNBSmartChainRPC(config json.RawMessage, pushHandler func(bchain.Notific s := &BNBSmartChainRPC{ EthereumRPC: c.(*eth.EthereumRPC), } + s.Parser.EnsSuffix = ".bnb" return s, nil } diff --git a/bchain/coins/eth/ethparser.go b/bchain/coins/eth/ethparser.go index 161c14ad18..73f58b621a 100644 --- a/bchain/coins/eth/ethparser.go +++ b/bchain/coins/eth/ethparser.go @@ -25,15 +25,19 @@ const EtherAmountDecimalPoint = 18 // EthereumParser handle type EthereumParser struct { *bchain.BaseParser + EnsSuffix string } // NewEthereumParser returns new EthereumParser instance func NewEthereumParser(b int, addressAliases bool) *EthereumParser { - return &EthereumParser{&bchain.BaseParser{ - BlockAddressesToKeep: b, - AmountDecimalPoint: EtherAmountDecimalPoint, - AddressAliases: addressAliases, - }} + return &EthereumParser{ + BaseParser: &bchain.BaseParser{ + BlockAddressesToKeep: b, + AmountDecimalPoint: EtherAmountDecimalPoint, + AddressAliases: addressAliases, + }, + EnsSuffix: ".eth", + } } type rpcHeader struct { @@ -489,7 +493,7 @@ func (p *EthereumParser) EthereumTypeGetTokenTransfersFromTx(tx *bchain.Tx) (bch // FormatAddressAlias adds .eth to a name alias func (p *EthereumParser) FormatAddressAlias(address string, name string) string { - return name + ".eth" + return name + p.EnsSuffix } // TxStatus is status of transaction From 4a7fdb509569a31ada721801d1ff57ad88afa553 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Mon, 6 Jan 2025 13:46:14 -0700 Subject: [PATCH 406/530] Avalanche Etna Upgrade --- bchain/coins/avalanche/avalancherpc.go | 1 - build/docker/bin/Dockerfile | 2 +- configs/coins/avalanche.json | 10 +- configs/coins/avalanche_archive.json | 10 +- docs/build.md | 2 +- go.mod | 79 ++-- go.sum | 552 ++++--------------------- 7 files changed, 132 insertions(+), 524 deletions(-) diff --git a/bchain/coins/avalanche/avalancherpc.go b/bchain/coins/avalanche/avalancherpc.go index 8e102529f6..1d12623709 100644 --- a/bchain/coins/avalanche/avalancherpc.go +++ b/bchain/coins/avalanche/avalancherpc.go @@ -110,7 +110,6 @@ func (b *AvalancheRPC) Initialize() error { func (b *AvalancheRPC) GetChainInfo() (*bchain.ChainInfo, error) { ci, err := b.EthereumRPC.GetChainInfo() if err != nil { - fmt.Println(err) return nil, err } diff --git a/build/docker/bin/Dockerfile b/build/docker/bin/Dockerfile index 8930bdcc75..f56d828b31 100644 --- a/build/docker/bin/Dockerfile +++ b/build/docker/bin/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update && \ libzstd-dev liblz4-dev graphviz && \ apt-get clean ARG GOLANG_VERSION -ENV GOLANG_VERSION=go1.22.2 +ENV GOLANG_VERSION=go1.22.8 ENV ROCKSDB_VERSION=v7.7.2 ENV GOPATH=/go ENV PATH=$PATH:$GOPATH/bin diff --git a/configs/coins/avalanche.json b/configs/coins/avalanche.json index 707d90c559..4514b030b0 100644 --- a/configs/coins/avalanche.json +++ b/configs/coins/avalanche.json @@ -19,10 +19,10 @@ "package_name": "backend-avalanche", "package_revision": "satoshilabs-1", "system_user": "avalanche", - "version": "1.9.11", - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz", + "version": "1.12.1", + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz", "verification_type": "gpg", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz.sig", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz.sig", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMEtmUT09IgogIH0KfQ==", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz.sig" + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz.sig" } } }, diff --git a/configs/coins/avalanche_archive.json b/configs/coins/avalanche_archive.json index cbc1b615aa..10ed9113f0 100644 --- a/configs/coins/avalanche_archive.json +++ b/configs/coins/avalanche_archive.json @@ -19,10 +19,10 @@ "package_name": "backend-avalanche-archive", "package_revision": "satoshilabs-1", "system_user": "avalanche", - "version": "1.9.11", - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz", + "version": "1.12.1", + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz", "verification_type": "gpg", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz.sig", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz.sig", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMHNDaUFnSW5CeWRXNXBibWN0Wlc1aFlteGxaQ0k2Wm1Gc2MyVUtmUT09IgogIH0KfQ==", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz.sig" + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz.sig" } } }, diff --git a/docs/build.md b/docs/build.md index 0fbed91471..e9f0b14c72 100644 --- a/docs/build.md +++ b/docs/build.md @@ -191,7 +191,7 @@ like macOS or Windows, please adapt the instructions to your target system. Setup go environment (use newer version of go as available) ``` -wget https://golang.org/dl/go1.21.4.linux-amd64.tar.gz && tar xf go1.21.4.linux-amd64.tar.gz +wget https://golang.org/dl/go1.22.8.linux-amd64.tar.gz && tar xf go1.22.8.linux-amd64.tar.gz sudo mv go /opt/go sudo ln -s /opt/go/bin/go /usr/bin/go # see `go help gopath` for details diff --git a/go.mod b/go.mod index 776e943ca1..ecc47fc67b 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/trezor/blockbook -go 1.21 +go 1.22.8 require ( - github.com/ava-labs/avalanchego v1.10.18-rc.0 - github.com/ava-labs/coreth v0.12.10-rc.0 + github.com/ava-labs/avalanchego v1.12.1 + github.com/ava-labs/coreth v0.14.0 github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e github.com/deckarep/golang-set v1.8.0 github.com/decred/dcrd/chaincfg/chainhash v1.0.2 @@ -15,9 +15,8 @@ require ( github.com/decred/dcrd/hdkeychain/v3 v3.0.0 github.com/decred/dcrd/txscript/v3 v3.0.0 github.com/ethereum/go-ethereum v1.13.14 - github.com/golang/glog v1.1.0 - github.com/golang/protobuf v1.5.3 - github.com/gorilla/websocket v1.4.2 + github.com/golang/glog v1.2.1 + github.com/gorilla/websocket v1.5.0 github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 github.com/linxGnu/grocksdb v1.7.7 github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe @@ -27,14 +26,15 @@ require ( github.com/pebbe/zmq4 v1.2.1 github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29 github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e - github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/client_golang v1.16.0 github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a github.com/tkrajina/typescriptify-golang-structs v0.1.11 - golang.org/x/crypto v0.17.0 - google.golang.org/protobuf v1.31.0 + golang.org/x/crypto v0.31.0 + google.golang.org/protobuf v1.34.2 ) require ( + github.com/BurntSushi/toml v1.4.0 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c // indirect github.com/Microsoft/go-winio v0.6.1 // indirect @@ -46,8 +46,8 @@ require ( github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect @@ -65,15 +65,15 @@ require ( github.com/decred/slog v1.1.0 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/renameio/v2 v2.0.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/rpc v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0 // indirect - github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/holiman/uint256 v1.2.4 // indirect github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect @@ -84,40 +84,39 @@ require ( github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.39.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/stretchr/testify v1.8.4 // indirect - github.com/supranational/blst v0.3.11 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/supranational/blst v0.3.13 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/tkrajina/go-reflector v0.5.5 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opentelemetry.io/otel v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0 // indirect - go.opentelemetry.io/otel/sdk v1.11.0 // indirect - go.opentelemetry.io/otel/trace v1.11.0 // indirect - go.opentelemetry.io/proto/otlp v0.19.0 // indirect - go.uber.org/mock v0.2.0 // indirect - go.uber.org/multierr v1.10.0 // indirect + go.opentelemetry.io/otel v1.22.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect + go.opentelemetry.io/otel/sdk v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.22.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.uber.org/mock v0.4.0 // indirect + go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/sync v0.5.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect - google.golang.org/grpc v1.58.3 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect + google.golang.org/grpc v1.66.0 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 21454236c8..86b6c2c60b 100644 --- a/go.sum +++ b/go.sum @@ -1,47 +1,11 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c h1:8bYNmjELeCj7DEh/dN7zFzkJ0upK3GkbOC/0u1HMQ5s= github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c/go.mod h1:DwgC62sAn4RgH4L+O8REgcE7f0XplHPNeRYFy+ffy1M= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29 h1:B11BryeZQ1LrAzzM0lCpblwleB7SyxPfvN2AsNbyvQc= github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29/go.mod h1:+39XiGr9m9TPY49sG4XIH5CVaRxHGFWT0U4MOY6dy3o= github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= @@ -52,11 +16,10 @@ github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7I github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/ava-labs/avalanchego v1.10.18-rc.0 h1:8tsu5qB/Fp5NFZuJQR48q6wMHGJxGfzvlGxvxdnjg6o= -github.com/ava-labs/avalanchego v1.10.18-rc.0/go.mod h1:ZbZteX1xINA3U31/akSGO/ZrcVAA7V6tDle0ENJ3DPI= -github.com/ava-labs/coreth v0.12.10-rc.0 h1:qmuom7rtH5hc1E3lnqrMFNLFL1TMnEVa/2O8poB1YLU= -github.com/ava-labs/coreth v0.12.10-rc.0/go.mod h1:plFm/xzvWmx1+qJ3JQSTzF8+FdaA2xu7GgY/AdaZDfk= +github.com/ava-labs/avalanchego v1.12.1 h1:NL04K5+gciC2XqGZbDcIu0nuVApEddzc6YyujRBv+u8= +github.com/ava-labs/avalanchego v1.12.1/go.mod h1:xnVvN86jhxndxfS8e0U7v/0woyfx9BhX/feld7XDjDE= +github.com/ava-labs/coreth v0.14.0 h1:zOrgWXp67LBFj9UMcoXn0UTEv7GINyzUPjh9NrF8qm0= +github.com/ava-labs/coreth v0.14.0/go.mod h1:SN79q6EKPd0viuUZMIg0xxZyUfnvSalZRo8HDJR3JHs= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= @@ -78,26 +41,13 @@ github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= @@ -161,13 +111,6 @@ github.com/decred/dcrd/wire v1.4.0 h1:KmSo6eTQIvhXS0fLBQ/l7hG7QLcSJQKSwSyzSqJYDk github.com/decred/dcrd/wire v1.4.0/go.mod h1:WxC/0K+cCAnBh+SKsRjIX9YPgvrjhmE+6pZlel1G7Ro= github.com/decred/slog v1.1.0 h1:uz5ZFfmaexj1rEDgZvzQ7wjGkoSPjw2LCh8K+K1VrW4= github.com/decred/slog v1.1.0/go.mod h1:kVXlGnt6DHy2fV5OjSeuvCJ0OmlmTF6LFpEPMu/fOY0= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.13.14 h1:EwiY3FZP94derMCIam1iW4HFVrSgIcpsu0HwTQtm6CQ= @@ -182,13 +125,9 @@ github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2Gihuqh github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -200,90 +139,34 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0 h1:kr3j8iIMR4ywO/O0rvksXaJvauGGCMg2zAZIiNZ9uIQ= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0/go.mod h1:ummNFgdgLhhX7aIiy35vVmQNS0rWXknfPE0qe6fmFXg= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw= -github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= @@ -294,31 +177,24 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 h1:d2hBkTvi7B89+OXY8+bBBshPlc+7JYacGrG/dFak8SQ= github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 h1:UUHMLvzt/31azWTN/ifGWef4WUqvXk0iRqdhdy/2uzI= github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b h1:Rrp0ByJXEjhREMPGTt3aWYjoIsUGCbt21ekbeJcTWv0= github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc h1:I1QApI4r4SG8Hh45H0yRjVnThWRn1oOwod76rrAe5KE= github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -361,9 +237,8 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= -github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/pebbe/zmq4 v1.2.1 h1:jrXQW3mD8Si2mcSY/8VBs2nNkK/sKCOEM0rHAfxyc8c= github.com/pebbe/zmq4 v1.2.1/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM= github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29 h1:awILOeL107zIYvPB1zhkz6ZTp0AaMpLGMoV16DMairA= @@ -374,21 +249,18 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= -github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -399,18 +271,15 @@ github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a h1:q2+ github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a/go.mod h1:FdhEqBlgflrdbBs+Wh94EXSNJT+s6DTVvsHGMo0+u80= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= -github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= +github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/thepudds/fzgen v0.4.2 h1:HlEHl5hk2/cqEomf2uK5SA/FeJc12s/vIHmOG+FbACw= @@ -429,328 +298,81 @@ github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/otel v1.11.0 h1:kfToEGMDq6TrVrJ9Vht84Y8y9enykSZzDDZglV0kIEk= -go.opentelemetry.io/otel v1.11.0/go.mod h1:H2KtuEphyMvlhZ+F7tg9GRhAOe60moNx61Ex+WmiKkk= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0 h1:0dly5et1i/6Th3WHn0M6kYiJfFNzhhxanrJ0bOfnjEo= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0/go.mod h1:+Lq4/WkdCkjbGcBMVHHg2apTbv8oMBf29QCnyCCJjNQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0 h1:eyJ6njZmH16h9dOKCi7lMswAnGsSOwgTqWzfxqcuNr8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0/go.mod h1:FnDp7XemjN3oZ3xGunnfOUTVwd2XcvLbtRAuOSU3oc8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0 h1:j2RFV0Qdt38XQ2Jvi4WIsQ56w8T7eSirYbMw19VXRDg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0/go.mod h1:pILgiTEtrqvZpoiuGdblDgS5dbIaTgDrkIuKfEFkt+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0 h1:v29I/NbVp7LXQYMFZhU6q17D0jSEbYOAVONlrO1oH5s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0/go.mod h1:/RpLsmbQLDO1XCbWAM4S6TSwj8FKwwgyKKyqtvVfAnw= -go.opentelemetry.io/otel/sdk v1.11.0 h1:ZnKIL9V9Ztaq+ME43IUi/eo22mNsb6a7tGfzaOWB5fo= -go.opentelemetry.io/otel/sdk v1.11.0/go.mod h1:REusa8RsyKaq0OlyangWXaw97t2VogoO4SSEeKkSTAk= -go.opentelemetry.io/otel/trace v1.11.0 h1:20U/Vj42SX+mASlXLmSGBg6jpI1jQtv682lZtTAOVFI= -go.opentelemetry.io/otel/trace v1.11.0/go.mod h1:nyYjis9jy0gytE9LXGU+/m1sHTKbRY0fX0hulNNDP1U= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/mock v0.2.0 h1:TaP3xedm7JaAgScZO7tlvlKrqT0p7I6OsdGB5YNSMDU= -go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= -go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= -golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= -google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw= -google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= @@ -760,22 +382,10 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= From 720b391e940a1a87bb0a3ebb9953a4db168d366f Mon Sep 17 00:00:00 2001 From: JoHnY Date: Thu, 30 Jan 2025 16:43:08 +0100 Subject: [PATCH 407/530] =?UTF-8?q?btc=20(+testnets)=2028.0=20=E2=86=92=20?= =?UTF-8?q?28.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/bitcoin.json | 10 +++++----- configs/coins/bitcoin_regtest.json | 10 +++++----- configs/coins/bitcoin_signet.json | 10 +++++----- configs/coins/bitcoin_testnet.json | 10 +++++----- configs/coins/bitcoin_testnet4.json | 10 +++++----- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index 9e4b09ecbb..4148605c58 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": "28.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz", + "version": "28.1", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc", + "verification_source": "07f77afd326639145b9ba9562912b2ad2ccec47b8a305bd075b4f4cb127b7ed7", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -43,8 +43,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz", - "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-aarch64-linux-gnu.tar.gz", + "verification_source": "6ddb6990690bd4c9a9f4319ed6f6e9c995c85ce5530ee9f120e80ce09e090c44" } } }, diff --git a/configs/coins/bitcoin_regtest.json b/configs/coins/bitcoin_regtest.json index 42f9483367..825dbd8bd1 100644 --- a/configs/coins/bitcoin_regtest.json +++ b/configs/coins/bitcoin_regtest.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-regtest", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "28.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz", + "version": "28.1", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc", + "verification_source": "07f77afd326639145b9ba9562912b2ad2ccec47b8a305bd075b4f4cb127b7ed7", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz", - "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-aarch64-linux-gnu.tar.gz", + "verification_source": "6ddb6990690bd4c9a9f4319ed6f6e9c995c85ce5530ee9f120e80ce09e090c44" } } }, diff --git a/configs/coins/bitcoin_signet.json b/configs/coins/bitcoin_signet.json index c18e71ccbd..58768197fa 100644 --- a/configs/coins/bitcoin_signet.json +++ b/configs/coins/bitcoin_signet.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-signet", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "28.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz", + "version": "28.1", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc", + "verification_source": "07f77afd326639145b9ba9562912b2ad2ccec47b8a305bd075b4f4cb127b7ed7", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz", - "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-aarch64-linux-gnu.tar.gz", + "verification_source": "6ddb6990690bd4c9a9f4319ed6f6e9c995c85ce5530ee9f120e80ce09e090c44" } } }, diff --git a/configs/coins/bitcoin_testnet.json b/configs/coins/bitcoin_testnet.json index dc6048f616..0db14d8934 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": "28.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz", + "version": "28.1", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc", + "verification_source": "07f77afd326639145b9ba9562912b2ad2ccec47b8a305bd075b4f4cb127b7ed7", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz", - "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-aarch64-linux-gnu.tar.gz", + "verification_source": "6ddb6990690bd4c9a9f4319ed6f6e9c995c85ce5530ee9f120e80ce09e090c44" } } }, diff --git a/configs/coins/bitcoin_testnet4.json b/configs/coins/bitcoin_testnet4.json index 75bf73e848..7a80db6fa2 100644 --- a/configs/coins/bitcoin_testnet4.json +++ b/configs/coins/bitcoin_testnet4.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-testnet4", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "28.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz", + "version": "28.1", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc", + "verification_source": "07f77afd326639145b9ba9562912b2ad2ccec47b8a305bd075b4f4cb127b7ed7", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz", - "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-aarch64-linux-gnu.tar.gz", + "verification_source": "6ddb6990690bd4c9a9f4319ed6f6e9c995c85ce5530ee9f120e80ce09e090c44" } } }, From 9d0be099ec59c9a0a8d6eb1e43ec9e3a2d1534d0 Mon Sep 17 00:00:00 2001 From: wakiyamap Date: Sat, 25 Jan 2025 07:24:15 +0900 Subject: [PATCH 408/530] =?UTF-8?q?mona=20(+testnets)=200.20.3=20=E2=86=92?= =?UTF-8?q?=200.20.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/monacoin.json | 8 +- configs/coins/monacoin_testnet.json | 132 ++++++++++++++-------------- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/configs/coins/monacoin.json b/configs/coins/monacoin.json index 361a48bff8..39bbaba0e6 100644 --- a/configs/coins/monacoin.json +++ b/configs/coins/monacoin.json @@ -22,10 +22,10 @@ "package_name": "backend-monacoin", "package_revision": "satoshilabs-1", "system_user": "monacoin", - "version": "0.20.3", - "binary_url": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.3/monacoin-0.20.3-x86_64-linux-gnu.tar.gz", - "verification_type": "gpg-sha256", - "verification_source": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.3/monacoin-0.20.3-signatures.asc", + "version": "0.20.4", + "binary_url": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.4/monacoin-0.20.4-x86_64-linux-gnu.tar.gz", + "verification_type": "sha256", + "verification_source": "94f8fe7400d23a9bad10af3dfc3f800e333be0aa4d61e5c8cfc5f338253d9451", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/monacoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/monacoind -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/monacoin_testnet.json b/configs/coins/monacoin_testnet.json index c867057e7e..46d5826449 100644 --- a/configs/coins/monacoin_testnet.json +++ b/configs/coins/monacoin_testnet.json @@ -1,69 +1,69 @@ { - "coin": { - "name": "Monacoin Testnet", - "shortcut": "TMONA", - "label": "Monacoin Testnet", - "alias": "monacoin_testnet" - }, - "ports": { - "backend_rpc": 18041, - "backend_message_queue": 48341, - "blockbook_internal": 19041, - "blockbook_public": 19141 - }, - "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-monacoin-testnet", - "package_revision": "satoshilabs-1", - "system_user": "monacoin", - "version": "0.20.3", - "binary_url": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.3/monacoin-0.20.3-x86_64-linux-gnu.tar.gz", - "verification_type": "gpg-sha256", - "verification_source": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.3/monacoin-0.20.3-signatures.asc", - "extract_command": "tar -C backend --strip 1 -xf", - "exclude_files": [ - "bin/monacoin-qt" - ], - "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/monacoind -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/testnet4/*.log", - "postinst_script_template": "", - "service_type": "forking", - "service_additional_params_template": "", - "protect_memory": true, - "mainnet": false, - "server_config_file": "bitcoin.conf", - "client_config_file": "bitcoin_like_client.conf", - "additional_params": { - "whitelist": "127.0.0.1" + "coin": { + "name": "Monacoin Testnet", + "shortcut": "TMONA", + "label": "Monacoin Testnet", + "alias": "monacoin_testnet" + }, + "ports": { + "backend_rpc": 18041, + "backend_message_queue": 48341, + "blockbook_internal": 19041, + "blockbook_public": 19141 + }, + "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-monacoin-testnet", + "package_revision": "satoshilabs-1", + "system_user": "monacoin", + "version": "0.20.4", + "binary_url": "https://github.com/monacoinproject/monacoin/releases/download/v0.20.4/monacoin-0.20.4-x86_64-linux-gnu.tar.gz", + "verification_type": "sha256", + "verification_source": "94f8fe7400d23a9bad10af3dfc3f800e333be0aa4d61e5c8cfc5f338253d9451", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": [ + "bin/monacoin-qt" + ], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/monacoind -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/testnet4/*.log", + "postinst_script_template": "", + "service_type": "forking", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "bitcoin.conf", + "client_config_file": "bitcoin_like_client.conf", + "additional_params": { + "whitelist": "127.0.0.1" + } + }, + "blockbook": { + "package_name": "blockbook-monacoin-testnet", + "system_user": "blockbook-monacoin", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "xpub_magic": 70617039, + "xpub_magic_segwit_p2sh": 71979618, + "xpub_magic_segwit_native": 73342198, + "slip44": 1, + "additional_params": {} + } + }, + "meta": { + "package_maintainer": "wakiyamap", + "package_maintainer_email": "wakiyamap@gmail.com" } - }, - "blockbook": { - "package_name": "blockbook-monacoin-testnet", - "system_user": "blockbook-monacoin", - "internal_binding_template": ":{{.Ports.BlockbookInternal}}", - "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "", - "additional_params": "", - "block_chain": { - "parse": true, - "mempool_workers": 8, - "mempool_sub_workers": 2, - "block_addresses_to_keep": 300, - "xpub_magic": 70617039, - "xpub_magic_segwit_p2sh": 71979618, - "xpub_magic_segwit_native": 73342198, - "slip44": 1, - "additional_params": {} - } - }, - "meta": { - "package_maintainer": "wakiyamap", - "package_maintainer_email": "wakiyamap@gmail.com" - } } From 91c3b50b2d89455115b95c66699c239c52737bae Mon Sep 17 00:00:00 2001 From: grdddj Date: Tue, 4 Feb 2025 12:40:32 +0100 Subject: [PATCH 409/530] chore: make testnet4 use mempool.space fees estimation --- configs/coins/bitcoin_testnet4.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/coins/bitcoin_testnet4.json b/configs/coins/bitcoin_testnet4.json index 7a80db6fa2..426829c239 100644 --- a/configs/coins/bitcoin_testnet4.json +++ b/configs/coins/bitcoin_testnet4.json @@ -64,6 +64,8 @@ "xpub_magic_segwit_native": 73342198, "slip44": 1, "additional_params": { + "alternative_estimate_fee": "mempoolspace", + "alternative_estimate_fee_params": "{\"url\": \"https://mempool.space/testnet4/api/v1/fees/recommended\", \"periodSeconds\": 60}", "block_golomb_filter_p": 20, "block_filter_scripts": "taproot-noordinals", "block_filter_use_zeroed_key": true, From af3e98f7479adf5b42f35b78641ad7def25bf9cd Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 6 Feb 2025 22:43:15 +0100 Subject: [PATCH 410/530] Fix slow removal of transactions from mempool --- bchain/basemempool.go | 26 ++++-- bchain/basemempool_test.go | 176 +++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 9 deletions(-) create mode 100644 bchain/basemempool_test.go diff --git a/bchain/basemempool.go b/bchain/basemempool.go index 3ed633c29c..4561ca8898 100644 --- a/bchain/basemempool.go +++ b/bchain/basemempool.go @@ -72,19 +72,27 @@ func (a MempoolTxidEntries) Less(i, j int) bool { // removeEntryFromMempool removes entry from mempool structs. The caller is responsible for locking! func (m *BaseMempool) removeEntryFromMempool(txid string, entry txEntry) { delete(m.txEntries, txid) + // store already processed addrDesc - it can appear multiple times as a different outpoint + processedAddrDesc := make(map[string]struct{}) for _, si := range entry.addrIndexes { outpoints, found := m.addrDescToTx[si.addrDesc] if found { - newOutpoints := make([]Outpoint, 0, len(outpoints)-1) - for _, o := range outpoints { - if o.Txid != txid { - newOutpoints = append(newOutpoints, o) + _, processed := processedAddrDesc[si.addrDesc] + if !processed { + processedAddrDesc[si.addrDesc] = struct{}{} + j := 0 + for i := 0; i < len(outpoints); i++ { + if outpoints[i].Txid != txid { + outpoints[j] = outpoints[i] + j++ + } + } + outpoints = outpoints[:j] + if len(outpoints) > 0 { + m.addrDescToTx[si.addrDesc] = outpoints + } else { + delete(m.addrDescToTx, si.addrDesc) } - } - if len(newOutpoints) > 0 { - m.addrDescToTx[si.addrDesc] = newOutpoints - } else { - delete(m.addrDescToTx, si.addrDesc) } } } diff --git a/bchain/basemempool_test.go b/bchain/basemempool_test.go new file mode 100644 index 0000000000..5842456d1f --- /dev/null +++ b/bchain/basemempool_test.go @@ -0,0 +1,176 @@ +package bchain + +import ( + reflect "reflect" + "strconv" + "testing" +) + +func generateAddIndexes(count int) []addrIndex { + rv := make([]addrIndex, count) + for i := range count { + rv[i] = addrIndex{ + addrDesc: "ad" + strconv.Itoa(i), + } + } + return rv +} + +func generateTxEntries(count int, skipTx int) map[string]txEntry { + rv := make(map[string]txEntry) + for i := range count { + if i != skipTx { + tx := "tx" + strconv.Itoa(i) + rv[tx] = txEntry{ + addrIndexes: generateAddIndexes(count), + } + } + } + return rv +} + +func generateAddrDescToTx(count int, skipTx int) map[string][]Outpoint { + rv := make(map[string][]Outpoint) + for i := range count { + ad := "ad" + strconv.Itoa(i) + op := []Outpoint{} + for j := range count { + if j != skipTx { + tx := "tx" + strconv.Itoa(j) + op = append(op, Outpoint{ + Txid: tx, + }) + } + } + if len(op) > 0 { + rv[ad] = op + } + } + return rv +} + +func TestBaseMempool_removeEntryFromMempool(t *testing.T) { + tests := []struct { + name string + m *BaseMempool + want *BaseMempool + txid string + entry txEntry + }{ + { + name: "test1", + m: &BaseMempool{ + txEntries: map[string]txEntry{ + "tx1": { + addrIndexes: []addrIndex{{addrDesc: "ad1", n: 0}, {addrDesc: "ad1", n: 1}}, + }, + "tx2": { + addrIndexes: []addrIndex{{addrDesc: "ad1"}}, + }, + }, + addrDescToTx: map[string][]Outpoint{ + "ad1": { + {Txid: "tx1", Vout: 0}, + {Txid: "tx1", Vout: 1}, + {Txid: "tx2"}, + }, + }, + }, + want: &BaseMempool{ + txEntries: map[string]txEntry{ + "tx2": { + addrIndexes: []addrIndex{{addrDesc: "ad1"}}, + }, + }, + addrDescToTx: map[string][]Outpoint{ + "ad1": {{Txid: "tx2"}}}, + }, + txid: "tx1", + entry: txEntry{ + addrIndexes: []addrIndex{ + {addrDesc: "ad1"}, + {addrDesc: "ad2"}, + }, + }, + }, + { + name: "test2", + m: &BaseMempool{ + txEntries: map[string]txEntry{ + "tx1": { + addrIndexes: []addrIndex{{addrDesc: "ad1"}, {addrDesc: "ad1", n: 1}}, + }, + }, + addrDescToTx: map[string][]Outpoint{ + "ad1": { + {Txid: "tx1", Vout: 0}, + {Txid: "tx1", Vout: 1}, + }, + }, + }, + want: &BaseMempool{ + txEntries: map[string]txEntry{}, + addrDescToTx: map[string][]Outpoint{}, + }, + txid: "tx1", + entry: txEntry{ + addrIndexes: []addrIndex{ + {addrDesc: "ad1"}, + }, + }, + }, + { + name: "generated1", + m: &BaseMempool{ + txEntries: generateTxEntries(1, -1), + addrDescToTx: generateAddrDescToTx(1, -1), + }, + want: &BaseMempool{ + txEntries: generateTxEntries(1, 0), + addrDescToTx: generateAddrDescToTx(1, 0), + }, + txid: "tx0", + entry: txEntry{ + addrIndexes: generateAddIndexes(1), + }, + }, + { + name: "generated2", + m: &BaseMempool{ + txEntries: generateTxEntries(2, -1), + addrDescToTx: generateAddrDescToTx(2, -1), + }, + want: &BaseMempool{ + txEntries: generateTxEntries(2, 1), + addrDescToTx: generateAddrDescToTx(2, 1), + }, + txid: "tx1", + entry: txEntry{ + addrIndexes: generateAddIndexes(2), + }, + }, + { + name: "generated5000", + m: &BaseMempool{ + txEntries: generateTxEntries(5000, -1), + addrDescToTx: generateAddrDescToTx(5000, -1), + }, + want: &BaseMempool{ + txEntries: generateTxEntries(5000, 2), + addrDescToTx: generateAddrDescToTx(5000, 2), + }, + txid: "tx2", + entry: txEntry{ + addrIndexes: generateAddIndexes(5000), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.m.removeEntryFromMempool(tt.txid, tt.entry) + if !reflect.DeepEqual(tt.m, tt.want) { + t.Errorf("removeEntryFromMempool() got = %+v, want %+v", tt.m, tt.want) + } + }) + } +} From 783ab61cf6b63b52e0f62b2bac2bf815a0ccbbe4 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 6 Feb 2025 22:46:51 +0100 Subject: [PATCH 411/530] Add support for h suffix in addition to ' suffix is to denote hardened xpub derivation #1200 --- bchain/coins/btc/bitcoinlikeparser.go | 2 +- bchain/coins/btc/bitcoinparser_test.go | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/bchain/coins/btc/bitcoinlikeparser.go b/bchain/coins/btc/bitcoinlikeparser.go index 9034dbc1a4..67a31d0c57 100644 --- a/bchain/coins/btc/bitcoinlikeparser.go +++ b/bchain/coins/btc/bitcoinlikeparser.go @@ -440,7 +440,7 @@ var ( ) func init() { - xpubDesriptorRegex, _ = regexp.Compile(`^(?P(sh\(wpkh|wpkh|pk|pkh|wpkh|wsh|tr))\((\[\w+/(?P\d+)'/\d+'?/\d+'?\])?(?P\w+)(/(({(?P\d+(,\d+)*)})|(<(?P\d+(;\d+)*)>)|(?P\d+))/\*)?\)+`) + xpubDesriptorRegex, _ = regexp.Compile(`^(?P(sh\(wpkh|wpkh|pk|pkh|wpkh|wsh|tr))\((\[\w+/(?P\d+)['h]/\d+['h]?/\d+['h]?\])?(?P\w+)(/(({(?P\d+(,\d+)*)})|(<(?P\d+(;\d+)*)>)|(?P\d+))/\*)?\)+`) typeSubexpIndex = xpubDesriptorRegex.SubexpIndex("type") bipSubexpIndex = xpubDesriptorRegex.SubexpIndex("bip") xpubSubexpIndex = xpubDesriptorRegex.SubexpIndex("xpub") diff --git a/bchain/coins/btc/bitcoinparser_test.go b/bchain/coins/btc/bitcoinparser_test.go index a697cbfd1d..78d5ac4fbb 100644 --- a/bchain/coins/btc/bitcoinparser_test.go +++ b/bchain/coins/btc/bitcoinparser_test.go @@ -823,6 +823,18 @@ func TestParseXpubDescriptors(t *testing.T) { ChangeIndexes: []uint32{0, 1, 2}, }, }, + { + name: "tr([5c9e228d/86h/1h/0h]tpubD/{0,1,2}/*)#4rqwxvej", + xpub: "tr([5c9e228d/86h/1h/0h]tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1,2}/*)#4rqwxvej", + parser: btcTestnetParser, + want: &bchain.XpubDescriptor{ + XpubDescriptor: "tr([5c9e228d/86h/1h/0h]tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1,2}/*)#4rqwxvej", + Xpub: "tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN", + Type: bchain.P2TR, + Bip: "86", + ChangeIndexes: []uint32{0, 1, 2}, + }, + }, { name: "tr([5c9e228d/86'/1'/0']tpubD/<0;1;2>/*)#4rqwxvej", xpub: "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/<0;1;2>/*)#4rqwxvej", From a3b0a05b146bf992f13e807a1d608772d49d420a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 24 Oct 2024 15:56:27 +0200 Subject: [PATCH 412/530] Add Ethereum type EIP1559 fee estimate --- api/types.go | 15 ++ bchain/basechain.go | 5 + bchain/coins/blockchain.go | 5 + bchain/coins/btc/bitcoinrpc.go | 26 +-- bchain/coins/btc/mempoolspace.go | 7 +- bchain/coins/btc/whatthefee.go | 3 +- bchain/coins/eth/alternativefeeprovider.go | 19 +++ bchain/coins/eth/ethrpc.go | 126 ++++++++++++--- bchain/coins/eth/oneinchfees.go | 151 ++++++++++++++++++ bchain/types.go | 1 + bchain/types_ethereum_type.go | 15 ++ blockbook-api.ts | 12 ++ common/utils.go | 27 ++++ configs/coins/ethereum.json | 3 +- configs/coins/ethereum_archive.json | 5 +- configs/coins/ethereum_testnet_holesky.json | 3 +- .../ethereum_testnet_holesky_archive.json | 3 +- configs/coins/ethereum_testnet_sepolia.json | 3 +- .../ethereum_testnet_sepolia_archive.json | 3 +- server/websocket.go | 35 +++- server/ws_types.go | 13 +- 21 files changed, 418 insertions(+), 62 deletions(-) create mode 100644 bchain/coins/eth/alternativefeeprovider.go create mode 100644 bchain/coins/eth/oneinchfees.go diff --git a/api/types.go b/api/types.go index c283989354..13294d4ea5 100644 --- a/api/types.go +++ b/api/types.go @@ -566,3 +566,18 @@ type AvailableVsCurrencies struct { Tickers []string `json:"available_currencies"` Error string `json:"error,omitempty"` } + +// Eip1559Fee +type Eip1559Fee struct { + MaxFeePerGas *Amount `json:"maxFeePerGas"` + MaxPriorityFeePerGas *Amount `json:"maxPriorityFeePerGas"` +} + +// Eip1559Fees +type Eip1559Fees struct { + BaseFeePerGas *Amount `json:"baseFeePerGas,omitempty"` + Low *Eip1559Fee `json:"low,omitempty"` + Medium *Eip1559Fee `json:"medium,omitempty"` + High *Eip1559Fee `json:"high,omitempty"` + Instant *Eip1559Fee `json:"instant,omitempty"` +} diff --git a/bchain/basechain.go b/bchain/basechain.go index 5618c1d77b..2d7cdd2118 100644 --- a/bchain/basechain.go +++ b/bchain/basechain.go @@ -54,6 +54,11 @@ func (b *BaseChain) EthereumTypeEstimateGas(params map[string]interface{}) (uint return 0, errors.New("not supported") } +// EthereumTypeGetEip1559Fees is not supported +func (b *BaseChain) EthereumTypeGetEip1559Fees() (*Eip1559Fees, error) { + return nil, errors.New("not supported") +} + // GetContractInfo is not supported func (b *BaseChain) GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error) { return nil, errors.New("not supported") diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index e694dafca8..969e761295 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -323,6 +323,11 @@ func (c *blockChainWithMetrics) EthereumTypeEstimateGas(params map[string]interf return c.b.EthereumTypeEstimateGas(params) } +func (c *blockChainWithMetrics) EthereumTypeGetEip1559Fees() (v *bchain.Eip1559Fees, err error) { + defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetEip1559Fees", s, err) }(time.Now()) + return c.b.EthereumTypeGetEip1559Fees() +} + func (c *blockChainWithMetrics) GetContractInfo(contractDesc bchain.AddressDescriptor) (v *bchain.ContractInfo, err error) { defer func(s time.Time) { c.observeRPCLatency("GetContractInfo", s, err) }(time.Now()) return c.b.GetContractInfo(contractDesc) diff --git a/bchain/coins/btc/bitcoinrpc.go b/bchain/coins/btc/bitcoinrpc.go index 510f821b8c..e378d417bd 100644 --- a/bchain/coins/btc/bitcoinrpc.go +++ b/bchain/coins/btc/bitcoinrpc.go @@ -5,11 +5,9 @@ import ( "context" "encoding/hex" "encoding/json" - "io" "math/big" "net" "net/http" - "runtime/debug" "time" "github.com/golang/glog" @@ -907,26 +905,6 @@ func (b *BitcoinRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error) return res.Result, nil } -func safeDecodeResponse(body io.ReadCloser, res interface{}) (err error) { - var data []byte - defer func() { - if r := recover(); r != nil { - glog.Error("unmarshal json recovered from panic: ", r, "; data: ", string(data)) - debug.PrintStack() - if len(data) > 0 && len(data) < 2048 { - err = errors.Errorf("Error: %v", string(data)) - } else { - err = errors.New("Internal error") - } - } - }() - data, err = io.ReadAll(body) - if err != nil { - return err - } - return json.Unmarshal(data, &res) -} - // Call calls Backend RPC interface, using RPCMarshaler interface to marshall the request func (b *BitcoinRPC) Call(req interface{}, res interface{}) error { httpData, err := b.RPCMarshaler.Marshal(req) @@ -950,11 +928,11 @@ func (b *BitcoinRPC) Call(req interface{}, res interface{}) error { // if server returns HTTP error code it might not return json with response // handle both cases if httpRes.StatusCode != 200 { - err = safeDecodeResponse(httpRes.Body, &res) + err = common.SafeDecodeResponseFromReader(httpRes.Body, &res) if err != nil { return errors.Errorf("%v %v", httpRes.Status, err) } return nil } - return safeDecodeResponse(httpRes.Body, &res) + return common.SafeDecodeResponseFromReader(httpRes.Body, &res) } diff --git a/bchain/coins/btc/mempoolspace.go b/bchain/coins/btc/mempoolspace.go index a7cfc58b75..6de71db2d1 100644 --- a/bchain/coins/btc/mempoolspace.go +++ b/bchain/coins/btc/mempoolspace.go @@ -10,6 +10,7 @@ import ( "github.com/golang/glog" "github.com/juju/errors" "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/common" ) // https://mempool.space/api/v1/fees/recommended returns @@ -25,7 +26,7 @@ type mempoolSpaceFeeResult struct { type mempoolSpaceFeeParams struct { URL string `json:"url"` - PeriodSeconds int `periodSeconds:"url"` + PeriodSeconds int `json:"periodSeconds"` } type mempoolSpaceFeeProvider struct { @@ -41,7 +42,7 @@ func NewMempoolSpaceFee(chain bchain.BlockChain, params string) (alternativeFeeP return nil, err } if p.params.URL == "" || p.params.PeriodSeconds == 0 { - return nil, errors.New("NewWhatTheFee: Missing parameters") + return nil, errors.New("NewMempoolSpaceFee: Missing parameters") } p.chain = chain go p.mempoolSpaceFeeDownloader() @@ -131,5 +132,5 @@ func (p *mempoolSpaceFeeProvider) mempoolSpaceFeeGetData(res interface{}) error if httpRes.StatusCode != http.StatusOK { return errors.New(p.params.URL + " returned status " + strconv.Itoa(httpRes.StatusCode)) } - return safeDecodeResponse(httpRes.Body, &res) + return common.SafeDecodeResponseFromReader(httpRes.Body, &res) } diff --git a/bchain/coins/btc/whatthefee.go b/bchain/coins/btc/whatthefee.go index c7567f7a56..dba3193434 100644 --- a/bchain/coins/btc/whatthefee.go +++ b/bchain/coins/btc/whatthefee.go @@ -11,6 +11,7 @@ import ( "github.com/golang/glog" "github.com/juju/errors" "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/common" ) // https://whatthefee.io returns @@ -119,5 +120,5 @@ func (p *whatTheFeeProvider) whatTheFeeGetData(res interface{}) error { if httpRes.StatusCode != 200 { return errors.New("whatthefee.io returned status " + strconv.Itoa(httpRes.StatusCode)) } - return safeDecodeResponse(httpRes.Body, &res) + return common.SafeDecodeResponseFromReader(httpRes.Body, &res) } diff --git a/bchain/coins/eth/alternativefeeprovider.go b/bchain/coins/eth/alternativefeeprovider.go new file mode 100644 index 0000000000..066a2386e1 --- /dev/null +++ b/bchain/coins/eth/alternativefeeprovider.go @@ -0,0 +1,19 @@ +package eth + +import ( + "sync" + "time" + + "github.com/trezor/blockbook/bchain" +) + +type alternativeFeeProvider struct { + eip1559Fees *bchain.Eip1559Fees + lastSync time.Time + chain bchain.BlockChain + mux sync.Mutex +} + +type alternativeFeeProviderInterface interface { + GetEip1559Fees() (*bchain.Eip1559Fees, error) +} diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 67b9a28931..7c7e30ed0b 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -51,31 +51,35 @@ type Configuration struct { ProcessZeroInternalTransactions bool `json:"processZeroInternalTransactions"` ConsensusNodeVersionURL string `json:"consensusNodeVersion"` DisableMempoolSync bool `json:"disableMempoolSync,omitempty"` + Eip1559Fees bool `json:"eip1559Fees,omitempty"` + AlternativeEstimateFee string `json:"alternative_estimate_fee,omitempty"` + AlternativeEstimateFeeParams string `json:"alternative_estimate_fee_params,omitempty"` } // EthereumRPC is an interface to JSON-RPC eth service. type EthereumRPC struct { *bchain.BaseChain - Client bchain.EVMClient - RPC bchain.EVMRPCClient - MainNetChainID Network - Timeout time.Duration - Parser *EthereumParser - PushHandler func(bchain.NotificationType) - OpenRPC func(string) (bchain.EVMRPCClient, bchain.EVMClient, error) - Mempool *bchain.MempoolEthereumType - mempoolInitialized bool - bestHeaderLock sync.Mutex - bestHeader bchain.EVMHeader - bestHeaderTime time.Time - NewBlock bchain.EVMNewBlockSubscriber - newBlockSubscription bchain.EVMClientSubscription - NewTx bchain.EVMNewTxSubscriber - newTxSubscription bchain.EVMClientSubscription - ChainConfig *Configuration - supportedStakingPools []string - stakingPoolNames []string - stakingPoolContracts []string + Client bchain.EVMClient + RPC bchain.EVMRPCClient + MainNetChainID Network + Timeout time.Duration + Parser *EthereumParser + PushHandler func(bchain.NotificationType) + OpenRPC func(string) (bchain.EVMRPCClient, bchain.EVMClient, error) + Mempool *bchain.MempoolEthereumType + mempoolInitialized bool + bestHeaderLock sync.Mutex + bestHeader bchain.EVMHeader + bestHeaderTime time.Time + NewBlock bchain.EVMNewBlockSubscriber + newBlockSubscription bchain.EVMClientSubscription + NewTx bchain.EVMNewTxSubscriber + newTxSubscription bchain.EVMClientSubscription + ChainConfig *Configuration + supportedStakingPools []string + stakingPoolNames []string + stakingPoolContracts []string + alternativeFeeProvider alternativeFeeProviderInterface } // ProcessInternalTransactions specifies if internal transactions are processed @@ -166,6 +170,14 @@ func (b *EthereumRPC) Initialize() error { return err } + if b.ChainConfig.AlternativeEstimateFee == "1inch" { + if b.alternativeFeeProvider, err = NewOneInchFeesProvider(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil { + glog.Error("New1InchFeesProvider error ", err, " Reverting to default estimateFee functionality") + // disable AlternativeEstimateFee logic + b.alternativeFeeProvider = nil + } + } + glog.Info("rpc: block chain ", b.Network) return nil @@ -990,6 +1002,80 @@ func (b *EthereumRPC) EthereumTypeEstimateGas(params map[string]interface{}) (ui return b.Client.EstimateGas(ctx, msg) } +// EthereumTypeGetEip1559Fees retrieves Eip1559Fees, if supported +func (b *EthereumRPC) EthereumTypeGetEip1559Fees() (*bchain.Eip1559Fees, error) { + if !b.ChainConfig.Eip1559Fees { + return nil, nil + } + // if there is an alternative provider, use it + if b.alternativeFeeProvider != nil { + return b.alternativeFeeProvider.GetEip1559Fees() + } + + // otherwise use algorithm from here https://docs.alchemy.com/docs/how-to-build-a-gas-fee-estimator-using-eip-1559 + ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) + defer cancel() + + var maxPriorityFeePerGas hexutil.Big + err := b.RPC.CallContext(ctx, &maxPriorityFeePerGas, "eth_maxPriorityFeePerGas") + if err != nil { + return nil, err + } + + var fees bchain.Eip1559Fees + + type history struct { + OldestBlock string `json:"oldestBlock"` + Reward [][]string `json:"reward"` + BaseFeePerGas []string `json:"baseFeePerGas"` + GasUsedRatio []float64 `json:"gasUsedRatio"` + } + var h history + percentiles := []int{ + 20, // low + 70, // medium + 90, // high + 99, // instant + } + blocks := 4 + + err = b.RPC.CallContext(ctx, &h, "eth_feeHistory", blocks, "pending", percentiles) + if err != nil { + return nil, err + } + + hs, _ := json.Marshal(h) + baseFee, _ := hexutil.DecodeUint64(h.BaseFeePerGas[blocks-1]) + fees.BaseFeePerGas = big.NewInt(int64(baseFee)) + maxBasePriorityFee := maxPriorityFeePerGas.ToInt().Int64() + glog.Info("eth_maxPriorityFeePerGas ", maxPriorityFeePerGas) + glog.Info("eth_feeHistory ", string(hs)) + + for i := 0; i < 4; i++ { + var f bchain.Eip1559Fee + priorityFee := int64(0) + for j := 0; j < len(h.Reward); j++ { + p, _ := hexutil.DecodeUint64(h.Reward[j][i]) + priorityFee += int64(p) + } + priorityFee = priorityFee / int64(len(h.Reward)) + f.MaxFeePerGas = big.NewInt(priorityFee) + f.MaxPriorityFeePerGas = big.NewInt(maxBasePriorityFee) + maxBasePriorityFee *= 2 + switch i { + case 0: + fees.Low = &f + case 1: + fees.Medium = &f + case 2: + fees.High = &f + default: + fees.Instant = &f + } + } + return &fees, err +} + // SendRawTransaction sends raw transaction func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) { return b.callRpcStringResult("eth_sendRawTransaction", hex) diff --git a/bchain/coins/eth/oneinchfees.go b/bchain/coins/eth/oneinchfees.go new file mode 100644 index 0000000000..d979f82d0c --- /dev/null +++ b/bchain/coins/eth/oneinchfees.go @@ -0,0 +1,151 @@ +package eth + +import ( + "bytes" + "encoding/json" + "math/big" + "net/http" + "os" + "strconv" + "time" + + "github.com/golang/glog" + "github.com/juju/errors" + "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/common" +) + +// https://api.1inch.dev/gas-price/v1.5/1 returns +// { +// "baseFee": "12456587953", +// "low": { +// "maxPriorityFeePerGas": "1000000", +// "maxFeePerGas": "14948905543" +// }, +// "medium": { +// "maxPriorityFeePerGas": "2000000", +// "maxFeePerGas": "14949905543" +// }, +// "high": { +// "maxPriorityFeePerGas": "5000000", +// "maxFeePerGas": "14952905543" +// }, +// "instant": { +// "maxPriorityFeePerGas": "10000000", +// "maxFeePerGas": "29905811086" +// } +// } + +type oneInchFeeFeeResult struct { + MaxPriorityFeePerGas string `json:"maxPriorityFeePerGas"` + MaxFeePerGas string `json:"maxFeePerGas"` +} + +type oneInchFeeFeesResult struct { + BaseFee string `json:"baseFee"` + Low oneInchFeeFeeResult `json:"low"` + Medium oneInchFeeFeeResult `json:"medium"` + High oneInchFeeFeeResult `json:"high"` + Instant oneInchFeeFeeResult `json:"instant"` +} + +type oneInchFeeParams struct { + URL string `json:"url"` + PeriodSeconds int `json:"periodSeconds"` +} + +type oneInchFeeProvider struct { + *alternativeFeeProvider + params oneInchFeeParams + apiKey string +} + +// NewOneInchFeesProvider initializes https://api.1inch.dev provider +func NewOneInchFeesProvider(chain bchain.BlockChain, params string) (alternativeFeeProviderInterface, error) { + p := &oneInchFeeProvider{alternativeFeeProvider: &alternativeFeeProvider{}} + err := json.Unmarshal([]byte(params), &p.params) + if err != nil { + return nil, err + } + if p.params.URL == "" || p.params.PeriodSeconds == 0 { + return nil, errors.New("NewOneInchFeesProvider: missing config parameters 'url' or 'periodSeconds'.") + } + p.apiKey = os.Getenv("ONE_INCH_API_KEY") + if p.apiKey == "" { + return nil, errors.New("NewOneInchFeesProvider: missing ONE_INCH_API_KEY env variable.") + } + p.chain = chain + go p.FeeDownloader() + return p, nil +} + +func (p *oneInchFeeProvider) FeeDownloader() { + period := time.Duration(p.params.PeriodSeconds) * time.Second + timer := time.NewTimer(period) + for { + var data oneInchFeeFeesResult + err := p.getData(&data) + if err != nil { + glog.Error("oneInchFeeProvider.FeeDownloader", err) + } else { + p.processData(&data) + } + <-timer.C + timer.Reset(period) + } +} + +func bigIntFromString(s string) *big.Int { + b := big.NewInt(0) + b, _ = b.SetString(s, 10) + return b +} + +func feesFromResult(result *oneInchFeeFeeResult) *bchain.Eip1559Fee { + fee := bchain.Eip1559Fee{} + fee.MaxFeePerGas = bigIntFromString(result.MaxFeePerGas) + fee.MaxPriorityFeePerGas = bigIntFromString(result.MaxPriorityFeePerGas) + return &fee +} + +func (p *oneInchFeeProvider) processData(data *oneInchFeeFeesResult) bool { + fees := bchain.Eip1559Fees{} + fees.BaseFeePerGas = bigIntFromString(data.BaseFee) + fees.Instant = feesFromResult(&data.Instant) + fees.High = feesFromResult(&data.High) + fees.Medium = feesFromResult(&data.Medium) + fees.Low = feesFromResult(&data.Low) + p.mux.Lock() + defer p.mux.Unlock() + p.lastSync = time.Now() + p.eip1559Fees = &fees + glog.Infof("oneInchFeesProvider: %+v", p.eip1559Fees) + return true +} + +func (p *oneInchFeeProvider) getData(res interface{}) error { + var httpData []byte + httpReq, err := http.NewRequest("GET", p.params.URL, bytes.NewBuffer(httpData)) + if err != nil { + return err + } + httpReq.Header.Set("Content-Type", "application/json") + httpReq.Header.Set("Authorization", " Bearer "+p.apiKey) + httpRes, err := http.DefaultClient.Do(httpReq) + if httpRes != nil { + defer httpRes.Body.Close() + } + if err != nil { + return err + } + if httpRes.StatusCode != http.StatusOK { + return errors.New(p.params.URL + " returned status " + strconv.Itoa(httpRes.StatusCode)) + } + return common.SafeDecodeResponseFromReader(httpRes.Body, &res) +} + +func (p *oneInchFeeProvider) GetEip1559Fees() (*bchain.Eip1559Fees, error) { + p.mux.Lock() + defer p.mux.Unlock() + return p.eip1559Fees, nil +} diff --git a/bchain/types.go b/bchain/types.go index 8f1c25435f..3f44225f56 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -333,6 +333,7 @@ type BlockChain interface { EthereumTypeGetBalance(addrDesc AddressDescriptor) (*big.Int, error) EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) + EthereumTypeGetEip1559Fees() (*Eip1559Fees, error) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error) EthereumTypeGetSupportedStakingPools() []string EthereumTypeGetStakingPoolsData(addrDesc AddressDescriptor) ([]StakingPoolData, error) diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index 652a9edd52..6f807170a1 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -163,3 +163,18 @@ type StakingPoolData struct { RestakedReward big.Int `json:"restakedReward"` // restakedRewardOf method AutocompoundBalance big.Int `json:"autocompoundBalance"` // autocompoundBalanceOf method } + +// Eip1559Fee +type Eip1559Fee struct { + MaxFeePerGas *big.Int `json:"maxFeePerGas"` + MaxPriorityFeePerGas *big.Int `json:"maxPriorityFeePerGas"` +} + +// Eip1559Fees +type Eip1559Fees struct { + BaseFeePerGas *big.Int `json:"baseFeePerGas,omitempty"` + Low *Eip1559Fee `json:"low,omitempty"` + Medium *Eip1559Fee `json:"medium,omitempty"` + High *Eip1559Fee `json:"high,omitempty"` + Instant *Eip1559Fee `json:"instant,omitempty"` +} diff --git a/blockbook-api.ts b/blockbook-api.ts index 295acff9d7..706803f54c 100644 --- a/blockbook-api.ts +++ b/blockbook-api.ts @@ -415,10 +415,22 @@ export interface WsEstimateFeeReq { value?: string; }; } +export interface Eip1559Fee { + maxFeePerGas: string; + maxPriorityFeePerGas: string; +} +export interface Eip1559Fees { + baseFeePerGas?: string; + low?: Eip1559Fee; + medium?: Eip1559Fee; + high?: Eip1559Fee; + instant?: Eip1559Fee; +} export interface WsEstimateFeeRes { feePerTx?: string; feePerUnit?: string; feeLimit?: string; + eip1559?: Eip1559Fees; } export interface WsSendTransactionReq { hex: string; diff --git a/common/utils.go b/common/utils.go index bfe8980bf0..e90e116639 100644 --- a/common/utils.go +++ b/common/utils.go @@ -1,7 +1,13 @@ package common import ( + "encoding/json" + "io" + "runtime/debug" "time" + + "github.com/golang/glog" + "github.com/juju/errors" ) // TickAndDebounce calls function f on trigger channel or with tickTime period (whatever is sooner) with debounce @@ -39,3 +45,24 @@ Loop: } } } + +// SafeDecodeResponseFromReader reads from io.ReadCloser safely, with recovery from panic +func SafeDecodeResponseFromReader(body io.ReadCloser, res interface{}) (err error) { + var data []byte + defer func() { + if r := recover(); r != nil { + glog.Error("unmarshal json recovered from panic: ", r, "; data: ", string(data)) + debug.PrintStack() + if len(data) > 0 && len(data) < 2048 { + err = errors.Errorf("Error: %v", string(data)) + } else { + err = errors.New("Internal error") + } + } + }() + data, err = io.ReadAll(body) + if err != nil { + return err + } + return json.Unmarshal(data, &res) +} diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index c81e86602c..47316d95f1 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -59,6 +59,7 @@ "additional_params": { "consensusNodeVersion": "http://localhost:7536/eth/v1/node/version", "address_aliases": true, + "eip1559Fees": true, "mempoolTxTimeoutHours": 48, "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", @@ -72,4 +73,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 6bab22394f..e7e839ba90 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -59,6 +59,9 @@ "additional_params": { "consensusNodeVersion": "http://localhost:7516/eth/v1/node/version", "address_aliases": true, + "eip1559Fees": true, + "alternative_estimate_fee": "1inch", + "alternative_estimate_fee_params": "{\"url\": \"https://api.1inch.dev/gas-price/v1.5/1\", \"periodSeconds\": 20}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, @@ -73,4 +76,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index 7dceab3677..a5ea8e3315 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -58,6 +58,7 @@ "block_addresses_to_keep": 3000, "additional_params": { "consensusNodeVersion": "http://localhost:17516/eth/v1/node/version", + "eip1559Fees": true, "mempoolTxTimeoutHours": 12, "queryBackendOnMempoolResync": false } @@ -67,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 3474b63a79..6dccbccd40 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -60,6 +60,7 @@ "additional_params": { "consensusNodeVersion": "http://localhost:17536/eth/v1/node/version", "address_aliases": true, + "eip1559Fees": true, "mempoolTxTimeoutHours": 12, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, @@ -73,4 +74,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index dfca9bf914..480caf3944 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -58,6 +58,7 @@ "block_addresses_to_keep": 3000, "additional_params": { "consensusNodeVersion": "http://localhost:17576/eth/v1/node/version", + "eip1559Fees": true, "mempoolTxTimeoutHours": 12, "queryBackendOnMempoolResync": false } @@ -67,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 35926e52a5..8504d7c41c 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -60,6 +60,7 @@ "additional_params": { "consensusNodeVersion": "http://localhost:17586/eth/v1/node/version", "address_aliases": true, + "eip1559Fees": true, "mempoolTxTimeoutHours": 12, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, @@ -73,4 +74,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/server/websocket.go b/server/websocket.go index c782adea6b..a95e5f83aa 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -367,7 +367,7 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *WsRe return }, "estimateFee": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) { - return s.estimateFee(c, req.Params) + return s.estimateFee(req.Params) }, "sendTransaction": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) { r := WsSendTransactionReq{} @@ -632,7 +632,19 @@ func (s *WebsocketServer) getBlock(id string, page, pageSize int) (interface{}, return block, nil } -func (s *WebsocketServer) estimateFee(c *websocketChannel, params []byte) (interface{}, error) { +func eip1559FeesToApi(fee *bchain.Eip1559Fee) *api.Eip1559Fee { + if fee == nil { + return nil + } + apiFee := api.Eip1559Fee{} + if fee != nil { + apiFee.MaxFeePerGas = (*api.Amount)(fee.MaxFeePerGas) + apiFee.MaxPriorityFeePerGas = (*api.Amount)(fee.MaxPriorityFeePerGas) + } + return &apiFee +} + +func (s *WebsocketServer) estimateFee(params []byte) (interface{}, error) { var r WsEstimateFeeReq err := json.Unmarshal(params, &r) if err != nil { @@ -653,11 +665,26 @@ func (s *WebsocketServer) estimateFee(c *websocketChannel, params []byte) (inter if err != nil { return nil, err } + feePerTx := new(big.Int) + feePerTx.Mul(&fee, new(big.Int).SetUint64(gas)) + eip1559, err := s.chain.EthereumTypeGetEip1559Fees() + if err != nil { + return nil, err + } + var eip1559Api *api.Eip1559Fees + if eip1559 != nil { + eip1559Api = &api.Eip1559Fees{} + eip1559Api.BaseFeePerGas = (*api.Amount)(eip1559.BaseFeePerGas) + eip1559Api.Instant = eip1559FeesToApi(eip1559.Instant) + eip1559Api.High = eip1559FeesToApi(eip1559.High) + eip1559Api.Medium = eip1559FeesToApi(eip1559.Medium) + eip1559Api.Low = eip1559FeesToApi(eip1559.Low) + } for i := range r.Blocks { res[i].FeePerUnit = fee.String() res[i].FeeLimit = sg - fee.Mul(&fee, new(big.Int).SetUint64(gas)) - res[i].FeePerTx = fee.String() + res[i].FeePerTx = feePerTx.String() + res[i].Eip1559 = eip1559Api } } else { conservative := true diff --git a/server/ws_types.go b/server/ws_types.go index f49f02ad4a..111b698e1c 100644 --- a/server/ws_types.go +++ b/server/ws_types.go @@ -1,6 +1,10 @@ package server -import "encoding/json" +import ( + "encoding/json" + + "github.com/trezor/blockbook/api" +) type WsReq struct { ID string `json:"id"` @@ -106,9 +110,10 @@ type WsEstimateFeeReq struct { } type WsEstimateFeeRes struct { - FeePerTx string `json:"feePerTx,omitempty"` - FeePerUnit string `json:"feePerUnit,omitempty"` - FeeLimit string `json:"feeLimit,omitempty"` + FeePerTx string `json:"feePerTx,omitempty"` + FeePerUnit string `json:"feePerUnit,omitempty"` + FeeLimit string `json:"feeLimit,omitempty"` + Eip1559 *api.Eip1559Fees `json:"eip1559,omitempty"` } type WsSendTransactionReq struct { From a2ba8c4b0963c75bad569ef54a2c55d7defe8748 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 17 Nov 2024 11:46:24 +0100 Subject: [PATCH 413/530] Add Ethereum type EIP1559 fee estimate from infura --- api/types.go | 18 +- bchain/coins/eth/alternativefeeprovider.go | 6 + bchain/coins/eth/ethrpc.go | 28 ++- bchain/coins/eth/infurafees.go | 190 ++++++++++++++++++ bchain/coins/eth/oneinchfees.go | 17 +- bchain/types_ethereum_type.go | 18 +- configs/coins/bsc_archive.json | 6 +- configs/coins/ethereum_archive.json | 4 +- .../ethereum_testnet_holesky_archive.json | 2 + server/websocket.go | 25 ++- 10 files changed, 277 insertions(+), 37 deletions(-) create mode 100644 bchain/coins/eth/infurafees.go diff --git a/api/types.go b/api/types.go index 13294d4ea5..1525a77fec 100644 --- a/api/types.go +++ b/api/types.go @@ -571,13 +571,21 @@ type AvailableVsCurrencies struct { type Eip1559Fee struct { MaxFeePerGas *Amount `json:"maxFeePerGas"` MaxPriorityFeePerGas *Amount `json:"maxPriorityFeePerGas"` + MinWaitTimeEstimate int `json:"minWaitTimeEstimate,omitempty"` + MaxWaitTimeEstimate int `json:"maxWaitTimeEstimate,omitempty"` } // Eip1559Fees type Eip1559Fees struct { - BaseFeePerGas *Amount `json:"baseFeePerGas,omitempty"` - Low *Eip1559Fee `json:"low,omitempty"` - Medium *Eip1559Fee `json:"medium,omitempty"` - High *Eip1559Fee `json:"high,omitempty"` - Instant *Eip1559Fee `json:"instant,omitempty"` + BaseFeePerGas *Amount `json:"baseFeePerGas,omitempty"` + Low *Eip1559Fee `json:"low,omitempty"` + Medium *Eip1559Fee `json:"medium,omitempty"` + High *Eip1559Fee `json:"high,omitempty"` + Instant *Eip1559Fee `json:"instant,omitempty"` + NetworkCongestion float64 `json:"networkCongestion,omitempty"` + LatestPriorityFeeRange []*Amount `json:"latestPriorityFeeRange,omitempty"` + HistoricalPriorityFeeRange []*Amount `json:"historicalPriorityFeeRange,omitempty"` + HistoricalBaseFeeRange []*Amount `json:"historicalBaseFeeRange,omitempty"` + PriorityFeeTrend string `json:"priorityFeeTrend,omitempty"` + BaseFeeTrend string `json:"baseFeeTrend,omitempty"` } diff --git a/bchain/coins/eth/alternativefeeprovider.go b/bchain/coins/eth/alternativefeeprovider.go index 066a2386e1..fab48b33b6 100644 --- a/bchain/coins/eth/alternativefeeprovider.go +++ b/bchain/coins/eth/alternativefeeprovider.go @@ -17,3 +17,9 @@ type alternativeFeeProvider struct { type alternativeFeeProviderInterface interface { GetEip1559Fees() (*bchain.Eip1559Fees, error) } + +func (p *alternativeFeeProvider) GetEip1559Fees() (*bchain.Eip1559Fees, error) { + p.mux.Lock() + defer p.mux.Unlock() + return p.eip1559Fees, nil +} diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 7c7e30ed0b..c0b00b78f9 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -110,6 +110,23 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification s.Timeout = time.Duration(c.RPCTimeout) * time.Second s.PushHandler = pushHandler + if s.ChainConfig.AlternativeEstimateFee == "1inch" { + if s.alternativeFeeProvider, err = NewOneInchFeesProvider(s, s.ChainConfig.AlternativeEstimateFeeParams); err != nil { + glog.Error("New1InchFeesProvider error ", err, " Reverting to default estimateFee functionality") + // disable AlternativeEstimateFee logic + s.alternativeFeeProvider = nil + } + } else if s.ChainConfig.AlternativeEstimateFee == "infura" { + if s.alternativeFeeProvider, err = NewInfuraFeesProvider(s, s.ChainConfig.AlternativeEstimateFeeParams); err != nil { + glog.Error("NewInfuraFeesProvider error ", err, " Reverting to default estimateFee functionality") + // disable AlternativeEstimateFee logic + s.alternativeFeeProvider = nil + } + } + if s.alternativeFeeProvider != nil { + glog.Info("Using alternative fee provider ", s.ChainConfig.AlternativeEstimateFee) + } + return s, nil } @@ -170,14 +187,6 @@ func (b *EthereumRPC) Initialize() error { return err } - if b.ChainConfig.AlternativeEstimateFee == "1inch" { - if b.alternativeFeeProvider, err = NewOneInchFeesProvider(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil { - glog.Error("New1InchFeesProvider error ", err, " Reverting to default estimateFee functionality") - // disable AlternativeEstimateFee logic - b.alternativeFeeProvider = nil - } - } - glog.Info("rpc: block chain ", b.Network) return nil @@ -1043,6 +1052,9 @@ func (b *EthereumRPC) EthereumTypeGetEip1559Fees() (*bchain.Eip1559Fees, error) if err != nil { return nil, err } + if len(h.BaseFeePerGas) < blocks { + return nil, nil + } hs, _ := json.Marshal(h) baseFee, _ := hexutil.DecodeUint64(h.BaseFeePerGas[blocks-1]) diff --git a/bchain/coins/eth/infurafees.go b/bchain/coins/eth/infurafees.go new file mode 100644 index 0000000000..448a815603 --- /dev/null +++ b/bchain/coins/eth/infurafees.go @@ -0,0 +1,190 @@ +package eth + +import ( + "bytes" + "encoding/json" + "math/big" + "net/http" + "os" + "strconv" + "strings" + "time" + + "github.com/golang/glog" + "github.com/juju/errors" + "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/common" +) + +// https://gas.api.infura.io/v3/${api_key}/networks/1/suggestedGasFees returns +// { +// "low": { +// "suggestedMaxPriorityFeePerGas": "0.01128", +// "suggestedMaxFeePerGas": "9.919888552", +// "minWaitTimeEstimate": 15000, +// "maxWaitTimeEstimate": 60000 +// }, +// "medium": { +// "suggestedMaxPriorityFeePerGas": "1.148315423", +// "suggestedMaxFeePerGas": "15.317625653", +// "minWaitTimeEstimate": 15000, +// "maxWaitTimeEstimate": 45000 +// }, +// "high": { +// "suggestedMaxPriorityFeePerGas": "2", +// "suggestedMaxFeePerGas": "24.78979967", +// "minWaitTimeEstimate": 15000, +// "maxWaitTimeEstimate": 30000 +// }, +// "estimatedBaseFee": "9.908608552", +// "networkCongestion": 0.004, +// "latestPriorityFeeRange": [ +// "0.05", +// "4" +// ], +// "historicalPriorityFeeRange": [ +// "0.006381976", +// "155.777346207" +// ], +// "historicalBaseFeeRange": [ +// "9.243163495", +// "16.734915363" +// ], +// "priorityFeeTrend": "up", +// "baseFeeTrend": "up", +// "version": "0.0.1" +// } + +type infuraFeeResult struct { + MaxPriorityFeePerGas string `json:"suggestedMaxPriorityFeePerGas"` + MaxFeePerGas string `json:"suggestedMaxFeePerGas"` + MinWaitTimeEstimate int `json:"minWaitTimeEstimate"` + MaxWaitTimeEstimate int `json:"maxWaitTimeEstimate"` +} + +type infuraFeesResult struct { + BaseFee string `json:"estimatedBaseFee"` + Low infuraFeeResult `json:"low"` + Medium infuraFeeResult `json:"medium"` + High infuraFeeResult `json:"high"` + NetworkCongestion float64 `json:"networkCongestion"` + LatestPriorityFeeRange []string `json:"latestPriorityFeeRange"` + HistoricalPriorityFeeRange []string `json:"historicalPriorityFeeRange"` + HistoricalBaseFeeRange []string `json:"historicalBaseFeeRange"` + PriorityFeeTrend string `json:"priorityFeeTrend"` + BaseFeeTrend string `json:"baseFeeTrend"` +} + +type infuraFeeParams struct { + URL string `json:"url"` + PeriodSeconds int `json:"periodSeconds"` +} + +type infuraFeeProvider struct { + *alternativeFeeProvider + params infuraFeeParams + apiKey string +} + +// NewInfuraFeesProvider initializes https://gas.api.infura.io provider +func NewInfuraFeesProvider(chain bchain.BlockChain, params string) (alternativeFeeProviderInterface, error) { + p := &infuraFeeProvider{alternativeFeeProvider: &alternativeFeeProvider{}} + err := json.Unmarshal([]byte(params), &p.params) + if err != nil { + return nil, err + } + if p.params.URL == "" || p.params.PeriodSeconds == 0 { + return nil, errors.New("NewInfuraFeesProvider: missing config parameters 'url' or 'periodSeconds'.") + } + p.apiKey = os.Getenv("INFURA_API_KEY") + if p.apiKey == "" { + return nil, errors.New("NewInfuraFeesProvider: missing INFURA_API_KEY env variable.") + } + p.params.URL = strings.Replace(p.params.URL, "${api_key}", p.apiKey, -1) + p.chain = chain + go p.FeeDownloader() + return p, nil +} + +func (p *infuraFeeProvider) FeeDownloader() { + period := time.Duration(p.params.PeriodSeconds) * time.Second + timer := time.NewTimer(period) + for { + var data infuraFeesResult + err := p.getData(&data) + if err != nil { + glog.Error("infuraFeeProvider.FeeDownloader", err) + } else { + p.processData(&data) + } + <-timer.C + timer.Reset(period) + } +} + +func bigIntFromFloatString(s string) *big.Int { + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil + } + return big.NewInt(int64(f * 1e9)) +} + +func infuraFeesFromResult(result *infuraFeeResult) *bchain.Eip1559Fee { + fee := bchain.Eip1559Fee{} + fee.MaxFeePerGas = bigIntFromFloatString(result.MaxFeePerGas) + fee.MaxPriorityFeePerGas = bigIntFromFloatString(result.MaxPriorityFeePerGas) + fee.MinWaitTimeEstimate = result.MinWaitTimeEstimate + fee.MaxWaitTimeEstimate = result.MaxWaitTimeEstimate + return &fee +} + +func rangeFromString(feeRange []string) []*big.Int { + if feeRange == nil { + return nil + } + result := make([]*big.Int, len(feeRange)) + for i := range feeRange { + result[i] = bigIntFromFloatString(feeRange[i]) + } + return result +} + +func (p *infuraFeeProvider) processData(data *infuraFeesResult) bool { + fees := bchain.Eip1559Fees{} + fees.BaseFeePerGas = bigIntFromFloatString(data.BaseFee) + fees.High = infuraFeesFromResult(&data.High) + fees.Medium = infuraFeesFromResult(&data.Medium) + fees.Low = infuraFeesFromResult(&data.Low) + fees.NetworkCongestion = data.NetworkCongestion + fees.LatestPriorityFeeRange = rangeFromString(data.LatestPriorityFeeRange) + fees.HistoricalPriorityFeeRange = rangeFromString(data.HistoricalPriorityFeeRange) + fees.HistoricalBaseFeeRange = rangeFromString(data.HistoricalBaseFeeRange) + fees.PriorityFeeTrend = data.PriorityFeeTrend + fees.BaseFeeTrend = data.BaseFeeTrend + p.mux.Lock() + defer p.mux.Unlock() + p.lastSync = time.Now() + p.eip1559Fees = &fees + return true +} + +func (p *infuraFeeProvider) getData(res interface{}) error { + var httpData []byte + httpReq, err := http.NewRequest("GET", p.params.URL, bytes.NewBuffer(httpData)) + if err != nil { + return err + } + httpReq.Header.Set("Content-Type", "application/json") + httpRes, err := http.DefaultClient.Do(httpReq) + if httpRes != nil { + defer httpRes.Body.Close() + } + if err != nil { + return err + } + if httpRes.StatusCode != http.StatusOK { + return errors.New(p.params.URL + " returned status " + strconv.Itoa(httpRes.StatusCode)) + } + return common.SafeDecodeResponseFromReader(httpRes.Body, &res) +} diff --git a/bchain/coins/eth/oneinchfees.go b/bchain/coins/eth/oneinchfees.go index d979f82d0c..e7bcecabcb 100644 --- a/bchain/coins/eth/oneinchfees.go +++ b/bchain/coins/eth/oneinchfees.go @@ -101,7 +101,7 @@ func bigIntFromString(s string) *big.Int { return b } -func feesFromResult(result *oneInchFeeFeeResult) *bchain.Eip1559Fee { +func oneInchFeesFromResult(result *oneInchFeeFeeResult) *bchain.Eip1559Fee { fee := bchain.Eip1559Fee{} fee.MaxFeePerGas = bigIntFromString(result.MaxFeePerGas) fee.MaxPriorityFeePerGas = bigIntFromString(result.MaxPriorityFeePerGas) @@ -111,15 +111,14 @@ func feesFromResult(result *oneInchFeeFeeResult) *bchain.Eip1559Fee { func (p *oneInchFeeProvider) processData(data *oneInchFeeFeesResult) bool { fees := bchain.Eip1559Fees{} fees.BaseFeePerGas = bigIntFromString(data.BaseFee) - fees.Instant = feesFromResult(&data.Instant) - fees.High = feesFromResult(&data.High) - fees.Medium = feesFromResult(&data.Medium) - fees.Low = feesFromResult(&data.Low) + fees.Instant = oneInchFeesFromResult(&data.Instant) + fees.High = oneInchFeesFromResult(&data.High) + fees.Medium = oneInchFeesFromResult(&data.Medium) + fees.Low = oneInchFeesFromResult(&data.Low) p.mux.Lock() defer p.mux.Unlock() p.lastSync = time.Now() p.eip1559Fees = &fees - glog.Infof("oneInchFeesProvider: %+v", p.eip1559Fees) return true } @@ -143,9 +142,3 @@ func (p *oneInchFeeProvider) getData(res interface{}) error { } return common.SafeDecodeResponseFromReader(httpRes.Body, &res) } - -func (p *oneInchFeeProvider) GetEip1559Fees() (*bchain.Eip1559Fees, error) { - p.mux.Lock() - defer p.mux.Unlock() - return p.eip1559Fees, nil -} diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index 6f807170a1..f1cb5d4ee1 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -168,13 +168,21 @@ type StakingPoolData struct { type Eip1559Fee struct { MaxFeePerGas *big.Int `json:"maxFeePerGas"` MaxPriorityFeePerGas *big.Int `json:"maxPriorityFeePerGas"` + MinWaitTimeEstimate int `json:"minWaitTimeEstimate,omitempty"` + MaxWaitTimeEstimate int `json:"maxWaitTimeEstimate,omitempty"` } // Eip1559Fees type Eip1559Fees struct { - BaseFeePerGas *big.Int `json:"baseFeePerGas,omitempty"` - Low *Eip1559Fee `json:"low,omitempty"` - Medium *Eip1559Fee `json:"medium,omitempty"` - High *Eip1559Fee `json:"high,omitempty"` - Instant *Eip1559Fee `json:"instant,omitempty"` + BaseFeePerGas *big.Int `json:"baseFeePerGas,omitempty"` + Low *Eip1559Fee `json:"low,omitempty"` + Medium *Eip1559Fee `json:"medium,omitempty"` + High *Eip1559Fee `json:"high,omitempty"` + Instant *Eip1559Fee `json:"instant,omitempty"` + NetworkCongestion float64 `json:"networkCongestion,omitempty"` + LatestPriorityFeeRange []*big.Int `json:"latestPriorityFeeRange,omitempty"` + HistoricalPriorityFeeRange []*big.Int `json:"historicalPriorityFeeRange,omitempty"` + HistoricalBaseFeeRange []*big.Int `json:"historicalBaseFeeRange,omitempty"` + PriorityFeeTrend string `json:"priorityFeeTrend,omitempty"` + BaseFeeTrend string `json:"baseFeeTrend,omitempty"` } diff --git a/configs/coins/bsc_archive.json b/configs/coins/bsc_archive.json index 0b460876df..5261674539 100644 --- a/configs/coins/bsc_archive.json +++ b/configs/coins/bsc_archive.json @@ -15,7 +15,7 @@ }, "ipc": { "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", - "rpc_timeout": 25 + "rpc_timeout": 240 }, "backend": { "package_name": "backend-bsc-archive", @@ -58,9 +58,13 @@ "block_addresses_to_keep": 600, "additional_params": { "address_aliases": true, + "eip1559Fees": true, + "alternative_estimate_fee": "infura", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/56/suggestedGasFees\", \"periodSeconds\": 20}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, + "disableMempoolSync": true, "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"coin\": \"binancecoin\",\"platformIdentifier\": \"binance-smart-chain\",\"platformVsCurrency\": \"bnb\",\"periodSeconds\": 900}", diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index e7e839ba90..f9a868b8b4 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -60,8 +60,8 @@ "consensusNodeVersion": "http://localhost:7516/eth/v1/node/version", "address_aliases": true, "eip1559Fees": true, - "alternative_estimate_fee": "1inch", - "alternative_estimate_fee_params": "{\"url\": \"https://api.1inch.dev/gas-price/v1.5/1\", \"periodSeconds\": 20}", + "alternative_estimate_fee": "infura", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/1/suggestedGasFees\", \"periodSeconds\": 20}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 6dccbccd40..e659c49d39 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -61,6 +61,8 @@ "consensusNodeVersion": "http://localhost:17536/eth/v1/node/version", "address_aliases": true, "eip1559Fees": true, + "alternative_estimate_fee": "infura", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/17000/suggestedGasFees\", \"periodSeconds\": 60}", "mempoolTxTimeoutHours": 12, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, diff --git a/server/websocket.go b/server/websocket.go index a95e5f83aa..3dc30a5464 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -637,13 +637,24 @@ func eip1559FeesToApi(fee *bchain.Eip1559Fee) *api.Eip1559Fee { return nil } apiFee := api.Eip1559Fee{} - if fee != nil { - apiFee.MaxFeePerGas = (*api.Amount)(fee.MaxFeePerGas) - apiFee.MaxPriorityFeePerGas = (*api.Amount)(fee.MaxPriorityFeePerGas) - } + apiFee.MaxFeePerGas = (*api.Amount)(fee.MaxFeePerGas) + apiFee.MaxPriorityFeePerGas = (*api.Amount)(fee.MaxPriorityFeePerGas) + apiFee.MaxWaitTimeEstimate = fee.MaxWaitTimeEstimate + apiFee.MinWaitTimeEstimate = fee.MinWaitTimeEstimate return &apiFee } +func eip1559FeeRangeToApi(feeRange []*big.Int) []*api.Amount { + if feeRange == nil { + return nil + } + apiFeeRange := make([]*api.Amount, len(feeRange)) + for i := range feeRange { + apiFeeRange[i] = (*api.Amount)(feeRange[i]) + } + return apiFeeRange +} + func (s *WebsocketServer) estimateFee(params []byte) (interface{}, error) { var r WsEstimateFeeReq err := json.Unmarshal(params, &r) @@ -679,6 +690,12 @@ func (s *WebsocketServer) estimateFee(params []byte) (interface{}, error) { eip1559Api.High = eip1559FeesToApi(eip1559.High) eip1559Api.Medium = eip1559FeesToApi(eip1559.Medium) eip1559Api.Low = eip1559FeesToApi(eip1559.Low) + eip1559Api.NetworkCongestion = eip1559.NetworkCongestion + eip1559Api.BaseFeeTrend = eip1559.BaseFeeTrend + eip1559Api.PriorityFeeTrend = eip1559.PriorityFeeTrend + eip1559Api.LatestPriorityFeeRange = eip1559FeeRangeToApi(eip1559.LatestPriorityFeeRange) + eip1559Api.HistoricalBaseFeeRange = eip1559FeeRangeToApi(eip1559.HistoricalBaseFeeRange) + eip1559Api.HistoricalPriorityFeeRange = eip1559FeeRangeToApi(eip1559.HistoricalPriorityFeeRange) } for i := range r.Blocks { res[i].FeePerUnit = fee.String() From 9feda1a857771a229f66f46e8e126f8545bcc64d Mon Sep 17 00:00:00 2001 From: Albina Nikiforova Date: Mon, 13 Jan 2025 16:37:28 +0100 Subject: [PATCH 414/530] chore(blockbook): rename type to standard --- api/types.go | 52 +++++++++++++++++++---------------- api/worker.go | 24 ++++++++-------- api/xpub.go | 2 ++ bchain/coins/bsc/bscrpc.go | 8 +++--- bchain/coins/eth/ethrpc.go | 2 +- bchain/types.go | 25 +++++++++-------- bchain/types_ethereum_type.go | 28 +++++++++++-------- db/rocksdb_ethereumtype.go | 18 ++++++------ server/public.go | 16 +++++------ 9 files changed, 95 insertions(+), 80 deletions(-) diff --git a/api/types.go b/api/types.go index 1525a77fec..a884d476c9 100644 --- a/api/types.go +++ b/api/types.go @@ -158,21 +158,23 @@ type MultiTokenValue struct { // Token contains info about tokens held by an address type Token struct { - Type bchain.TokenTypeName `json:"type" ts_type:"'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155'"` - Name string `json:"name"` - Path string `json:"path,omitempty"` - Contract string `json:"contract,omitempty"` - Transfers int `json:"transfers"` - Symbol string `json:"symbol,omitempty"` - Decimals int `json:"decimals,omitempty"` - BalanceSat *Amount `json:"balance,omitempty"` - BaseValue float64 `json:"baseValue,omitempty"` // value in the base currency (ETH for Ethereum) - SecondaryValue float64 `json:"secondaryValue,omitempty"` // value in secondary (fiat) currency, if specified - Ids []Amount `json:"ids,omitempty"` // multiple ERC721 tokens - MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` // multiple ERC1155 tokens - TotalReceivedSat *Amount `json:"totalReceived,omitempty"` - TotalSentSat *Amount `json:"totalSent,omitempty"` - ContractIndex string `json:"-"` + // Deprecated: Use Standard instead. + Type bchain.TokenStandardName `json:"type" ts_type:"'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155'"` + Standard bchain.TokenStandardName `json:"standard" ts_type:"'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155'"` + Name string `json:"name"` + Path string `json:"path,omitempty"` + Contract string `json:"contract,omitempty"` + Transfers int `json:"transfers"` + Symbol string `json:"symbol,omitempty"` + Decimals int `json:"decimals,omitempty"` + BalanceSat *Amount `json:"balance,omitempty"` + BaseValue float64 `json:"baseValue,omitempty"` // value in the base currency (ETH for Ethereum) + SecondaryValue float64 `json:"secondaryValue,omitempty"` // value in secondary (fiat) currency, if specified + Ids []Amount `json:"ids,omitempty"` // multiple ERC721 tokens + MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` // multiple ERC1155 tokens + TotalReceivedSat *Amount `json:"totalReceived,omitempty"` + TotalSentSat *Amount `json:"totalSent,omitempty"` + ContractIndex string `json:"-"` } // Tokens is array of Token @@ -204,15 +206,17 @@ func (a Tokens) Less(i, j int) bool { // TokenTransfer contains info about a token transfer done in a transaction type TokenTransfer struct { - Type bchain.TokenTypeName `json:"type"` - From string `json:"from"` - To string `json:"to"` - Contract string `json:"contract"` - Name string `json:"name,omitempty"` - Symbol string `json:"symbol,omitempty"` - Decimals int `json:"decimals,omitempty"` - Value *Amount `json:"value,omitempty"` - MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` + // Deprecated: Use Standard instead. + Type bchain.TokenStandardName `json:"type"` + Standard bchain.TokenStandardName `json:"standard"` + From string `json:"from"` + To string `json:"to"` + Contract string `json:"contract"` + Name string `json:"name,omitempty"` + Symbol string `json:"symbol,omitempty"` + Decimals int `json:"decimals,omitempty"` + Value *Amount `json:"value,omitempty"` + MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` } type EthereumInternalTransfer struct { diff --git a/api/worker.go b/api/worker.go index d478f85c23..07afae339d 100644 --- a/api/worker.go +++ b/api/worker.go @@ -622,7 +622,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, return r, nil } -func (w *Worker) GetContractInfo(contract string, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) { +func (w *Worker) GetContractInfo(contract string, typeFromContext bchain.TokenStandardName) (*bchain.ContractInfo, bool, error) { cd, err := w.chainParser.GetAddrDescFromAddress(contract) if err != nil { return nil, false, err @@ -630,7 +630,7 @@ func (w *Worker) GetContractInfo(contract string, typeFromContext bchain.TokenTy return w.getContractDescriptorInfo(cd, typeFromContext) } -func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) { +func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFromContext bchain.TokenStandardName) (*bchain.ContractInfo, bool, error) { var err error validContract := true contractInfo, err := w.db.GetContractInfo(cd, typeFromContext) @@ -647,7 +647,7 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom glog.Errorf("GetContractInfo from chain error %v, contract %v", err, cd) } if contractInfo == nil { - contractInfo = &bchain.ContractInfo{Type: bchain.UnknownTokenType, Decimals: w.chainParser.AmountDecimals()} + contractInfo = &bchain.ContractInfo{Standard: bchain.UnknownTokenType, Decimals: w.chainParser.AmountDecimals()} addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(cd) if len(addresses) > 0 { contractInfo.Contract = addresses[0] @@ -655,14 +655,14 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom validContract = false } else { - if typeFromContext != bchain.UnknownTokenType && contractInfo.Type == bchain.UnknownTokenType { - contractInfo.Type = typeFromContext + if typeFromContext != bchain.UnknownTokenType && contractInfo.Standard == bchain.UnknownTokenType { + contractInfo.Standard = typeFromContext } if err = w.db.StoreContractInfo(contractInfo); err != nil { glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) } } - } else if (contractInfo.Type == bchain.UnhandledTokenType || len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) { + } else if (contractInfo.Standard == bchain.UnhandledTokenType || len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) { // fix contract name/symbol that was parsed as a string consisting of zeroes blockchainContractInfo, err := w.chain.GetContractInfo(cd) if err != nil { @@ -681,9 +681,9 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom if blockchainContractInfo != nil { contractInfo.Decimals = blockchainContractInfo.Decimals } - if contractInfo.Type == bchain.UnhandledTokenType { + if contractInfo.Standard == bchain.UnhandledTokenType { glog.Infof("Contract %v %v [%s] handled", cd, typeFromContext, contractInfo.Name) - contractInfo.Type = typeFromContext + contractInfo.Standard = typeFromContext } if err = w.db.StoreContractInfo(contractInfo); err != nil { glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) @@ -700,7 +700,7 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add contractCache := make(contractInfoCache) for i := range transfers { t := transfers[i] - typeName := bchain.EthereumTokenTypeMap[t.Type] + typeName := bchain.EthereumTokenStandardMap[t.Standard] var contractInfo *bchain.ContractInfo if info, ok := contractCache[t.Contract]; ok { contractInfo = info @@ -715,7 +715,7 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add } var value *Amount var values []MultiTokenValue - if t.Type == bchain.MultiToken { + if t.Standard == bchain.MultiToken { values = make([]MultiTokenValue, len(t.MultiTokenValues)) for j := range values { values[j].Id = (*Amount)(&t.MultiTokenValues[j].Id) @@ -957,7 +957,7 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) { } func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails, ticker *common.CurrencyRatesTicker, secondaryCoin string) (*Token, error) { - typeName := bchain.EthereumTokenTypeMap[c.Type] + typeName := bchain.EthereumTokenStandardMap[c.Standard] ci, validContract, err := w.getContractDescriptorInfo(c.Contract, typeName) if err != nil { return nil, errors.Annotatef(err, "getEthereumContractBalance %v", c.Contract) @@ -1468,7 +1468,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco StakingPools: ed.stakingPools, } // keep address backward compatible, set deprecated Erc20Contract value if ERC20 token - if ed.contractInfo != nil && ed.contractInfo.Type == bchain.ERC20TokenType { + if ed.contractInfo != nil && ed.contractInfo.Standard == bchain.ERC20TokenStandard { r.Erc20Contract = ed.contractInfo } glog.Info("GetAddress-", option, " ", address, ", ", time.Since(start)) diff --git a/api/xpub.go b/api/xpub.go index 3a0a6d642f..b4ab068567 100644 --- a/api/xpub.go +++ b/api/xpub.go @@ -267,7 +267,9 @@ func (w *Worker) tokenFromXpubAddress(data *xpubData, ad *xpubAddress, changeInd } } return Token{ + // Deprecated: Use Standard instead. Type: bchain.XPUBAddressTokenType, + Standard: bchain.XPUBAddressTokenType, Name: address, Decimals: w.chainParser.AmountDecimals(), BalanceSat: (*Amount)(balance), diff --git a/bchain/coins/bsc/bscrpc.go b/bchain/coins/bsc/bscrpc.go index a1fb649cd8..bca05b4719 100644 --- a/bchain/coins/bsc/bscrpc.go +++ b/bchain/coins/bsc/bscrpc.go @@ -15,9 +15,9 @@ const ( MainNet eth.Network = 56 // bsc token type names - BEP20TokenType bchain.TokenTypeName = "BEP20" - BEP721TokenType bchain.TokenTypeName = "BEP721" - BEP1155TokenType bchain.TokenTypeName = "BEP1155" + BEP20TokenStandard bchain.TokenStandardName = "BEP20" + BEP721TokenStandard bchain.TokenStandardName = "BEP721" + BEP1155TokenStandard bchain.TokenStandardName = "BEP1155" ) // BNBSmartChainRPC is an interface to JSON-RPC bsc service. @@ -33,7 +33,7 @@ func NewBNBSmartChainRPC(config json.RawMessage, pushHandler func(bchain.Notific } // overwrite EthereumTokenTypeMap with bsc specific token type names - bchain.EthereumTokenTypeMap = []bchain.TokenTypeName{BEP20TokenType, BEP721TokenType, BEP1155TokenType} + bchain.EthereumTokenStandardMap = []bchain.TokenStandardName{BEP20TokenStandard, BEP721TokenStandard, BEP1155TokenStandard} s := &BNBSmartChainRPC{ EthereumRPC: c.(*eth.EthereumRPC), diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index c0b00b78f9..00c7a0cf59 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -643,7 +643,7 @@ func (b *EthereumRPC) getCreationContractInfo(contract string, height uint32) *b Contract: contract, } // } - ci.Type = bchain.UnhandledTokenType + ci.Standard = bchain.UnhandledTokenType ci.CreatedInBlock = height return ci } diff --git a/bchain/types.go b/bchain/types.go index 3f44225f56..df93de96b4 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -116,26 +116,29 @@ type MempoolTx struct { CoinSpecificData interface{} `json:"-"` } -// TokenType - type of token -type TokenType int +// // Deprecated: Use TokenStandard instead. +// type TokenType int -// TokenType enumeration +// TokenStandard - standard of token +type TokenStandard int + +// TokenStandard enumeration const ( - FungibleToken = TokenType(iota) // ERC20/BEP20 - NonFungibleToken // ERC721/BEP721 - MultiToken // ERC1155/BEP1155 + FungibleToken = TokenStandard(iota) // ERC20/BEP20 + NonFungibleToken // ERC721/BEP721 + MultiToken // ERC1155/BEP1155 ) -// TokenTypeName specifies type of token -type TokenTypeName string +// TokenStandardName specifies type of token +type TokenStandardName string // Token types const ( - UnknownTokenType TokenTypeName = "" - UnhandledTokenType TokenTypeName = "-" + UnknownTokenType TokenStandardName = "" + UnhandledTokenType TokenStandardName = "-" // XPUBAddressTokenType is address derived from xpub - XPUBAddressTokenType TokenTypeName = "XPUBAddress" + XPUBAddressTokenType TokenStandardName = "XPUBAddress" ) // TokenTransfers is array of TokenTransfer diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index f1cb5d4ee1..69f7238104 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -59,25 +59,27 @@ type EthereumInternalData struct { // ContractInfo contains info about a contract type ContractInfo struct { - Type TokenTypeName `json:"type"` - Contract string `json:"contract"` - Name string `json:"name"` - Symbol string `json:"symbol"` - Decimals int `json:"decimals"` - CreatedInBlock uint32 `json:"createdInBlock,omitempty"` - DestructedInBlock uint32 `json:"destructedInBlock,omitempty"` + // Deprecated: Use Standard instead. + Type TokenStandardName `json:"type"` + Standard TokenStandardName `json:"standard"` + Contract string `json:"contract"` + Name string `json:"name"` + Symbol string `json:"symbol"` + Decimals int `json:"decimals"` + CreatedInBlock uint32 `json:"createdInBlock,omitempty"` + DestructedInBlock uint32 `json:"destructedInBlock,omitempty"` } // Ethereum token type names const ( - ERC20TokenType TokenTypeName = "ERC20" - ERC771TokenType TokenTypeName = "ERC721" - ERC1155TokenType TokenTypeName = "ERC1155" + ERC20TokenStandard TokenStandardName = "ERC20" + ERC771TokenStandard TokenStandardName = "ERC721" + ERC1155TokenStandard TokenStandardName = "ERC1155" ) // EthereumTokenTypeMap maps bchain.TokenType to TokenTypeName // the map must match all bchain.TokenType to avoid index out of range panic -var EthereumTokenTypeMap = []TokenTypeName{ERC20TokenType, ERC771TokenType, ERC1155TokenType} +var EthereumTokenStandardMap = []TokenStandardName{ERC20TokenStandard, ERC771TokenStandard, ERC1155TokenStandard} type MultiTokenValue struct { Id big.Int @@ -86,7 +88,9 @@ type MultiTokenValue struct { // TokenTransfer contains a single token transfer type TokenTransfer struct { - Type TokenType + // Deprecated: Use Standard instead. + Type TokenStandard + Standard TokenStandard Contract string From string To string diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index 11371a7914..c7551d25ee 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -108,7 +108,9 @@ func (s *MultiTokenValues) upsert(m bchain.MultiTokenValue, index int32, aggrega // AddrContract is Contract address with number of transactions done by given address type AddrContract struct { - Type bchain.TokenType + // Deprecated: Use Standard instead. + Type bchain.TokenStandard + Standard bchain.TokenStandard Contract bchain.AddressDescriptor Txs uint Value big.Int // single value of ERC20 @@ -177,7 +179,7 @@ func unpackAddrContracts(buf []byte, addrDesc bchain.AddressDescriptor) (*AddrCo contract := append(bchain.AddressDescriptor(nil), buf[:eth.EthereumTypeAddressDescriptorLen]...) txs, l := unpackVaruint(buf[eth.EthereumTypeAddressDescriptorLen:]) buf = buf[eth.EthereumTypeAddressDescriptorLen+l:] - ttt := bchain.TokenType(txs & 3) + ttt := bchain.TokenStandard(txs & 3) txs >>= 2 ac := AddrContract{ Type: ttt, @@ -391,7 +393,7 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address type ethBlockTxContract struct { from, to, contract bchain.AddressDescriptor - transferType bchain.TokenType + transferType bchain.TokenStandard value big.Int idValues []bchain.MultiTokenValue } @@ -868,7 +870,7 @@ func unpackContractInfo(buf []byte) (*bchain.ContractInfo, error) { contractInfo.Symbol, l = unpackString(buf) buf = buf[l:] s, l = unpackString(buf) - contractInfo.Type = bchain.TokenTypeName(s) + contractInfo.Standard = bchain.TokenStandardName(s) buf = buf[l:] ui, l = unpackVaruint(buf) contractInfo.Decimals = int(ui) @@ -891,7 +893,7 @@ func (d *RocksDB) GetContractInfoForAddress(address string) (*bchain.ContractInf // GetContractInfo gets contract from cache or DB and possibly updates the type from typeFromContext // it is hard to guess the type of the contract using API, it is easier to set it the first time the contract is processed in a tx -func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, error) { +func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, typeFromContext bchain.TokenStandardName) (*bchain.ContractInfo, error) { cacheKey := string(contract) cachedContractsMux.Lock() contractInfo, found := cachedContracts[cacheKey] @@ -912,8 +914,8 @@ func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, typeFromCon contractInfo.Contract = addresses[0] } // if the type is specified and stored contractInfo has unknown type, set and store it - if typeFromContext != bchain.UnknownTokenType && contractInfo.Type == bchain.UnknownTokenType { - contractInfo.Type = typeFromContext + if typeFromContext != bchain.UnknownTokenStandard && contractInfo.Standard == bchain.UnknownTokenStandard { + contractInfo.Standard = typeFromContext err = d.db.PutCF(d.wo, d.cfh[cfContracts], contract, packContractInfo(contractInfo)) if err != nil { return nil, err @@ -1142,7 +1144,7 @@ func unpackBlockTx(buf []byte, pos int) (*ethBlockTx, int, error) { return nil, 0, err } cc, l = unpackVaruint(buf[pos:]) - c.transferType = bchain.TokenType(cc) + c.transferType = bchain.TokenStandard(cc) pos += l if c.transferType == bchain.MultiToken { cc, l = unpackVaruint(buf[pos:]) diff --git a/server/public.go b/server/public.go index e2d8c11903..9b7a1df07e 100644 --- a/server/public.go +++ b/server/public.go @@ -299,9 +299,9 @@ func (s *PublicServer) newTemplateData(r *http.Request) *TemplateData { TOSLink: api.Text.TOSLink, } if t.ChainType == bchain.ChainEthereumType { - t.FungibleTokenName = bchain.EthereumTokenTypeMap[bchain.FungibleToken] - t.NonFungibleTokenName = bchain.EthereumTokenTypeMap[bchain.NonFungibleToken] - t.MultiTokenName = bchain.EthereumTokenTypeMap[bchain.MultiToken] + t.FungibleTokenName = bchain.EthereumTokenStandardMap[bchain.FungibleToken] + t.NonFungibleTokenName = bchain.EthereumTokenStandardMap[bchain.NonFungibleToken] + t.MultiTokenName = bchain.EthereumTokenStandardMap[bchain.MultiToken] } if !s.debug { t.Minified = ".min.4" @@ -378,9 +378,9 @@ type TemplateData struct { CoinLabel string InternalExplorer bool ChainType bchain.ChainType - FungibleTokenName bchain.TokenTypeName - NonFungibleTokenName bchain.TokenTypeName - MultiTokenName bchain.TokenTypeName + FungibleTokenName bchain.TokenStandardName + NonFungibleTokenName bchain.TokenStandardName + MultiTokenName bchain.TokenStandardName Address *api.Address AddrStr string Tx *api.Tx @@ -742,7 +742,7 @@ func isOwnAddress(td *TemplateData, a string) bool { } // called from template, returns count of token transfers of given type in a tx -func tokenTransfersCount(tx *api.Tx, t bchain.TokenTypeName) int { +func tokenTransfersCount(tx *api.Tx, t bchain.TokenStandardName) int { count := 0 for i := range tx.TokenTransfers { if tx.TokenTransfers[i].Type == t { @@ -753,7 +753,7 @@ func tokenTransfersCount(tx *api.Tx, t bchain.TokenTypeName) int { } // called from template, returns count of tokens in array of given type -func tokenCount(tokens []api.Token, t bchain.TokenTypeName) int { +func tokenCount(tokens []api.Token, t bchain.TokenStandardName) int { count := 0 for i := range tokens { if tokens[i].Type == t { From cddbf7228a2599933cc4e4541be9c3a40c761f6b Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 17 Jan 2025 17:06:14 +0100 Subject: [PATCH 415/530] chore(blockbook): rename type to standard --- api/types.go | 11 +-- api/worker.go | 47 +++++++------ api/xpub.go | 4 +- bchain/coins/eth/contract.go | 16 ++--- bchain/coins/eth/contract_test.go | 10 +-- bchain/coins/eth/ethrpc.go | 3 +- bchain/types.go | 10 +-- bchain/types_ethereum_type.go | 2 - db/rocksdb_ethereumtype.go | 24 +++---- db/rocksdb_ethereumtype_test.go | 74 +++++++++++---------- server/internal.go | 2 +- server/public_ethereumtype_test.go | 8 +-- server/public_test.go | 20 +++--- static/templates/address.html | 18 ++--- static/templates/tokenDetail.html | 4 +- static/templates/txdetail_ethereumtype.html | 6 +- tests/dbtestdata/dbtestdata_ethereumtype.go | 2 +- tests/dbtestdata/fakechain_ethereumtype.go | 2 +- 18 files changed, 135 insertions(+), 128 deletions(-) diff --git a/api/types.go b/api/types.go index a884d476c9..305bf37978 100644 --- a/api/types.go +++ b/api/types.go @@ -361,9 +361,10 @@ type Address struct { TotalBaseValue float64 `json:"totalBaseValue,omitempty"` // value including tokens in base currency TotalSecondaryValue float64 `json:"totalSecondaryValue,omitempty"` // value including tokens in secondary currency ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"` - Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty"` // deprecated - AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` - StakingPools []StakingPool `json:"stakingPools,omitempty"` + // Deprecated: replaced by ContractInfo + Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty"` + AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` + StakingPools []StakingPool `json:"stakingPools,omitempty"` // helpers for explorer Filter string `json:"-"` XPubAddresses map[string]struct{} `json:"-"` @@ -590,6 +591,6 @@ type Eip1559Fees struct { LatestPriorityFeeRange []*Amount `json:"latestPriorityFeeRange,omitempty"` HistoricalPriorityFeeRange []*Amount `json:"historicalPriorityFeeRange,omitempty"` HistoricalBaseFeeRange []*Amount `json:"historicalBaseFeeRange,omitempty"` - PriorityFeeTrend string `json:"priorityFeeTrend,omitempty"` - BaseFeeTrend string `json:"baseFeeTrend,omitempty"` + PriorityFeeTrend string `json:"priorityFeeTrend,omitempty" ts_type:"'up' | 'down'"` + BaseFeeTrend string `json:"baseFeeTrend,omitempty" ts_type:"'up' | 'down'"` } diff --git a/api/worker.go b/api/worker.go index 07afae339d..bd122d20fa 100644 --- a/api/worker.go +++ b/api/worker.go @@ -176,10 +176,10 @@ func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliases if err != nil || addrDesc == nil { continue } - ci, err := w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType) + ci, err := w.db.GetContractInfo(addrDesc, bchain.UnknownTokenStandard) if err == nil && ci != nil { - if ci.Type == bchain.UnhandledTokenType { - ci, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenType) + if ci.Standard == bchain.UnhandledTokenStandard { + ci, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenStandard) } if err == nil && ci != nil && ci.Name != "" { aliases[a] = AddressAlias{Type: "Contract", Alias: ci.Name} @@ -647,7 +647,7 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom glog.Errorf("GetContractInfo from chain error %v, contract %v", err, cd) } if contractInfo == nil { - contractInfo = &bchain.ContractInfo{Standard: bchain.UnknownTokenType, Decimals: w.chainParser.AmountDecimals()} + contractInfo = &bchain.ContractInfo{Standard: bchain.UnknownTokenStandard, Decimals: w.chainParser.AmountDecimals()} addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(cd) if len(addresses) > 0 { contractInfo.Contract = addresses[0] @@ -655,14 +655,15 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom validContract = false } else { - if typeFromContext != bchain.UnknownTokenType && contractInfo.Standard == bchain.UnknownTokenType { + if typeFromContext != bchain.UnknownTokenStandard && contractInfo.Standard == bchain.UnknownTokenStandard { contractInfo.Standard = typeFromContext + contractInfo.Type = typeFromContext } if err = w.db.StoreContractInfo(contractInfo); err != nil { glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) } } - } else if (contractInfo.Standard == bchain.UnhandledTokenType || len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) { + } else if (contractInfo.Standard == bchain.UnhandledTokenStandard || len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) { // fix contract name/symbol that was parsed as a string consisting of zeroes blockchainContractInfo, err := w.chain.GetContractInfo(cd) if err != nil { @@ -681,9 +682,10 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom if blockchainContractInfo != nil { contractInfo.Decimals = blockchainContractInfo.Decimals } - if contractInfo.Standard == bchain.UnhandledTokenType { + if contractInfo.Standard == bchain.UnhandledTokenStandard { glog.Infof("Contract %v %v [%s] handled", cd, typeFromContext, contractInfo.Name) contractInfo.Standard = typeFromContext + contractInfo.Type = typeFromContext } if err = w.db.StoreContractInfo(contractInfo); err != nil { glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) @@ -700,12 +702,12 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add contractCache := make(contractInfoCache) for i := range transfers { t := transfers[i] - typeName := bchain.EthereumTokenStandardMap[t.Standard] + standard := bchain.EthereumTokenStandardMap[t.Standard] var contractInfo *bchain.ContractInfo if info, ok := contractCache[t.Contract]; ok { contractInfo = info } else { - info, _, err := w.GetContractInfo(t.Contract, typeName) + info, _, err := w.GetContractInfo(t.Contract, standard) if err != nil { glog.Errorf("getContractInfo error %v, contract %v", err, t.Contract) continue @@ -727,7 +729,8 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add aggregateAddress(addresses, t.From) aggregateAddress(addresses, t.To) tokens[i] = TokenTransfer{ - Type: typeName, + Type: standard, + Standard: standard, Contract: t.Contract, From: t.From, To: t.To, @@ -755,7 +758,7 @@ func (w *Worker) GetEthereumTokenURI(contract string, id string) (string, *bchai if err != nil { return "", nil, err } - ci, _, err := w.getContractDescriptorInfo(cd, bchain.UnknownTokenType) + ci, _, err := w.getContractDescriptorInfo(cd, bchain.UnknownTokenStandard) if err != nil { return "", nil, err } @@ -957,8 +960,8 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) { } func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails, ticker *common.CurrencyRatesTicker, secondaryCoin string) (*Token, error) { - typeName := bchain.EthereumTokenStandardMap[c.Standard] - ci, validContract, err := w.getContractDescriptorInfo(c.Contract, typeName) + standard := bchain.EthereumTokenStandardMap[c.Standard] + ci, validContract, err := w.getContractDescriptorInfo(c.Contract, standard) if err != nil { return nil, errors.Annotatef(err, "getEthereumContractBalance %v", c.Contract) } @@ -966,14 +969,15 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i Contract: ci.Contract, Name: ci.Name, Symbol: ci.Symbol, - Type: typeName, + Type: standard, + Standard: standard, Transfers: int(c.Txs), Decimals: ci.Decimals, ContractIndex: strconv.Itoa(index), } // return contract balances/values only at or above AccountDetailsTokenBalances if details >= AccountDetailsTokenBalances && validContract { - if c.Type == bchain.FungibleToken { + if c.Standard == bchain.FungibleToken { // get Erc20 Contract Balance from blockchain, balance obtained from adding and subtracting transfers is not correct b, err := w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, c.Contract) if err != nil { @@ -1022,7 +1026,7 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i // a fallback method in case internal transactions are not processed and there is no indexed info about contract balance for an address func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bchain.AddressDescriptor, details AccountDetails) (*Token, error) { var b *big.Int - ci, validContract, err := w.getContractDescriptorInfo(contract, bchain.UnknownTokenType) + ci, validContract, err := w.getContractDescriptorInfo(contract, bchain.UnknownTokenStandard) if err != nil { return nil, errors.Annotatef(err, "GetContractInfo %v", contract) } @@ -1037,7 +1041,8 @@ func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bch b = nil } return &Token{ - Type: ci.Type, + Type: ci.Standard, + Standard: ci.Standard, BalanceSat: (*Amount)(b), Contract: ci.Contract, Name: ci.Name, @@ -1142,12 +1147,12 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto d.tokens = d.tokens[:j] sort.Sort(d.tokens) } - d.contractInfo, err = w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType) + d.contractInfo, err = w.db.GetContractInfo(addrDesc, bchain.UnknownTokenStandard) if err != nil { return nil, nil, err } - if d.contractInfo != nil && d.contractInfo.Type == bchain.UnhandledTokenType { - d.contractInfo, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenType) + if d.contractInfo != nil && d.contractInfo.Standard == bchain.UnhandledTokenStandard { + d.contractInfo, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenStandard) if err != nil { return nil, nil, err } @@ -1683,7 +1688,7 @@ func (w *Worker) GetBalanceHistory(address string, fromTimestamp, toTimestamp in } // do not get balance history for contracts if w.chainType == bchain.ChainEthereumType { - ci, err := w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType) + ci, err := w.db.GetContractInfo(addrDesc, bchain.UnknownTokenStandard) if err != nil { return nil, err } diff --git a/api/xpub.go b/api/xpub.go index b4ab068567..ca1c4c009c 100644 --- a/api/xpub.go +++ b/api/xpub.go @@ -268,8 +268,8 @@ func (w *Worker) tokenFromXpubAddress(data *xpubData, ad *xpubAddress, changeInd } return Token{ // Deprecated: Use Standard instead. - Type: bchain.XPUBAddressTokenType, - Standard: bchain.XPUBAddressTokenType, + Type: bchain.XPUBAddressStandard, + Standard: bchain.XPUBAddressStandard, Name: address, Decimals: w.chainParser.AmountDecimals(), BalanceSat: (*Amount)(balance), diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go index 6dbca33e3a..08149c085b 100644 --- a/bchain/coins/eth/contract.go +++ b/bchain/coins/eth/contract.go @@ -51,16 +51,16 @@ func processTransferEvent(l *bchain.RpcLog) (transfer *bchain.TokenTransfer, err } }() tl := len(l.Topics) - var ttt bchain.TokenType + var standard bchain.TokenStandard var value big.Int if tl == 3 { - ttt = bchain.FungibleToken + standard = bchain.FungibleToken _, ok := value.SetString(l.Data, 0) if !ok { return nil, errors.New("ERC20 log Data is not a number") } } else if tl == 4 { - ttt = bchain.NonFungibleToken + standard = bchain.NonFungibleToken _, ok := value.SetString(l.Topics[3], 0) if !ok { return nil, errors.New("ERC721 log Topics[3] is not a number") @@ -78,7 +78,7 @@ func processTransferEvent(l *bchain.RpcLog) (transfer *bchain.TokenTransfer, err return nil, err } return &bchain.TokenTransfer{ - Type: ttt, + Standard: standard, Contract: EIP55AddressFromAddress(l.Address), From: EIP55AddressFromAddress(from), To: EIP55AddressFromAddress(to), @@ -119,7 +119,7 @@ func processERC1155TransferSingleEvent(l *bchain.RpcLog) (transfer *bchain.Token return nil, errors.New("ERC1155 log Data value is not a number") } return &bchain.TokenTransfer{ - Type: bchain.MultiToken, + Standard: bchain.MultiToken, Contract: EIP55AddressFromAddress(l.Address), From: EIP55AddressFromAddress(from), To: EIP55AddressFromAddress(to), @@ -190,7 +190,7 @@ func processERC1155TransferBatchEvent(l *bchain.RpcLog) (transfer *bchain.TokenT idValues[i] = bchain.MultiTokenValue{Id: id, Value: value} } return &bchain.TokenTransfer{ - Type: bchain.MultiToken, + Standard: bchain.MultiToken, Contract: EIP55AddressFromAddress(l.Address), From: EIP55AddressFromAddress(from), To: EIP55AddressFromAddress(to), @@ -239,7 +239,7 @@ func contractGetTransfersFromTx(tx *bchain.RpcTransaction) (bchain.TokenTransfer return nil, errors.New("Data is not a number") } r = append(r, &bchain.TokenTransfer{ - Type: bchain.FungibleToken, + Standard: bchain.FungibleToken, Contract: EIP55AddressFromAddress(tx.To), From: EIP55AddressFromAddress(tx.From), To: EIP55AddressFromAddress(to), @@ -263,7 +263,7 @@ func contractGetTransfersFromTx(tx *bchain.RpcTransaction) (bchain.TokenTransfer return nil, errors.New("Data is not a number") } r = append(r, &bchain.TokenTransfer{ - Type: bchain.NonFungibleToken, + Standard: bchain.NonFungibleToken, Contract: EIP55AddressFromAddress(tx.To), From: EIP55AddressFromAddress(from), To: EIP55AddressFromAddress(to), diff --git a/bchain/coins/eth/contract_test.go b/bchain/coins/eth/contract_test.go index 587d98a774..ca70c85878 100644 --- a/bchain/coins/eth/contract_test.go +++ b/bchain/coins/eth/contract_test.go @@ -133,7 +133,7 @@ func Test_contractGetTransfersFromLog(t *testing.T) { }, want: bchain.TokenTransfers{ { - Type: bchain.NonFungibleToken, + Standard: bchain.NonFungibleToken, Contract: "0x5689b918D34C038901870105A6C7fc24744D31eB", From: "0x0a206d4d5ff79cb5069def7fe3598421cff09391", To: "0x6a016d7eec560549ffa0fbdb7f15c2b27302087f", @@ -171,7 +171,7 @@ func Test_contractGetTransfersFromLog(t *testing.T) { }, want: bchain.TokenTransfers{ { - Type: bchain.MultiToken, + Standard: bchain.MultiToken, Contract: "0x6Fd712E3A5B556654044608F9129040A4839E36c", From: "0xa3950b823cb063dd9afc0d27f35008b805b3ed53", To: "0x4392faf3bb96b5694ecc6ef64726f61cdd4bb0ec", @@ -195,7 +195,7 @@ func Test_contractGetTransfersFromLog(t *testing.T) { }, want: bchain.TokenTransfers{ { - Type: bchain.MultiToken, + Standard: bchain.MultiToken, Contract: "0x6c42c26a081c2f509f8bb68fb7ac3062311ccfb7", From: "0x0000000000000000000000000000000000000000", To: "0x5dc6288b35e0807a3d6feb89b3a2ff4ab773168e", @@ -247,7 +247,7 @@ func Test_contractGetTransfersFromTx(t *testing.T) { args: (b1.Txs[1].CoinSpecificData.(bchain.EthereumSpecificData)).Tx, want: bchain.TokenTransfers{ { - Type: bchain.FungibleToken, + Standard: bchain.FungibleToken, Contract: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2", From: "0x20cd153de35d469ba46127a0c8f18626b59a256a", To: "0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f", @@ -260,7 +260,7 @@ func Test_contractGetTransfersFromTx(t *testing.T) { args: (b2.Txs[2].CoinSpecificData.(bchain.EthereumSpecificData)).Tx, want: bchain.TokenTransfers{ { - Type: bchain.NonFungibleToken, + Standard: bchain.NonFungibleToken, Contract: "0xcda9fc258358ecaa88845f19af595e908bb7efe9", From: "0x837e3f699d85a4b0b99894567e9233dfb1dcb081", To: "0x7b62eb7fe80350dc7ec945c0b73242cb9877fb1b", diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 00c7a0cf59..7d3e35a6b7 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -643,7 +643,8 @@ func (b *EthereumRPC) getCreationContractInfo(contract string, height uint32) *b Contract: contract, } // } - ci.Standard = bchain.UnhandledTokenType + ci.Standard = bchain.UnhandledTokenStandard + ci.Type = bchain.UnhandledTokenStandard ci.CreatedInBlock = height return ci } diff --git a/bchain/types.go b/bchain/types.go index df93de96b4..83b73d51dc 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -134,11 +134,11 @@ type TokenStandardName string // Token types const ( - UnknownTokenType TokenStandardName = "" - UnhandledTokenType TokenStandardName = "-" + UnknownTokenStandard TokenStandardName = "" + UnhandledTokenStandard TokenStandardName = "-" - // XPUBAddressTokenType is address derived from xpub - XPUBAddressTokenType TokenStandardName = "XPUBAddress" + // XPUBAddressStandard is address derived from xpub + XPUBAddressStandard TokenStandardName = "XPUBAddress" ) // TokenTransfers is array of TokenTransfer @@ -147,7 +147,7 @@ type TokenTransfers []*TokenTransfer func (a TokenTransfers) Len() int { return len(a) } func (a TokenTransfers) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a TokenTransfers) Less(i, j int) bool { - return a[i].Type < a[j].Type + return a[i].Standard < a[j].Standard } // Block is block header and list of transactions diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index 69f7238104..202a14e4e4 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -88,8 +88,6 @@ type MultiTokenValue struct { // TokenTransfer contains a single token transfer type TokenTransfer struct { - // Deprecated: Use Standard instead. - Type TokenStandard Standard TokenStandard Contract string From string diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index c7551d25ee..f52d705b07 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -108,8 +108,6 @@ func (s *MultiTokenValues) upsert(m bchain.MultiTokenValue, index int32, aggrega // AddrContract is Contract address with number of transactions done by given address type AddrContract struct { - // Deprecated: Use Standard instead. - Type bchain.TokenStandard Standard bchain.TokenStandard Contract bchain.AddressDescriptor Txs uint @@ -138,12 +136,12 @@ func packAddrContracts(acs *AddrContracts) []byte { buf = append(buf, varBuf[:l]...) for _, ac := range acs.Contracts { buf = append(buf, ac.Contract...) - l = packVaruint(uint(ac.Type)+ac.Txs<<2, varBuf) + l = packVaruint(uint(ac.Standard)+ac.Txs<<2, varBuf) buf = append(buf, varBuf[:l]...) - if ac.Type == bchain.FungibleToken { + if ac.Standard == bchain.FungibleToken { l = packBigint(&ac.Value, varBuf) buf = append(buf, varBuf[:l]...) - } else if ac.Type == bchain.NonFungibleToken { + } else if ac.Standard == bchain.NonFungibleToken { l = packVaruint(uint(len(ac.Ids)), varBuf) buf = append(buf, varBuf[:l]...) for i := range ac.Ids { @@ -182,7 +180,7 @@ func unpackAddrContracts(buf []byte, addrDesc bchain.AddressDescriptor) (*AddrCo ttt := bchain.TokenStandard(txs & 3) txs >>= 2 ac := AddrContract{ - Type: ttt, + Standard: ttt, Contract: contract, Txs: txs, } @@ -318,9 +316,9 @@ func addToContract(c *AddrContract, contractIndex int, index int32, contract bch s.Add(s, v) } } - if transfer.Type == bchain.FungibleToken { + if transfer.Standard == bchain.FungibleToken { aggregate(&c.Value, &transfer.Value) - } else if transfer.Type == bchain.NonFungibleToken { + } else if transfer.Standard == bchain.NonFungibleToken { if index < 0 { c.Ids.remove(transfer.Value) } else { @@ -371,7 +369,7 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address contractIndex = len(ac.Contracts) ac.Contracts = append(ac.Contracts, AddrContract{ Contract: contract, - Type: transfer.Type, + Standard: transfer.Standard, }) } c := &ac.Contracts[contractIndex] @@ -568,7 +566,7 @@ func (d *RocksDB) processContractTransfers(blockTx *ethBlockTx, tx *bchain.Tx, a return err } bc := &blockTx.contracts[i] - bc.transferType = t.Type + bc.transferType = t.Standard bc.from = from bc.to = to bc.contract = contract @@ -849,7 +847,7 @@ var cachedContractsMux sync.Mutex func packContractInfo(contractInfo *bchain.ContractInfo) []byte { buf := packString(contractInfo.Name) buf = append(buf, packString(contractInfo.Symbol)...) - buf = append(buf, packString(string(contractInfo.Type))...) + buf = append(buf, packString(string(contractInfo.Standard))...) varBuf := make([]byte, vlq.MaxLen64) l := packVaruint(uint(contractInfo.Decimals), varBuf) buf = append(buf, varBuf[:l]...) @@ -871,6 +869,7 @@ func unpackContractInfo(buf []byte) (*bchain.ContractInfo, error) { buf = buf[l:] s, l = unpackString(buf) contractInfo.Standard = bchain.TokenStandardName(s) + contractInfo.Type = bchain.TokenStandardName(s) buf = buf[l:] ui, l = unpackVaruint(buf) contractInfo.Decimals = int(ui) @@ -916,6 +915,7 @@ func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, typeFromCon // if the type is specified and stored contractInfo has unknown type, set and store it if typeFromContext != bchain.UnknownTokenStandard && contractInfo.Standard == bchain.UnknownTokenStandard { contractInfo.Standard = typeFromContext + contractInfo.Type = typeFromContext err = d.db.PutCF(d.wo, d.cfh[cfContracts], contract, packContractInfo(contractInfo)) if err != nil { return nil, err @@ -1259,7 +1259,7 @@ func (d *RocksDB) disconnectAddress(btxID []byte, internal bool, addrDesc bchain index = transferTo } addToContract(addrContract, contractIndex, index, btxContract.contract, &bchain.TokenTransfer{ - Type: btxContract.transferType, + Standard: btxContract.transferType, Value: btxContract.value, MultiTokenValues: btxContract.idValues, }, false) diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index c08c5e3eaa..6dc2303ab8 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -756,13 +756,13 @@ func Test_packUnpackAddrContracts(t *testing.T) { InternalTxs: 8873, Contracts: []AddrContract{ { - Type: bchain.FungibleToken, + Standard: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract0d, parser), Txs: 8, Value: *big.NewInt(793201132), }, { - Type: bchain.NonFungibleToken, + Standard: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Txs: 41235, Ids: Ids{ @@ -774,7 +774,7 @@ func Test_packUnpackAddrContracts(t *testing.T) { }, }, { - Type: bchain.MultiToken, + Standard: bchain.MultiToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), Txs: 64, MultiTokenValues: MultiTokenValues{ @@ -830,8 +830,8 @@ func Test_addToContracts(t *testing.T) { index: 1, contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.FungibleToken, - Value: *big.NewInt(123456), + Standard: bchain.FungibleToken, + Value: *big.NewInt(123456), }, addTxCount: true, }, @@ -839,7 +839,7 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.FungibleToken, + Standard: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Txs: 1, Value: *big.NewInt(123456), @@ -853,8 +853,8 @@ func Test_addToContracts(t *testing.T) { index: ^1, contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.FungibleToken, - Value: *big.NewInt(23456), + Standard: bchain.FungibleToken, + Value: *big.NewInt(23456), }, addTxCount: true, }, @@ -862,7 +862,7 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.FungibleToken, + Standard: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, @@ -876,8 +876,8 @@ func Test_addToContracts(t *testing.T) { index: 1, contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.NonFungibleToken, - Value: *big.NewInt(1), + Standard: bchain.NonFungibleToken, + Value: *big.NewInt(1), }, addTxCount: true, }, @@ -885,13 +885,13 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.FungibleToken, + Standard: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.NonFungibleToken, + Standard: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 1, Ids: Ids{*big.NewInt(1)}, @@ -905,8 +905,8 @@ func Test_addToContracts(t *testing.T) { index: 1, contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.NonFungibleToken, - Value: *big.NewInt(2), + Standard: bchain.NonFungibleToken, + Value: *big.NewInt(2), }, addTxCount: true, }, @@ -914,13 +914,13 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.FungibleToken, + Standard: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.NonFungibleToken, + Standard: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 2, Ids: Ids{*big.NewInt(1), *big.NewInt(2)}, @@ -934,8 +934,8 @@ func Test_addToContracts(t *testing.T) { index: ^1, contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.NonFungibleToken, - Value: *big.NewInt(1), + Standard: bchain.NonFungibleToken, + Value: *big.NewInt(1), }, addTxCount: false, }, @@ -943,13 +943,13 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.FungibleToken, + Standard: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.NonFungibleToken, + Standard: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 2, Ids: Ids{*big.NewInt(2)}, @@ -963,7 +963,7 @@ func Test_addToContracts(t *testing.T) { index: 1, contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.MultiToken, + Standard: bchain.MultiToken, MultiTokenValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(11), @@ -977,19 +977,19 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.FungibleToken, + Standard: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.NonFungibleToken, + Standard: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 2, Ids: Ids{*big.NewInt(2)}, }, { - Type: bchain.MultiToken, + Standard: bchain.MultiToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), Txs: 1, MultiTokenValues: MultiTokenValues{ @@ -1008,7 +1008,7 @@ func Test_addToContracts(t *testing.T) { index: 1, contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.MultiToken, + Standard: bchain.MultiToken, MultiTokenValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(11), @@ -1026,19 +1026,19 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.FungibleToken, + Standard: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.NonFungibleToken, + Standard: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 2, Ids: Ids{*big.NewInt(2)}, }, { - Type: bchain.MultiToken, + Standard: bchain.MultiToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), Txs: 2, MultiTokenValues: MultiTokenValues{ @@ -1061,7 +1061,7 @@ func Test_addToContracts(t *testing.T) { index: ^1, contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), transfer: &bchain.TokenTransfer{ - Type: bchain.MultiToken, + Standard: bchain.MultiToken, MultiTokenValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(11), @@ -1079,19 +1079,19 @@ func Test_addToContracts(t *testing.T) { wantAddrContracts: &AddrContracts{ Contracts: []AddrContract{ { - Type: bchain.FungibleToken, + Standard: bchain.FungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), Value: *big.NewInt(100000), Txs: 2, }, { - Type: bchain.NonFungibleToken, + Standard: bchain.NonFungibleToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), Txs: 2, Ids: Ids{*big.NewInt(2)}, }, { - Type: bchain.MultiToken, + Standard: bchain.MultiToken, Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), Txs: 3, MultiTokenValues: MultiTokenValues{ @@ -1112,7 +1112,7 @@ func Test_addToContracts(t *testing.T) { contractIndex = len(addrContracts.Contracts) addrContracts.Contracts = append(addrContracts.Contracts, AddrContract{ Contract: tt.args.contract, - Type: tt.args.transfer.Type, + Standard: tt.args.transfer.Standard, }) } if got := addToContract(&addrContracts.Contracts[contractIndex], contractIndex, tt.args.index, tt.args.contract, tt.args.transfer, tt.args.addTxCount); got != tt.wantIndex { @@ -1269,7 +1269,8 @@ func Test_packUnpackContractInfo(t *testing.T) { { name: "unknown", contractInfo: bchain.ContractInfo{ - Type: bchain.UnknownTokenType, + Type: bchain.UnknownTokenStandard, + Standard: bchain.UnknownTokenStandard, Name: "Test contract", Symbol: "TCT", Decimals: 18, @@ -1280,7 +1281,8 @@ func Test_packUnpackContractInfo(t *testing.T) { { name: "ERC20", contractInfo: bchain.ContractInfo{ - Type: bchain.ERC20TokenType, + Type: bchain.ERC20TokenStandard, + Standard: bchain.ERC20TokenStandard, Name: "GreenContract🟢", Symbol: "🟢", Decimals: 0, diff --git a/server/internal.go b/server/internal.go index 2544c05bd9..e440fbd8e6 100644 --- a/server/internal.go +++ b/server/internal.go @@ -242,7 +242,7 @@ func (s *InternalServer) apiContractInfo(r *http.Request, apiVersion int) (inter return nil, api.NewAPIError("Missing contract address", true) } - contractInfo, valid, err := s.api.GetContractInfo(contractAddress, bchain.UnknownTokenType) + contractInfo, valid, err := s.api.GetContractInfo(contractAddress, bchain.UnknownTokenStandard) if err != nil { return nil, api.NewAPIError(err.Error(), true) } diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index a0714f4706..89ca59e6db 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -51,7 +51,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { r: newGetRequest(ts.URL + "/nft/" + dbtestdata.EthAddrContractCd + "/" + "1"), status: http.StatusOK, contentType: "text/html; charset=utf-8", - body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
Contract typeERC20
`}, + body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
StandardERC20
`}, }, { name: "apiIndex", @@ -72,7 +72,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":12,"balance":"1000075074"}]}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","standard":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","standard":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":12,"balance":"1000075074"}]}`, }, }, { @@ -81,7 +81,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"}],"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"},"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9":{"Type":"Contract","Alias":"Contract 205"}}}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","standard":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","standard":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","standard":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","standard":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","standard":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","standard":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","standard":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]},{"type":"ERC20","standard":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"}],"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"},"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9":{"Type":"Contract","Alias":"Contract 205"}}}`, }, }, { @@ -90,7 +90,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}},"addressAliases":{"0x20cD153de35D469BA46127A0C8F18626b59a256A":{"Type":"ENS","Alias":"address20.eth"},"0x4af4114F73d1c1C903aC9E0361b379D1291808A2":{"Type":"Contract","Alias":"Contract 74"}}}`, + `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","standard":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}},"addressAliases":{"0x20cD153de35D469BA46127A0C8F18626b59a256A":{"Type":"ENS","Alias":"address20.eth"},"0x4af4114F73d1c1C903aC9E0361b379D1291808A2":{"Type":"Contract","Alias":"Contract 74"}}}`, }, }, { diff --git a/server/public_test.go b/server/public_test.go index 550afefe52..bea3e69024 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -676,7 +676,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"txids":["3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"],"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"}]}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"txids":["3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"],"usedTokens":2,"tokens":[{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"}]}`, }, }, { @@ -685,7 +685,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"txids":["3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"],"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"}]}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"txids":["3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"],"usedTokens":2,"tokens":[{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"}]}`, }, }, { @@ -694,7 +694,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"txids":["3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"],"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuWrWMzoBt8VDFNvPmpJf42M1GTUs85fPx","path":"m/49'/1'/33'/0/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuVZ2Ca6Da9zmYynt49Rx7uikAgubGcymF","path":"m/49'/1'/33'/0/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzRGWDUmrPP9HwYu4B43QGCTLwoop5cExa","path":"m/49'/1'/33'/0/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5C9EEWJzyBXhpyPHqa3UNed73Amsi5b3L","path":"m/49'/1'/33'/0/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzNawz2zjwq1L85GDE3YydEJGJYfXxaWkk","path":"m/49'/1'/33'/0/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7NdeuAMgL57WE7QCeV2gTWi2Um8iAu5dA","path":"m/49'/1'/33'/0/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8JQEP6DSHEZHNsSDPA1gHMUq9YFndhkfV","path":"m/49'/1'/33'/0/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mvbn3YXqKZVpQKugaoQrfjSYPvz76RwZkC","path":"m/49'/1'/33'/0/13","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8MRNxCfwUY9TSW27X9ooGYtqgrGCfLRHx","path":"m/49'/1'/33'/0/14","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6HvwrHC113KYZAmCtJ9XJNWgaTcnFunCM","path":"m/49'/1'/33'/0/15","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEo3oNyHUoi7rmRWee7wki37jxPWsWCopJ","path":"m/49'/1'/33'/0/16","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mzm5KY8qdFbDHsQfy4akXbFvbR3FAwDuVo","path":"m/49'/1'/33'/0/17","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NGMwftmQCogp6XZNGvgiybz3WZysvsJzqC","path":"m/49'/1'/33'/0/18","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3fJrrefndYjLGycvFFfYgevpZtcRKCkRD","path":"m/49'/1'/33'/0/19","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1T7TnHBwfdpBoyw53EGUL7vuJmb2mU6jF","path":"m/49'/1'/33'/0/20","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"},{"type":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7HexL4dyAQc7Th4iqcCW4hZuyiZsLWf74","path":"m/49'/1'/33'/1/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NF6X5FDGWrQj4nQrfP6hA77zB5WAc1DGup","path":"m/49'/1'/33'/1/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4ZRPdvc7BVioBTohy4F6QtxreqcjNj26b","path":"m/49'/1'/33'/1/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mtfho1rLmevh4qTnkYWxZEFCWteDMtTcUF","path":"m/49'/1'/33'/1/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFUCphKYvmMcNZRZrF261mRX6iADVB9Qms","path":"m/49'/1'/33'/1/13","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5kBNMB8qgxE4Y4f8J19fScsE49J4aNvoJ","path":"m/49'/1'/33'/1/14","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NANWCaefhCKdXMcW8NbZnnrFRDvhJN2wPy","path":"m/49'/1'/33'/1/15","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFHw7Yo2Bz8D2wGAYHW9qidbZFLpfJ72qB","path":"m/49'/1'/33'/1/16","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBDSsBgy5PpFniLCb1eAFHcSxgxwPSDsZa","path":"m/49'/1'/33'/1/17","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NDWCSQHogc7sCuc2WoYt9PX2i2i6a5k6dX","path":"m/49'/1'/33'/1/18","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8vNyDP7iSDjm3BKpXrbDjAxyphqfvnJz8","path":"m/49'/1'/33'/1/19","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4tFKLurSbMusAyq1tv4tzymVjveAFV1Vb","path":"m/49'/1'/33'/1/20","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBx5WwjAr2cH6Yqrp3Vsf957HtRKwDUVdX","path":"m/49'/1'/33'/1/21","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBu1seHTaFhQxbcW5L5BkZzqFLGmZqpxsa","path":"m/49'/1'/33'/1/22","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NCDLoea22jGsXuarfT1n2QyCUh6RFhAPnT","path":"m/49'/1'/33'/1/23","transfers":0,"decimals":8}]}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"txids":["3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"],"usedTokens":2,"tokens":[{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuWrWMzoBt8VDFNvPmpJf42M1GTUs85fPx","path":"m/49'/1'/33'/0/6","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuVZ2Ca6Da9zmYynt49Rx7uikAgubGcymF","path":"m/49'/1'/33'/0/7","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzRGWDUmrPP9HwYu4B43QGCTLwoop5cExa","path":"m/49'/1'/33'/0/8","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5C9EEWJzyBXhpyPHqa3UNed73Amsi5b3L","path":"m/49'/1'/33'/0/9","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzNawz2zjwq1L85GDE3YydEJGJYfXxaWkk","path":"m/49'/1'/33'/0/10","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N7NdeuAMgL57WE7QCeV2gTWi2Um8iAu5dA","path":"m/49'/1'/33'/0/11","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8JQEP6DSHEZHNsSDPA1gHMUq9YFndhkfV","path":"m/49'/1'/33'/0/12","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mvbn3YXqKZVpQKugaoQrfjSYPvz76RwZkC","path":"m/49'/1'/33'/0/13","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8MRNxCfwUY9TSW27X9ooGYtqgrGCfLRHx","path":"m/49'/1'/33'/0/14","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6HvwrHC113KYZAmCtJ9XJNWgaTcnFunCM","path":"m/49'/1'/33'/0/15","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEo3oNyHUoi7rmRWee7wki37jxPWsWCopJ","path":"m/49'/1'/33'/0/16","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mzm5KY8qdFbDHsQfy4akXbFvbR3FAwDuVo","path":"m/49'/1'/33'/0/17","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NGMwftmQCogp6XZNGvgiybz3WZysvsJzqC","path":"m/49'/1'/33'/0/18","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N3fJrrefndYjLGycvFFfYgevpZtcRKCkRD","path":"m/49'/1'/33'/0/19","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N1T7TnHBwfdpBoyw53EGUL7vuJmb2mU6jF","path":"m/49'/1'/33'/0/20","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N7HexL4dyAQc7Th4iqcCW4hZuyiZsLWf74","path":"m/49'/1'/33'/1/9","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NF6X5FDGWrQj4nQrfP6hA77zB5WAc1DGup","path":"m/49'/1'/33'/1/10","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4ZRPdvc7BVioBTohy4F6QtxreqcjNj26b","path":"m/49'/1'/33'/1/11","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mtfho1rLmevh4qTnkYWxZEFCWteDMtTcUF","path":"m/49'/1'/33'/1/12","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NFUCphKYvmMcNZRZrF261mRX6iADVB9Qms","path":"m/49'/1'/33'/1/13","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5kBNMB8qgxE4Y4f8J19fScsE49J4aNvoJ","path":"m/49'/1'/33'/1/14","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NANWCaefhCKdXMcW8NbZnnrFRDvhJN2wPy","path":"m/49'/1'/33'/1/15","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NFHw7Yo2Bz8D2wGAYHW9qidbZFLpfJ72qB","path":"m/49'/1'/33'/1/16","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NBDSsBgy5PpFniLCb1eAFHcSxgxwPSDsZa","path":"m/49'/1'/33'/1/17","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NDWCSQHogc7sCuc2WoYt9PX2i2i6a5k6dX","path":"m/49'/1'/33'/1/18","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8vNyDP7iSDjm3BKpXrbDjAxyphqfvnJz8","path":"m/49'/1'/33'/1/19","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4tFKLurSbMusAyq1tv4tzymVjveAFV1Vb","path":"m/49'/1'/33'/1/20","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NBx5WwjAr2cH6Yqrp3Vsf957HtRKwDUVdX","path":"m/49'/1'/33'/1/21","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NBu1seHTaFhQxbcW5L5BkZzqFLGmZqpxsa","path":"m/49'/1'/33'/1/22","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NCDLoea22jGsXuarfT1n2QyCUh6RFhAPnT","path":"m/49'/1'/33'/1/23","transfers":0,"decimals":8}]}`, }, }, { @@ -703,7 +703,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej","balance":"0","totalReceived":"0","totalSent":"0","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":0,"tokens":[{"type":"XPUBAddress","name":"tb1pswrqtykue8r89t9u4rprjs0gt4qzkdfuursfnvqaa3f2yql07zmq8s8a5u","path":"m/86'/1'/0'/0/0","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"tb1p8tvmvsvhsee73rhym86wt435qrqm92psfsyhy6a3n5gw455znnpqm8wald","path":"m/86'/1'/0'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"tb1p537ddhyuydg5c2v75xxmn6ac64yz4xns2x0gpdcwj5vzzzgrywlqlqwk43","path":"m/86'/1'/0'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"tb1pn2d0yjeedavnkd8z8lhm566p0f2utm3lgvxrsdehnl94y34txmts5s7t4c","path":"m/86'/1'/0'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"tb1p0pnd6ue5vryymvd28aeq3kdz6rmsdjqrq6eespgtg8wdgnxjzjksujhq4u","path":"m/86'/1'/0'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"tb1p29gpmd96hhgf7wj2vs03ca7x2xx39g8t6e0p55h2d5ssqs4fsj8qtx00wc","path":"m/86'/1'/0'/1/2","transfers":0,"decimals":8}]}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1}/*)#4rqwxvej","balance":"0","totalReceived":"0","totalSent":"0","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":0,"tokens":[{"type":"XPUBAddress","standard":"XPUBAddress","name":"tb1pswrqtykue8r89t9u4rprjs0gt4qzkdfuursfnvqaa3f2yql07zmq8s8a5u","path":"m/86'/1'/0'/0/0","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"tb1p8tvmvsvhsee73rhym86wt435qrqm92psfsyhy6a3n5gw455znnpqm8wald","path":"m/86'/1'/0'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"tb1p537ddhyuydg5c2v75xxmn6ac64yz4xns2x0gpdcwj5vzzzgrywlqlqwk43","path":"m/86'/1'/0'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"tb1pn2d0yjeedavnkd8z8lhm566p0f2utm3lgvxrsdehnl94y34txmts5s7t4c","path":"m/86'/1'/0'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"tb1p0pnd6ue5vryymvd28aeq3kdz6rmsdjqrq6eespgtg8wdgnxjzjksujhq4u","path":"m/86'/1'/0'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"tb1p29gpmd96hhgf7wj2vs03ca7x2xx39g8t6e0p55h2d5ssqs4fsj8qtx00wc","path":"m/86'/1'/0'/1/2","transfers":0,"decimals":8}]}`, }, }, { @@ -721,7 +721,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":3,"addrTxCount":3,"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8}]}`, + `{"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":3,"addrTxCount":3,"usedTokens":2,"tokens":[{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8}]}`, }, }, { @@ -730,7 +730,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":3,"addrTxCount":3,"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"}]}`, + `{"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":3,"addrTxCount":3,"usedTokens":2,"tokens":[{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"}]}`, }, }, { @@ -739,7 +739,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":3,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"transactions":[{"txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","vin":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","n":0,"addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true,"value":"317283951061"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":1,"n":1,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"isOwn":true,"value":"1"}],"vout":[{"value":"118641975500","n":0,"hex":"a91495e9fbe306449c991d314afe3c3567d5bf78efd287","addresses":["2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu"],"isAddress":true,"isOwn":true},{"value":"198641975500","n":1,"hex":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","addresses":["mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"317283951000","valueIn":"317283951062","fees":"62"}],"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"},{"type":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8}]}`, + `{"page":1,"totalPages":1,"itemsOnPage":3,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"transactions":[{"txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","vin":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","n":0,"addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true,"value":"317283951061"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":1,"n":1,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"isOwn":true,"value":"1"}],"vout":[{"value":"118641975500","n":0,"hex":"a91495e9fbe306449c991d314afe3c3567d5bf78efd287","addresses":["2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu"],"isAddress":true,"isOwn":true},{"value":"198641975500","n":1,"hex":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","addresses":["mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"317283951000","valueIn":"317283951062","fees":"62"}],"usedTokens":2,"tokens":[{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8}]}`, }, }, { @@ -1060,7 +1060,7 @@ var websocketTestsBitcoinType = []websocketTest{ "details": "txs", }, }, - want: `{"id":"2","data":{"page":1,"totalPages":1,"itemsOnPage":25,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"transactions":[{"txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","vin":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","n":0,"addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true,"value":"317283951061"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":1,"n":1,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"isOwn":true,"value":"1"}],"vout":[{"value":"118641975500","n":0,"hex":"a91495e9fbe306449c991d314afe3c3567d5bf78efd287","addresses":["2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu"],"isAddress":true,"isOwn":true},{"value":"198641975500","n":1,"hex":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","addresses":["mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"317283951000","valueIn":"317283951062","fees":"62"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vin":[],"vout":[{"value":"1234567890123","n":0,"spent":true,"hex":"76a914a08eae93007f22668ab5e4a9c83c8cd1c325e3e088ac","addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true},{"value":"1","n":1,"spent":true,"hex":"a91452724c5178682f70e0ba31c6ec0633755a3b41d987","addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"isOwn":true},{"value":"9876","n":2,"spent":true,"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"1234567900000","valueIn":"0","fees":"0"}],"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuWrWMzoBt8VDFNvPmpJf42M1GTUs85fPx","path":"m/49'/1'/33'/0/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuVZ2Ca6Da9zmYynt49Rx7uikAgubGcymF","path":"m/49'/1'/33'/0/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzRGWDUmrPP9HwYu4B43QGCTLwoop5cExa","path":"m/49'/1'/33'/0/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5C9EEWJzyBXhpyPHqa3UNed73Amsi5b3L","path":"m/49'/1'/33'/0/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzNawz2zjwq1L85GDE3YydEJGJYfXxaWkk","path":"m/49'/1'/33'/0/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7NdeuAMgL57WE7QCeV2gTWi2Um8iAu5dA","path":"m/49'/1'/33'/0/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8JQEP6DSHEZHNsSDPA1gHMUq9YFndhkfV","path":"m/49'/1'/33'/0/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mvbn3YXqKZVpQKugaoQrfjSYPvz76RwZkC","path":"m/49'/1'/33'/0/13","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8MRNxCfwUY9TSW27X9ooGYtqgrGCfLRHx","path":"m/49'/1'/33'/0/14","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6HvwrHC113KYZAmCtJ9XJNWgaTcnFunCM","path":"m/49'/1'/33'/0/15","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEo3oNyHUoi7rmRWee7wki37jxPWsWCopJ","path":"m/49'/1'/33'/0/16","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mzm5KY8qdFbDHsQfy4akXbFvbR3FAwDuVo","path":"m/49'/1'/33'/0/17","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NGMwftmQCogp6XZNGvgiybz3WZysvsJzqC","path":"m/49'/1'/33'/0/18","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3fJrrefndYjLGycvFFfYgevpZtcRKCkRD","path":"m/49'/1'/33'/0/19","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1T7TnHBwfdpBoyw53EGUL7vuJmb2mU6jF","path":"m/49'/1'/33'/0/20","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"},{"type":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7HexL4dyAQc7Th4iqcCW4hZuyiZsLWf74","path":"m/49'/1'/33'/1/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NF6X5FDGWrQj4nQrfP6hA77zB5WAc1DGup","path":"m/49'/1'/33'/1/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4ZRPdvc7BVioBTohy4F6QtxreqcjNj26b","path":"m/49'/1'/33'/1/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mtfho1rLmevh4qTnkYWxZEFCWteDMtTcUF","path":"m/49'/1'/33'/1/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFUCphKYvmMcNZRZrF261mRX6iADVB9Qms","path":"m/49'/1'/33'/1/13","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5kBNMB8qgxE4Y4f8J19fScsE49J4aNvoJ","path":"m/49'/1'/33'/1/14","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NANWCaefhCKdXMcW8NbZnnrFRDvhJN2wPy","path":"m/49'/1'/33'/1/15","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFHw7Yo2Bz8D2wGAYHW9qidbZFLpfJ72qB","path":"m/49'/1'/33'/1/16","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBDSsBgy5PpFniLCb1eAFHcSxgxwPSDsZa","path":"m/49'/1'/33'/1/17","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NDWCSQHogc7sCuc2WoYt9PX2i2i6a5k6dX","path":"m/49'/1'/33'/1/18","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8vNyDP7iSDjm3BKpXrbDjAxyphqfvnJz8","path":"m/49'/1'/33'/1/19","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4tFKLurSbMusAyq1tv4tzymVjveAFV1Vb","path":"m/49'/1'/33'/1/20","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBx5WwjAr2cH6Yqrp3Vsf957HtRKwDUVdX","path":"m/49'/1'/33'/1/21","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBu1seHTaFhQxbcW5L5BkZzqFLGmZqpxsa","path":"m/49'/1'/33'/1/22","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NCDLoea22jGsXuarfT1n2QyCUh6RFhAPnT","path":"m/49'/1'/33'/1/23","transfers":0,"decimals":8}]}}`, + want: `{"id":"2","data":{"page":1,"totalPages":1,"itemsOnPage":25,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"transactions":[{"txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","vin":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","n":0,"addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true,"value":"317283951061"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":1,"n":1,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"isOwn":true,"value":"1"}],"vout":[{"value":"118641975500","n":0,"hex":"a91495e9fbe306449c991d314afe3c3567d5bf78efd287","addresses":["2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu"],"isAddress":true,"isOwn":true},{"value":"198641975500","n":1,"hex":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","addresses":["mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"317283951000","valueIn":"317283951062","fees":"62"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vin":[],"vout":[{"value":"1234567890123","n":0,"spent":true,"hex":"76a914a08eae93007f22668ab5e4a9c83c8cd1c325e3e088ac","addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true},{"value":"1","n":1,"spent":true,"hex":"a91452724c5178682f70e0ba31c6ec0633755a3b41d987","addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"isOwn":true},{"value":"9876","n":2,"spent":true,"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"1234567900000","valueIn":"0","fees":"0"}],"usedTokens":2,"tokens":[{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuWrWMzoBt8VDFNvPmpJf42M1GTUs85fPx","path":"m/49'/1'/33'/0/6","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuVZ2Ca6Da9zmYynt49Rx7uikAgubGcymF","path":"m/49'/1'/33'/0/7","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzRGWDUmrPP9HwYu4B43QGCTLwoop5cExa","path":"m/49'/1'/33'/0/8","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5C9EEWJzyBXhpyPHqa3UNed73Amsi5b3L","path":"m/49'/1'/33'/0/9","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzNawz2zjwq1L85GDE3YydEJGJYfXxaWkk","path":"m/49'/1'/33'/0/10","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N7NdeuAMgL57WE7QCeV2gTWi2Um8iAu5dA","path":"m/49'/1'/33'/0/11","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8JQEP6DSHEZHNsSDPA1gHMUq9YFndhkfV","path":"m/49'/1'/33'/0/12","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mvbn3YXqKZVpQKugaoQrfjSYPvz76RwZkC","path":"m/49'/1'/33'/0/13","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8MRNxCfwUY9TSW27X9ooGYtqgrGCfLRHx","path":"m/49'/1'/33'/0/14","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6HvwrHC113KYZAmCtJ9XJNWgaTcnFunCM","path":"m/49'/1'/33'/0/15","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEo3oNyHUoi7rmRWee7wki37jxPWsWCopJ","path":"m/49'/1'/33'/0/16","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mzm5KY8qdFbDHsQfy4akXbFvbR3FAwDuVo","path":"m/49'/1'/33'/0/17","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NGMwftmQCogp6XZNGvgiybz3WZysvsJzqC","path":"m/49'/1'/33'/0/18","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N3fJrrefndYjLGycvFFfYgevpZtcRKCkRD","path":"m/49'/1'/33'/0/19","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N1T7TnHBwfdpBoyw53EGUL7vuJmb2mU6jF","path":"m/49'/1'/33'/0/20","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N7HexL4dyAQc7Th4iqcCW4hZuyiZsLWf74","path":"m/49'/1'/33'/1/9","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NF6X5FDGWrQj4nQrfP6hA77zB5WAc1DGup","path":"m/49'/1'/33'/1/10","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4ZRPdvc7BVioBTohy4F6QtxreqcjNj26b","path":"m/49'/1'/33'/1/11","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mtfho1rLmevh4qTnkYWxZEFCWteDMtTcUF","path":"m/49'/1'/33'/1/12","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NFUCphKYvmMcNZRZrF261mRX6iADVB9Qms","path":"m/49'/1'/33'/1/13","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5kBNMB8qgxE4Y4f8J19fScsE49J4aNvoJ","path":"m/49'/1'/33'/1/14","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NANWCaefhCKdXMcW8NbZnnrFRDvhJN2wPy","path":"m/49'/1'/33'/1/15","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NFHw7Yo2Bz8D2wGAYHW9qidbZFLpfJ72qB","path":"m/49'/1'/33'/1/16","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NBDSsBgy5PpFniLCb1eAFHcSxgxwPSDsZa","path":"m/49'/1'/33'/1/17","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NDWCSQHogc7sCuc2WoYt9PX2i2i6a5k6dX","path":"m/49'/1'/33'/1/18","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8vNyDP7iSDjm3BKpXrbDjAxyphqfvnJz8","path":"m/49'/1'/33'/1/19","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4tFKLurSbMusAyq1tv4tzymVjveAFV1Vb","path":"m/49'/1'/33'/1/20","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NBx5WwjAr2cH6Yqrp3Vsf957HtRKwDUVdX","path":"m/49'/1'/33'/1/21","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NBu1seHTaFhQxbcW5L5BkZzqFLGmZqpxsa","path":"m/49'/1'/33'/1/22","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NCDLoea22jGsXuarfT1n2QyCUh6RFhAPnT","path":"m/49'/1'/33'/1/23","transfers":0,"decimals":8}]}}`, }, { name: "websocket getAccountInfo address", @@ -1084,7 +1084,7 @@ var websocketTestsBitcoinType = []websocketTest{ "gap": 10, }, }, - want: `{"id":"4","data":{"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":3,"addrTxCount":3,"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8},{"type":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuWrWMzoBt8VDFNvPmpJf42M1GTUs85fPx","path":"m/49'/1'/33'/0/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuVZ2Ca6Da9zmYynt49Rx7uikAgubGcymF","path":"m/49'/1'/33'/0/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzRGWDUmrPP9HwYu4B43QGCTLwoop5cExa","path":"m/49'/1'/33'/0/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5C9EEWJzyBXhpyPHqa3UNed73Amsi5b3L","path":"m/49'/1'/33'/0/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzNawz2zjwq1L85GDE3YydEJGJYfXxaWkk","path":"m/49'/1'/33'/0/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8},{"type":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7HexL4dyAQc7Th4iqcCW4hZuyiZsLWf74","path":"m/49'/1'/33'/1/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NF6X5FDGWrQj4nQrfP6hA77zB5WAc1DGup","path":"m/49'/1'/33'/1/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4ZRPdvc7BVioBTohy4F6QtxreqcjNj26b","path":"m/49'/1'/33'/1/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mtfho1rLmevh4qTnkYWxZEFCWteDMtTcUF","path":"m/49'/1'/33'/1/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFUCphKYvmMcNZRZrF261mRX6iADVB9Qms","path":"m/49'/1'/33'/1/13","transfers":0,"decimals":8}]}}`, + want: `{"id":"4","data":{"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":3,"addrTxCount":3,"usedTokens":2,"tokens":[{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuWrWMzoBt8VDFNvPmpJf42M1GTUs85fPx","path":"m/49'/1'/33'/0/6","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuVZ2Ca6Da9zmYynt49Rx7uikAgubGcymF","path":"m/49'/1'/33'/0/7","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzRGWDUmrPP9HwYu4B43QGCTLwoop5cExa","path":"m/49'/1'/33'/0/8","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5C9EEWJzyBXhpyPHqa3UNed73Amsi5b3L","path":"m/49'/1'/33'/0/9","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzNawz2zjwq1L85GDE3YydEJGJYfXxaWkk","path":"m/49'/1'/33'/0/10","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N7HexL4dyAQc7Th4iqcCW4hZuyiZsLWf74","path":"m/49'/1'/33'/1/9","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NF6X5FDGWrQj4nQrfP6hA77zB5WAc1DGup","path":"m/49'/1'/33'/1/10","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4ZRPdvc7BVioBTohy4F6QtxreqcjNj26b","path":"m/49'/1'/33'/1/11","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mtfho1rLmevh4qTnkYWxZEFCWteDMtTcUF","path":"m/49'/1'/33'/1/12","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NFUCphKYvmMcNZRZrF261mRX6iADVB9Qms","path":"m/49'/1'/33'/1/13","transfers":0,"decimals":8}]}}`, }, { name: "websocket getAccountUtxo", @@ -1713,7 +1713,7 @@ var websocketTestsBitcoinTypeExtendedIndex = []websocketTest{ "details": "txs", }, }, - want: `{"id":"2","data":{"page":1,"totalPages":1,"itemsOnPage":25,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"transactions":[{"txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","vin":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","n":0,"addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true,"value":"317283951061"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":1,"n":1,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"isOwn":true,"value":"1"}],"vout":[{"value":"118641975500","n":0,"hex":"a91495e9fbe306449c991d314afe3c3567d5bf78efd287","addresses":["2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu"],"isAddress":true,"isOwn":true},{"value":"198641975500","n":1,"hex":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","addresses":["mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"317283951000","valueIn":"317283951062","fees":"62"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vin":[],"vout":[{"value":"1234567890123","n":0,"spent":true,"spentTxId":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","spentHeight":225494,"hex":"76a914a08eae93007f22668ab5e4a9c83c8cd1c325e3e088ac","addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true},{"value":"1","n":1,"spent":true,"spentTxId":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","spentIndex":1,"spentHeight":225494,"hex":"a91452724c5178682f70e0ba31c6ec0633755a3b41d987","addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"isOwn":true},{"value":"9876","n":2,"spent":true,"spentTxId":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","spentHeight":225494,"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"1234567900000","valueIn":"0","fees":"0"}],"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuWrWMzoBt8VDFNvPmpJf42M1GTUs85fPx","path":"m/49'/1'/33'/0/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuVZ2Ca6Da9zmYynt49Rx7uikAgubGcymF","path":"m/49'/1'/33'/0/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzRGWDUmrPP9HwYu4B43QGCTLwoop5cExa","path":"m/49'/1'/33'/0/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5C9EEWJzyBXhpyPHqa3UNed73Amsi5b3L","path":"m/49'/1'/33'/0/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzNawz2zjwq1L85GDE3YydEJGJYfXxaWkk","path":"m/49'/1'/33'/0/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7NdeuAMgL57WE7QCeV2gTWi2Um8iAu5dA","path":"m/49'/1'/33'/0/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8JQEP6DSHEZHNsSDPA1gHMUq9YFndhkfV","path":"m/49'/1'/33'/0/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mvbn3YXqKZVpQKugaoQrfjSYPvz76RwZkC","path":"m/49'/1'/33'/0/13","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8MRNxCfwUY9TSW27X9ooGYtqgrGCfLRHx","path":"m/49'/1'/33'/0/14","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6HvwrHC113KYZAmCtJ9XJNWgaTcnFunCM","path":"m/49'/1'/33'/0/15","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEo3oNyHUoi7rmRWee7wki37jxPWsWCopJ","path":"m/49'/1'/33'/0/16","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mzm5KY8qdFbDHsQfy4akXbFvbR3FAwDuVo","path":"m/49'/1'/33'/0/17","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NGMwftmQCogp6XZNGvgiybz3WZysvsJzqC","path":"m/49'/1'/33'/0/18","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3fJrrefndYjLGycvFFfYgevpZtcRKCkRD","path":"m/49'/1'/33'/0/19","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1T7TnHBwfdpBoyw53EGUL7vuJmb2mU6jF","path":"m/49'/1'/33'/0/20","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"},{"type":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7HexL4dyAQc7Th4iqcCW4hZuyiZsLWf74","path":"m/49'/1'/33'/1/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NF6X5FDGWrQj4nQrfP6hA77zB5WAc1DGup","path":"m/49'/1'/33'/1/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4ZRPdvc7BVioBTohy4F6QtxreqcjNj26b","path":"m/49'/1'/33'/1/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mtfho1rLmevh4qTnkYWxZEFCWteDMtTcUF","path":"m/49'/1'/33'/1/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFUCphKYvmMcNZRZrF261mRX6iADVB9Qms","path":"m/49'/1'/33'/1/13","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5kBNMB8qgxE4Y4f8J19fScsE49J4aNvoJ","path":"m/49'/1'/33'/1/14","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NANWCaefhCKdXMcW8NbZnnrFRDvhJN2wPy","path":"m/49'/1'/33'/1/15","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFHw7Yo2Bz8D2wGAYHW9qidbZFLpfJ72qB","path":"m/49'/1'/33'/1/16","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBDSsBgy5PpFniLCb1eAFHcSxgxwPSDsZa","path":"m/49'/1'/33'/1/17","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NDWCSQHogc7sCuc2WoYt9PX2i2i6a5k6dX","path":"m/49'/1'/33'/1/18","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8vNyDP7iSDjm3BKpXrbDjAxyphqfvnJz8","path":"m/49'/1'/33'/1/19","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4tFKLurSbMusAyq1tv4tzymVjveAFV1Vb","path":"m/49'/1'/33'/1/20","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBx5WwjAr2cH6Yqrp3Vsf957HtRKwDUVdX","path":"m/49'/1'/33'/1/21","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBu1seHTaFhQxbcW5L5BkZzqFLGmZqpxsa","path":"m/49'/1'/33'/1/22","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NCDLoea22jGsXuarfT1n2QyCUh6RFhAPnT","path":"m/49'/1'/33'/1/23","transfers":0,"decimals":8}]}}`, + want: `{"id":"2","data":{"page":1,"totalPages":1,"itemsOnPage":25,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"addrTxCount":3,"transactions":[{"txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","vin":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","n":0,"addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true,"value":"317283951061"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":1,"n":1,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"isOwn":true,"value":"1"}],"vout":[{"value":"118641975500","n":0,"hex":"a91495e9fbe306449c991d314afe3c3567d5bf78efd287","addresses":["2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu"],"isAddress":true,"isOwn":true},{"value":"198641975500","n":1,"hex":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","addresses":["mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"317283951000","valueIn":"317283951062","fees":"62"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vin":[],"vout":[{"value":"1234567890123","n":0,"spent":true,"spentTxId":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","spentHeight":225494,"hex":"76a914a08eae93007f22668ab5e4a9c83c8cd1c325e3e088ac","addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true},{"value":"1","n":1,"spent":true,"spentTxId":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","spentIndex":1,"spentHeight":225494,"hex":"a91452724c5178682f70e0ba31c6ec0633755a3b41d987","addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"isOwn":true},{"value":"9876","n":2,"spent":true,"spentTxId":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","spentHeight":225494,"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"1234567900000","valueIn":"0","fees":"0"}],"usedTokens":2,"tokens":[{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuWrWMzoBt8VDFNvPmpJf42M1GTUs85fPx","path":"m/49'/1'/33'/0/6","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MuVZ2Ca6Da9zmYynt49Rx7uikAgubGcymF","path":"m/49'/1'/33'/0/7","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzRGWDUmrPP9HwYu4B43QGCTLwoop5cExa","path":"m/49'/1'/33'/0/8","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5C9EEWJzyBXhpyPHqa3UNed73Amsi5b3L","path":"m/49'/1'/33'/0/9","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzNawz2zjwq1L85GDE3YydEJGJYfXxaWkk","path":"m/49'/1'/33'/0/10","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N7NdeuAMgL57WE7QCeV2gTWi2Um8iAu5dA","path":"m/49'/1'/33'/0/11","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8JQEP6DSHEZHNsSDPA1gHMUq9YFndhkfV","path":"m/49'/1'/33'/0/12","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mvbn3YXqKZVpQKugaoQrfjSYPvz76RwZkC","path":"m/49'/1'/33'/0/13","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8MRNxCfwUY9TSW27X9ooGYtqgrGCfLRHx","path":"m/49'/1'/33'/0/14","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6HvwrHC113KYZAmCtJ9XJNWgaTcnFunCM","path":"m/49'/1'/33'/0/15","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEo3oNyHUoi7rmRWee7wki37jxPWsWCopJ","path":"m/49'/1'/33'/0/16","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mzm5KY8qdFbDHsQfy4akXbFvbR3FAwDuVo","path":"m/49'/1'/33'/0/17","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NGMwftmQCogp6XZNGvgiybz3WZysvsJzqC","path":"m/49'/1'/33'/0/18","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N3fJrrefndYjLGycvFFfYgevpZtcRKCkRD","path":"m/49'/1'/33'/0/19","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N1T7TnHBwfdpBoyw53EGUL7vuJmb2mU6jF","path":"m/49'/1'/33'/0/20","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N7HexL4dyAQc7Th4iqcCW4hZuyiZsLWf74","path":"m/49'/1'/33'/1/9","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NF6X5FDGWrQj4nQrfP6hA77zB5WAc1DGup","path":"m/49'/1'/33'/1/10","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4ZRPdvc7BVioBTohy4F6QtxreqcjNj26b","path":"m/49'/1'/33'/1/11","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2Mtfho1rLmevh4qTnkYWxZEFCWteDMtTcUF","path":"m/49'/1'/33'/1/12","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NFUCphKYvmMcNZRZrF261mRX6iADVB9Qms","path":"m/49'/1'/33'/1/13","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N5kBNMB8qgxE4Y4f8J19fScsE49J4aNvoJ","path":"m/49'/1'/33'/1/14","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NANWCaefhCKdXMcW8NbZnnrFRDvhJN2wPy","path":"m/49'/1'/33'/1/15","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NFHw7Yo2Bz8D2wGAYHW9qidbZFLpfJ72qB","path":"m/49'/1'/33'/1/16","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NBDSsBgy5PpFniLCb1eAFHcSxgxwPSDsZa","path":"m/49'/1'/33'/1/17","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NDWCSQHogc7sCuc2WoYt9PX2i2i6a5k6dX","path":"m/49'/1'/33'/1/18","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N8vNyDP7iSDjm3BKpXrbDjAxyphqfvnJz8","path":"m/49'/1'/33'/1/19","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2N4tFKLurSbMusAyq1tv4tzymVjveAFV1Vb","path":"m/49'/1'/33'/1/20","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NBx5WwjAr2cH6Yqrp3Vsf957HtRKwDUVdX","path":"m/49'/1'/33'/1/21","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NBu1seHTaFhQxbcW5L5BkZzqFLGmZqpxsa","path":"m/49'/1'/33'/1/22","transfers":0,"decimals":8},{"type":"XPUBAddress","standard":"XPUBAddress","name":"2NCDLoea22jGsXuarfT1n2QyCUh6RFhAPnT","path":"m/49'/1'/33'/1/23","transfers":0,"decimals":8}]}}`, }, { name: "websocket getBlockFilter", diff --git a/static/templates/address.html b/static/templates/address.html index 6549c37728..d2bc9772e6 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -51,10 +51,10 @@

{{$addr.Nonce}} {{if $addr.ContractInfo}} - {{if $addr.ContractInfo.Type}} + {{if $addr.ContractInfo.Standard}} - Contract type - {{$addr.ContractInfo.Type}} + Standard + {{$addr.ContractInfo.Standard}} {{end}} {{if $addr.ContractInfo.CreatedInBlock}} @@ -131,7 +131,7 @@

{{summaryValuesSpa Transfers# {{range $t := $addr.Tokens}} - {{if eq $t.Type $.FungibleTokenName}} + {{if eq $t.Standard $.FungibleTokenName}} {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} {{formattedAmountSpan $t.BalanceSat $t.Decimals $t.Symbol $data "copyable"}} @@ -167,7 +167,7 @@
{{.NonFungibleTokenName}} Tokens Transfers# {{range $t := $addr.Tokens}} - {{if eq $t.Type $.NonFungibleTokenName}} + {{if eq $t.Standard $.NonFungibleTokenName}} {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} @@ -204,7 +204,7 @@
{{.MultiTokenName}} Tokens Transfers# {{range $t := $addr.Tokens}} - {{if eq $t.Type $.MultiTokenName}} + {{if eq $t.Standard $.MultiTokenName}} {{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} @@ -288,17 +288,17 @@

Transactions

{{range $t := $addr.Tokens}} - {{if eq $t.Type $.FungibleTokenName}} + {{if eq $t.Standard $.FungibleTokenName}} {{end}} {{end}} {{range $t := $addr.Tokens}} - {{if eq $t.Type $.NonFungibleTokenName}} + {{if eq $t.Standard $.NonFungibleTokenName}} {{end}} {{end}} {{range $t := $addr.Tokens}} - {{if eq $t.Type $.MultiTokenName}} + {{if eq $t.Standard $.MultiTokenName}} {{end}} {{end}} diff --git a/static/templates/tokenDetail.html b/static/templates/tokenDetail.html index 2bcd02672c..9eec908b4e 100644 --- a/static/templates/tokenDetail.html +++ b/static/templates/tokenDetail.html @@ -21,8 +21,8 @@

NFT Token Detail

{{$data.ContractInfo.Contract}}
{{$data.ContractInfo.Name}} - Contract type - {{$data.ContractInfo.Type}} + Standard + {{$data.ContractInfo.Standard}} diff --git a/static/templates/txdetail_ethereumtype.html b/static/templates/txdetail_ethereumtype.html index e2acaa60dc..7c003e5db7 100644 --- a/static/templates/txdetail_ethereumtype.html +++ b/static/templates/txdetail_ethereumtype.html @@ -100,7 +100,7 @@ {{.FungibleTokenName}} Token Transfers
{{range $tt := $tx.TokenTransfers}} - {{if eq $tt.Type $.FungibleTokenName}} + {{if eq $tt.Standard $.FungibleTokenName}}
@@ -128,7 +128,7 @@ {{.NonFungibleTokenName}} Token Transfers
{{range $tt := $tx.TokenTransfers}} - {{if eq $tt.Type $.NonFungibleTokenName}} + {{if eq $tt.Standard $.NonFungibleTokenName}}
@@ -156,7 +156,7 @@ {{.MultiTokenName}} Token Transfers
{{range $tt := $tx.TokenTransfers}} - {{if eq $tt.Type $.MultiTokenName}} + {{if eq $tt.Standard $.MultiTokenName}}
diff --git a/tests/dbtestdata/dbtestdata_ethereumtype.go b/tests/dbtestdata/dbtestdata_ethereumtype.go index 41cfac5761..b16b60e34b 100644 --- a/tests/dbtestdata/dbtestdata_ethereumtype.go +++ b/tests/dbtestdata/dbtestdata_ethereumtype.go @@ -132,7 +132,7 @@ var Block1SpecificData = &bchain.EthereumBlockSpecificData{ Contracts: []bchain.ContractInfo{ { Contract: EthAddrContract4a, - Type: bchain.ERC20TokenType, + Standard: bchain.ERC20TokenStandard, Name: "Contract 74", Symbol: "S74", Decimals: 12, diff --git a/tests/dbtestdata/fakechain_ethereumtype.go b/tests/dbtestdata/fakechain_ethereumtype.go index 3722ef416a..2b87602795 100644 --- a/tests/dbtestdata/fakechain_ethereumtype.go +++ b/tests/dbtestdata/fakechain_ethereumtype.go @@ -120,7 +120,7 @@ func (c *fakeBlockChainEthereumType) EthereumTypeGetNonce(addrDesc bchain.Addres func (c *fakeBlockChainEthereumType) GetContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.ContractInfo, error) { addresses, _, _ := c.Parser.GetAddressesFromAddrDesc(contractDesc) return &bchain.ContractInfo{ - Type: bchain.ERC20TokenType, + Standard: bchain.ERC20TokenStandard, Contract: addresses[0], Name: "Contract " + strconv.Itoa(int(contractDesc[0])), Symbol: "S" + strconv.Itoa(int(contractDesc[0])), From 2ee55f62db0a7f263a245221984ad44ebf0c39ff Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 20 Jan 2025 21:29:12 +0100 Subject: [PATCH 416/530] Update blockbook-api.ts --- api/types.go | 14 ++++++------ bchain/types_ethereum_type.go | 4 ++-- blockbook-api.ts | 25 +++++++++++++++++----- build/tools/typescriptify/typescriptify.go | 1 + 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/api/types.go b/api/types.go index 305bf37978..0525e3ce50 100644 --- a/api/types.go +++ b/api/types.go @@ -159,14 +159,14 @@ type MultiTokenValue struct { // Token contains info about tokens held by an address type Token struct { // Deprecated: Use Standard instead. - Type bchain.TokenStandardName `json:"type" ts_type:"'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155'"` - Standard bchain.TokenStandardName `json:"standard" ts_type:"'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155'"` + Type bchain.TokenStandardName `json:"type" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'" ts_doc:"@deprecated: Use standard instead."` + Standard bchain.TokenStandardName `json:"standard" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'"` Name string `json:"name"` Path string `json:"path,omitempty"` Contract string `json:"contract,omitempty"` Transfers int `json:"transfers"` Symbol string `json:"symbol,omitempty"` - Decimals int `json:"decimals,omitempty"` + Decimals int `json:"decimals"` BalanceSat *Amount `json:"balance,omitempty"` BaseValue float64 `json:"baseValue,omitempty"` // value in the base currency (ETH for Ethereum) SecondaryValue float64 `json:"secondaryValue,omitempty"` // value in secondary (fiat) currency, if specified @@ -207,14 +207,14 @@ func (a Tokens) Less(i, j int) bool { // TokenTransfer contains info about a token transfer done in a transaction type TokenTransfer struct { // Deprecated: Use Standard instead. - Type bchain.TokenStandardName `json:"type"` - Standard bchain.TokenStandardName `json:"standard"` + Type bchain.TokenStandardName `json:"type" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'" ts_doc:"@deprecated: Use standard instead."` + Standard bchain.TokenStandardName `json:"standard" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'"` From string `json:"from"` To string `json:"to"` Contract string `json:"contract"` Name string `json:"name,omitempty"` Symbol string `json:"symbol,omitempty"` - Decimals int `json:"decimals,omitempty"` + Decimals int `json:"decimals"` Value *Amount `json:"value,omitempty"` MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` } @@ -362,7 +362,7 @@ type Address struct { TotalSecondaryValue float64 `json:"totalSecondaryValue,omitempty"` // value including tokens in secondary currency ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"` // Deprecated: replaced by ContractInfo - Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty"` + Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty" ts_doc:"@deprecated: replaced by contractInfo"` AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` StakingPools []StakingPool `json:"stakingPools,omitempty"` // helpers for explorer diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index 202a14e4e4..31e1702ae8 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -60,8 +60,8 @@ type EthereumInternalData struct { // ContractInfo contains info about a contract type ContractInfo struct { // Deprecated: Use Standard instead. - Type TokenStandardName `json:"type"` - Standard TokenStandardName `json:"standard"` + Type TokenStandardName `json:"type" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'" ts_doc:"@deprecated: Use standard instead."` + Standard TokenStandardName `json:"standard" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'"` Contract string `json:"contract"` Name string `json:"name"` Symbol string `json:"symbol"` diff --git a/blockbook-api.ts b/blockbook-api.ts index 706803f54c..d66954ff58 100644 --- a/blockbook-api.ts +++ b/blockbook-api.ts @@ -46,13 +46,15 @@ export interface MultiTokenValue { value?: string; } export interface TokenTransfer { - type: string; + /** @deprecated: Use standard instead. */ + type: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; + standard: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; from: string; to: string; contract: string; name?: string; symbol?: string; - decimals?: number; + decimals: number; value?: string; multiTokenValues?: MultiTokenValue[]; } @@ -125,7 +127,9 @@ export interface StakingPool { autocompoundBalance: string; } export interface ContractInfo { - type: string; + /** @deprecated: Use standard instead. */ + type: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; + standard: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; contract: string; name: string; symbol: string; @@ -134,13 +138,15 @@ export interface ContractInfo { destructedInBlock?: number; } export interface Token { - type: 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155'; + /** @deprecated: Use standard instead. */ + type: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; + standard: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; name: string; path?: string; contract?: string; transfers: number; symbol?: string; - decimals?: number; + decimals: number; balance?: string; baseValue?: number; secondaryValue?: number; @@ -174,6 +180,7 @@ export interface Address { totalBaseValue?: number; totalSecondaryValue?: number; contractInfo?: ContractInfo; + /** @deprecated: replaced by contractInfo */ erc20Contract?: ContractInfo; addressAliases?: { [key: string]: AddressAlias }; stakingPools?: StakingPool[]; @@ -418,6 +425,8 @@ export interface WsEstimateFeeReq { export interface Eip1559Fee { maxFeePerGas: string; maxPriorityFeePerGas: string; + minWaitTimeEstimate?: number; + maxWaitTimeEstimate?: number; } export interface Eip1559Fees { baseFeePerGas?: string; @@ -425,6 +434,12 @@ export interface Eip1559Fees { medium?: Eip1559Fee; high?: Eip1559Fee; instant?: Eip1559Fee; + networkCongestion?: number; + latestPriorityFeeRange?: string[]; + historicalPriorityFeeRange?: string[]; + historicalBaseFeeRange?: string[]; + priorityFeeTrend?: 'up' | 'down'; + baseFeeTrend?: 'up' | 'down'; } export interface WsEstimateFeeRes { feePerTx?: string; diff --git a/build/tools/typescriptify/typescriptify.go b/build/tools/typescriptify/typescriptify.go index 069572cb9a..8ee0563e31 100644 --- a/build/tools/typescriptify/typescriptify.go +++ b/build/tools/typescriptify/typescriptify.go @@ -19,6 +19,7 @@ func main() { t.ManageType(api.Amount{}, typescriptify.TypeOptions{TSType: "string"}) t.ManageType([]api.Amount{}, typescriptify.TypeOptions{TSType: "string[]"}) + t.ManageType([]*api.Amount{}, typescriptify.TypeOptions{TSType: "string[]"}) t.ManageType(big.Int{}, typescriptify.TypeOptions{TSType: "number"}) t.ManageType(time.Time{}, typescriptify.TypeOptions{TSType: "string", TSDoc: "Time in ISO 8601 YYYY-MM-DDTHH:mm:ss.sssZd"}) From cb17ffa7c80d4c4e82d27339b67ff5fc26e32b0b Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 11 Feb 2025 09:34:27 +0100 Subject: [PATCH 417/530] Add infura fees estimate to Arbitrum, Optimism and Polygon --- configs/coins/arbitrum_archive.json | 3 +++ configs/coins/optimism_archive.json | 4 +++- configs/coins/polygon_archive.json | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/configs/coins/arbitrum_archive.json b/configs/coins/arbitrum_archive.json index c85bb4cb5f..d0add43185 100644 --- a/configs/coins/arbitrum_archive.json +++ b/configs/coins/arbitrum_archive.json @@ -51,6 +51,9 @@ "block_addresses_to_keep": 600, "additional_params": { "address_aliases": true, + "eip1559Fees": true, + "alternative_estimate_fee": "infura", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/42161/suggestedGasFees\", \"periodSeconds\": 60}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, diff --git a/configs/coins/optimism_archive.json b/configs/coins/optimism_archive.json index 1a642f11d5..ab7f2fcd1f 100644 --- a/configs/coins/optimism_archive.json +++ b/configs/coins/optimism_archive.json @@ -53,7 +53,9 @@ "block_addresses_to_keep": 600, "additional_params": { "address_aliases": true, - "mempoolTxTimeoutHours": 48, + "eip1559Fees": true, + "alternative_estimate_fee": "infura", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/10/suggestedGasFees\", \"periodSeconds\": 60}", "processInternalTransactions": true, "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index 6898e89cc5..8a6da7ffb5 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -52,6 +52,9 @@ "block_addresses_to_keep": 600, "additional_params": { "address_aliases": true, + "eip1559Fees": true, + "alternative_estimate_fee": "infura", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/137/suggestedGasFees\", \"periodSeconds\": 60}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, From f665eba5c6cd77e2c4a5c76f2cd02fe1577606b8 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 11 Feb 2025 16:19:33 +0100 Subject: [PATCH 418/530] =?UTF-8?q?polygon-heimdall=201.0.10=20=E2=86=92?= =?UTF-8?q?=201.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/polygon_heimdall.json | 12 ++++++------ configs/coins/polygon_heimdall_archive.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/polygon_heimdall.json b/configs/coins/polygon_heimdall.json index 1486deebab..80a2201450 100644 --- a/configs/coins/polygon_heimdall.json +++ b/configs/coins/polygon_heimdall.json @@ -16,16 +16,16 @@ "package_name": "backend-polygon-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.0.10", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.0.10.tar.gz", + "version": "1.2.0", + "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.0.tar.gz", "verification_type": "sha256", - "verification_source": "9058e054de2a0090e0a8400aa23d6144d7432ac31c6b4e4b6cff684a834e612f", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.0.10.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "8d49e6e9e4115d46ce3cc7ddf2d8ab3c471eb54c6278759ce27b3fdce96cc736", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.0.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.0.10/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -37,4 +37,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/polygon_heimdall_archive.json b/configs/coins/polygon_heimdall_archive.json index 228f42d01a..81fa3f447b 100644 --- a/configs/coins/polygon_heimdall_archive.json +++ b/configs/coins/polygon_heimdall_archive.json @@ -16,16 +16,16 @@ "package_name": "backend-polygon-archive-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.0.10", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.0.10.tar.gz", + "version": "1.2.0", + "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.0.tar.gz", "verification_type": "sha256", - "verification_source": "9058e054de2a0090e0a8400aa23d6144d7432ac31c6b4e4b6cff684a834e612f", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.0.10.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "8d49e6e9e4115d46ce3cc7ddf2d8ab3c471eb54c6278759ce27b3fdce96cc736", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.0.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.0.10/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -37,4 +37,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file From 0360c7932677b6f2b997991916f9793ba1cc5534 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 17 Feb 2025 14:23:51 +0100 Subject: [PATCH 419/530] Set period of infura alternative fee provider for BSC --- configs/coins/bsc_archive.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/coins/bsc_archive.json b/configs/coins/bsc_archive.json index 5261674539..0971b7e09a 100644 --- a/configs/coins/bsc_archive.json +++ b/configs/coins/bsc_archive.json @@ -60,7 +60,7 @@ "address_aliases": true, "eip1559Fees": true, "alternative_estimate_fee": "infura", - "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/56/suggestedGasFees\", \"periodSeconds\": 20}", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/56/suggestedGasFees\", \"periodSeconds\": 60}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, From 4c8562af917f2a292f5ceb2aa5b4d36b27092d0d Mon Sep 17 00:00:00 2001 From: JoHnY Date: Mon, 17 Feb 2025 09:54:19 +0100 Subject: [PATCH 420/530] =?UTF-8?q?polygon-bor=201.5.3=20=E2=86=92=201.5.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/polygon.json | 12 ++++++------ configs/coins/polygon_archive.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/polygon.json b/configs/coins/polygon.json index 71e0c14387..4502e3abb5 100644 --- a/configs/coins/polygon.json +++ b/configs/coins/polygon.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.5.3", - "binary_url": "https://github.com/maticnetwork/bor/archive/refs/tags/v1.5.3.tar.gz", + "version": "1.5.5", + "binary_url": "https://github.com/maticnetwork/bor/archive/refs/tags/v1.5.5.tar.gz", "verification_type": "sha256", - "verification_source": "6dabc3306aa628f86232e96e5ec1a970bbebe38ace09447a0d2e5421dd77e4bd", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.5.3.tar.gz && cd backend/source && make bor && mv build/bin/bor ../ && rm -rf ../source && echo", + "verification_source": "43ed5036b6c337e32b6c49a1e299bea817f1a7e236fffb401fa63f17063492da", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.5.5.tar.gz && cd backend/source && make bor && mv build/bin/bor ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v1.5.3/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v1.5.5/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -63,4 +63,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index 8a6da7ffb5..e3d01c0786 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-archive-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.5.3", - "binary_url": "https://github.com/maticnetwork/bor/archive/refs/tags/v1.5.3.tar.gz", + "version": "1.5.5", + "binary_url": "https://github.com/maticnetwork/bor/archive/refs/tags/v1.5.5.tar.gz", "verification_type": "sha256", - "verification_source": "6dabc3306aa628f86232e96e5ec1a970bbebe38ace09447a0d2e5421dd77e4bd", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.5.3.tar.gz && cd backend/source && make bor && mv build/bin/bor ../ && rm -rf ../source && echo", + "verification_source": "43ed5036b6c337e32b6c49a1e299bea817f1a7e236fffb401fa63f17063492da", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.5.5.tar.gz && cd backend/source && make bor && mv build/bin/bor ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v1.5.3/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v1.5.5/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -69,4 +69,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file From 49910d32a711b02345822436ed542485a894258b Mon Sep 17 00:00:00 2001 From: JoHnY Date: Mon, 17 Feb 2025 10:43:12 +0100 Subject: [PATCH 421/530] =?UTF-8?q?eth=20(+testnets)=202.60.10=20=E2=86=92?= =?UTF-8?q?=202.61.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 12 ++++++------ configs/coins/ethereum_archive.json | 12 ++++++------ configs/coins/ethereum_testnet_holesky.json | 12 ++++++------ configs/coins/ethereum_testnet_holesky_archive.json | 12 ++++++------ configs/coins/ethereum_testnet_sepolia.json | 12 ++++++------ configs/coins/ethereum_testnet_sepolia_archive.json | 12 ++++++------ 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 47316d95f1..24c5a56ac8 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.10", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", + "version": "2.61.1", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", + "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", - "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", + "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" } } }, @@ -73,4 +73,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index f9a868b8b4..b89009dacf 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.10", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", + "version": "2.61.1", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", + "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", - "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", + "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" } } }, @@ -76,4 +76,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index a5ea8e3315..9aa77b3061 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.10", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", + "version": "2.61.1", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", + "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", - "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", + "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" } } }, @@ -68,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index e659c49d39..001974ece2 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.10", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", + "version": "2.61.1", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", + "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", - "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", + "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" } } }, @@ -76,4 +76,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 480caf3944..575f7374a8 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.10", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", + "version": "2.61.1", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", + "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", - "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", + "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" } } }, @@ -68,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 8504d7c41c..69d6ec7d35 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.60.10", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_amd64.tar.gz", + "version": "2.61.1", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e22dc039846f2aee3d180b1dfb7d1b8282377d76ab4654137ed4abfec5d8e2af", + "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.60.10/erigon_v2.60.10_linux_arm64.tar.gz", - "verification_source": "68cb9baf937d19446de91bc1efccf389b4a2452233b3a5ef1cf5cd8a91b9ce95" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", + "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" } } }, @@ -74,4 +74,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file From 397789b130400a79c39db8b33f3447519307b49b Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 17 Feb 2025 21:47:13 +0100 Subject: [PATCH 422/530] Remove Blockbook deb package dependency on backend Blockbook could run on a different server than backend and this dependency was causing install problems --- build/templates/blockbook/debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/templates/blockbook/debian/control b/build/templates/blockbook/debian/control index e596de0142..9185723a52 100644 --- a/build/templates/blockbook/debian/control +++ b/build/templates/blockbook/debian/control @@ -8,6 +8,6 @@ Standards-Version: 3.9.5 Package: {{.Blockbook.PackageName}} Architecture: {{.Env.Architecture}} -Depends: ${shlibs:Depends}, ${misc:Depends}, coreutils, passwd, findutils, psmisc, {{.Backend.PackageName}} +Depends: ${shlibs:Depends}, ${misc:Depends}, coreutils, passwd, findutils, psmisc Description: Satoshilabs blockbook server ({{.Coin.Name}}) {{end}} From 7d4872e8300d89174c1dd8b089ac7e879e90f1d5 Mon Sep 17 00:00:00 2001 From: kevin <35275952+kaladinlight@users.noreply.github.com> Date: Thu, 20 Feb 2025 14:55:15 -0700 Subject: [PATCH 423/530] Add Base Support (#1150) * add base support * backend config * add base archive blockchain factory * add dbProtoAddrContracts flag and default to legacy encoding. fix tests * default cache behavior for dbMaxAddrContracts default value * update to defer func to ensure addressContracts is reset and handle possible error * base config default to use dbProtoAddrContracts * add network config * update op-geth and op-node versions * remove coingecko url * update coingecko platform identifier * token type -> token standard * reduce allocations as pack/unpack addr contracts is primary bottleneck for l2 chains * archive snapshot no longer supported, use fullnode snapshot as best effort * remove proto encoded addr contracts as bench marks indicate there is no performance gain as initially suspected * revert address contract cache changes --- api/worker.go | 22 +++--- bchain/coins/base/baserpc.go | 73 +++++++++++++++++++ bchain/coins/blockchain.go | 3 + bchain/coins/bsc/bscrpc.go | 4 +- bchain/types.go | 7 +- bchain/types_ethereum_type.go | 6 +- build/templates/backend/scripts/base.sh | 45 ++++++++++++ .../templates/backend/scripts/base_archive.sh | 47 ++++++++++++ .../backend/scripts/base_archive_op_node.sh | 24 ++++++ .../templates/backend/scripts/base_op_node.sh | 24 ++++++ configs/coins/base.json | 67 +++++++++++++++++ configs/coins/base_archive.json | 70 ++++++++++++++++++ configs/coins/base_archive_op_node.json | 38 ++++++++++ configs/coins/base_op_node.json | 38 ++++++++++ db/rocksdb_ethereumtype.go | 36 ++++----- db/rocksdb_ethereumtype_test.go | 38 +++++----- docs/ports.md | 4 +- server/public.go | 4 +- 18 files changed, 489 insertions(+), 61 deletions(-) create mode 100644 bchain/coins/base/baserpc.go create mode 100644 build/templates/backend/scripts/base.sh create mode 100644 build/templates/backend/scripts/base_archive.sh create mode 100644 build/templates/backend/scripts/base_archive_op_node.sh create mode 100644 build/templates/backend/scripts/base_op_node.sh create mode 100644 configs/coins/base.json create mode 100644 configs/coins/base_archive.json create mode 100644 configs/coins/base_archive_op_node.json create mode 100644 configs/coins/base_op_node.json diff --git a/api/worker.go b/api/worker.go index bd122d20fa..e10817628f 100644 --- a/api/worker.go +++ b/api/worker.go @@ -622,25 +622,25 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, return r, nil } -func (w *Worker) GetContractInfo(contract string, typeFromContext bchain.TokenStandardName) (*bchain.ContractInfo, bool, error) { +func (w *Worker) GetContractInfo(contract string, standardFromContext bchain.TokenStandardName) (*bchain.ContractInfo, bool, error) { cd, err := w.chainParser.GetAddrDescFromAddress(contract) if err != nil { return nil, false, err } - return w.getContractDescriptorInfo(cd, typeFromContext) + return w.getContractDescriptorInfo(cd, standardFromContext) } -func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFromContext bchain.TokenStandardName) (*bchain.ContractInfo, bool, error) { +func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, standardFromContext bchain.TokenStandardName) (*bchain.ContractInfo, bool, error) { var err error validContract := true - contractInfo, err := w.db.GetContractInfo(cd, typeFromContext) + contractInfo, err := w.db.GetContractInfo(cd, standardFromContext) if err != nil { return nil, false, err } if contractInfo == nil { // log warning only if the contract should have been known from processing of the internal data if eth.ProcessInternalTransactions { - glog.Warningf("Contract %v %v not found in DB", cd, typeFromContext) + glog.Warningf("Contract %v %v not found in DB", cd, standardFromContext) } contractInfo, err = w.chain.GetContractInfo(cd) if err != nil { @@ -655,9 +655,9 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom validContract = false } else { - if typeFromContext != bchain.UnknownTokenStandard && contractInfo.Standard == bchain.UnknownTokenStandard { - contractInfo.Standard = typeFromContext - contractInfo.Type = typeFromContext + if standardFromContext != bchain.UnknownTokenStandard && contractInfo.Standard == bchain.UnknownTokenStandard { + contractInfo.Standard = standardFromContext + contractInfo.Type = standardFromContext } if err = w.db.StoreContractInfo(contractInfo); err != nil { glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) @@ -683,9 +683,9 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom contractInfo.Decimals = blockchainContractInfo.Decimals } if contractInfo.Standard == bchain.UnhandledTokenStandard { - glog.Infof("Contract %v %v [%s] handled", cd, typeFromContext, contractInfo.Name) - contractInfo.Standard = typeFromContext - contractInfo.Type = typeFromContext + glog.Infof("Contract %v %v [%s] handled", cd, standardFromContext, contractInfo.Name) + contractInfo.Standard = standardFromContext + contractInfo.Type = standardFromContext } if err = w.db.StoreContractInfo(contractInfo); err != nil { glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) diff --git a/bchain/coins/base/baserpc.go b/bchain/coins/base/baserpc.go new file mode 100644 index 0000000000..116f82efa6 --- /dev/null +++ b/bchain/coins/base/baserpc.go @@ -0,0 +1,73 @@ +package base + +import ( + "context" + "encoding/json" + + "github.com/golang/glog" + "github.com/juju/errors" + "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/bchain/coins/eth" +) + +const ( + // MainNet is production network + MainNet eth.Network = 8453 +) + +// BaseRPC is an interface to JSON-RPC base service. +type BaseRPC struct { + *eth.EthereumRPC +} + +// NewBaseRPC returns new BaseRPC instance. +func NewBaseRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { + c, err := eth.NewEthereumRPC(config, pushHandler) + if err != nil { + return nil, err + } + + s := &BaseRPC{ + EthereumRPC: c.(*eth.EthereumRPC), + } + + return s, nil +} + +// Initialize base rpc interface +func (b *BaseRPC) Initialize() error { + b.OpenRPC = eth.OpenRPC + + rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL) + if err != nil { + return err + } + + // set chain specific + b.Client = ec + b.RPC = rc + b.MainNetChainID = MainNet + b.NewBlock = eth.NewEthereumNewBlock() + b.NewTx = eth.NewEthereumNewTx() + + ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) + defer cancel() + + id, err := b.Client.NetworkID(ctx) + if err != nil { + return err + } + + // parameters for getInfo request + switch eth.Network(id.Uint64()) { + case MainNet: + b.Testnet = false + b.Network = "livenet" + default: + return errors.Errorf("Unknown network id %v", id) + } + + glog.Info("rpc: block chain ", b.Network) + + return nil +} diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 969e761295..f4704919f8 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -13,6 +13,7 @@ import ( "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain/coins/arbitrum" "github.com/trezor/blockbook/bchain/coins/avalanche" + "github.com/trezor/blockbook/bchain/coins/base" "github.com/trezor/blockbook/bchain/coins/bch" "github.com/trezor/blockbook/bchain/coins/bellcoin" "github.com/trezor/blockbook/bchain/coins/bitcore" @@ -147,6 +148,8 @@ func init() { BlockChainFactories["Arbitrum Archive"] = arbitrum.NewArbitrumRPC BlockChainFactories["Arbitrum Nova"] = arbitrum.NewArbitrumRPC BlockChainFactories["Arbitrum Nova Archive"] = arbitrum.NewArbitrumRPC + BlockChainFactories["Base"] = base.NewBaseRPC + BlockChainFactories["Base Archive"] = base.NewBaseRPC } // NewBlockChain creates bchain.BlockChain and bchain.Mempool for the coin passed by the parameter coin diff --git a/bchain/coins/bsc/bscrpc.go b/bchain/coins/bsc/bscrpc.go index bca05b4719..96fb648144 100644 --- a/bchain/coins/bsc/bscrpc.go +++ b/bchain/coins/bsc/bscrpc.go @@ -14,7 +14,7 @@ const ( // MainNet is production network MainNet eth.Network = 56 - // bsc token type names + // bsc token standard names BEP20TokenStandard bchain.TokenStandardName = "BEP20" BEP721TokenStandard bchain.TokenStandardName = "BEP721" BEP1155TokenStandard bchain.TokenStandardName = "BEP1155" @@ -32,7 +32,7 @@ func NewBNBSmartChainRPC(config json.RawMessage, pushHandler func(bchain.Notific return nil, err } - // overwrite EthereumTokenTypeMap with bsc specific token type names + // overwrite EthereumTokenStandardMap with bsc specific token standard names bchain.EthereumTokenStandardMap = []bchain.TokenStandardName{BEP20TokenStandard, BEP721TokenStandard, BEP1155TokenStandard} s := &BNBSmartChainRPC{ diff --git a/bchain/types.go b/bchain/types.go index 83b73d51dc..04c87e73d1 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -116,9 +116,6 @@ type MempoolTx struct { CoinSpecificData interface{} `json:"-"` } -// // Deprecated: Use TokenStandard instead. -// type TokenType int - // TokenStandard - standard of token type TokenStandard int @@ -129,10 +126,10 @@ const ( MultiToken // ERC1155/BEP1155 ) -// TokenStandardName specifies type of token +// TokenStandardName specifies standard of token type TokenStandardName string -// Token types +// Token standards const ( UnknownTokenStandard TokenStandardName = "" UnhandledTokenStandard TokenStandardName = "-" diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index 31e1702ae8..9713b7682d 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -70,15 +70,15 @@ type ContractInfo struct { DestructedInBlock uint32 `json:"destructedInBlock,omitempty"` } -// Ethereum token type names +// Ethereum token standard names const ( ERC20TokenStandard TokenStandardName = "ERC20" ERC771TokenStandard TokenStandardName = "ERC721" ERC1155TokenStandard TokenStandardName = "ERC1155" ) -// EthereumTokenTypeMap maps bchain.TokenType to TokenTypeName -// the map must match all bchain.TokenType to avoid index out of range panic +// EthereumTokenStandardMap maps bchain.TokenStandard to TokenStandardName +// the map must match all bchain.TokenStandard to avoid index out of range panic var EthereumTokenStandardMap = []TokenStandardName{ERC20TokenStandard, ERC771TokenStandard, ERC1155TokenStandard} type MultiTokenValue struct { diff --git a/build/templates/backend/scripts/base.sh b/build/templates/backend/scripts/base.sh new file mode 100644 index 0000000000..1b9305644b --- /dev/null +++ b/build/templates/backend/scripts/base.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +{{define "main" -}} + +set -e + +GETH_BIN={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth +DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend + +CHAINDATA_DIR=$DATA_DIR/geth/chaindata +SNAPSHOT=https://mainnet-full-snapshots.base.org/$(curl https://mainnet-full-snapshots.base.org/latest) + +if [ ! -d "$CHAINDATA_DIR" ]; then + wget -c $SNAPSHOT -O - | zstd -cd | tar xf - --strip-components=1 -C $DATA_DIR +fi + +$GETH_BIN \ + --op-network base-mainnet \ + --datadir $DATA_DIR \ + --authrpc.jwtsecret $DATA_DIR/jwtsecret \ + --authrpc.addr 127.0.0.1 \ + --authrpc.port {{.Ports.BackendAuthRpc}} \ + --authrpc.vhosts "*" \ + --port {{.Ports.BackendP2P}} \ + --http \ + --http.port {{.Ports.BackendHttp}} \ + --http.addr 127.0.0.1 \ + --http.api eth,net,web3,debug,txpool,engine \ + --http.vhosts "*" \ + --http.corsdomain "*" \ + --ws \ + --ws.port {{.Ports.BackendRPC}} \ + --ws.addr 127.0.0.1 \ + --ws.api eth,net,web3,debug,txpool,engine \ + --ws.origins "*" \ + --rollup.disabletxpoolgossip=true \ + --rollup.sequencerhttp https://mainnet-sequencer.base.io \ + --state.scheme hash \ + --history.transactions 0 \ + --cache 4096 \ + --syncmode full \ + --maxpeers 0 \ + --nodiscover + +{{end}} diff --git a/build/templates/backend/scripts/base_archive.sh b/build/templates/backend/scripts/base_archive.sh new file mode 100644 index 0000000000..6f344e467e --- /dev/null +++ b/build/templates/backend/scripts/base_archive.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +{{define "main" -}} + +set -e + +GETH_BIN={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth +DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend + +CHAINDATA_DIR=$DATA_DIR/geth/chaindata +SNAPSHOT=https://mainnet-full-snapshots.base.org/$(curl https://mainnet-full-snapshots.base.org/latest) + +if [ ! -d "$CHAINDATA_DIR" ]; then + wget -c $SNAPSHOT -O - | zstd -cd | tar xf - --strip-components=1 -C $DATA_DIR +fi + +$GETH_BIN \ + --op-network base-mainnet \ + --datadir $DATA_DIR \ + --authrpc.jwtsecret $DATA_DIR/jwtsecret \ + --authrpc.addr 127.0.0.1 \ + --authrpc.port {{.Ports.BackendAuthRpc}} \ + --authrpc.vhosts "*" \ + --port {{.Ports.BackendP2P}} \ + --http \ + --http.port {{.Ports.BackendHttp}} \ + --http.addr 127.0.0.1 \ + --http.api eth,net,web3,debug,txpool,engine \ + --http.vhosts "*" \ + --http.corsdomain "*" \ + --ws \ + --ws.port {{.Ports.BackendRPC}} \ + --ws.addr 127.0.0.1 \ + --ws.api eth,net,web3,debug,txpool,engine \ + --ws.origins "*" \ + --rollup.disabletxpoolgossip=true \ + --rollup.sequencerhttp https://mainnet.sequencer.optimism.io \ + --cache 4096 \ + --cache.gc 0 \ + --cache.trie 30 \ + --cache.snapshot 20 \ + --syncmode full \ + --gcmode archive \ + --maxpeers 0 \ + --nodiscover + +{{end}} diff --git a/build/templates/backend/scripts/base_archive_op_node.sh b/build/templates/backend/scripts/base_archive_op_node.sh new file mode 100644 index 0000000000..75e122da8e --- /dev/null +++ b/build/templates/backend/scripts/base_archive_op_node.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +{{define "main" -}} + +set -e + +BIN={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/op-node + +$BIN \ + --network base-mainnet \ + --l1 http://127.0.0.1:8116 \ + --l1.beacon http://127.0.0.1:7516 \ + --l1.trustrpc \ + --l1.rpckind=debug_geth \ + --l2 http://127.0.0.1:8411 \ + --rpc.addr 127.0.0.1 \ + --rpc.port {{.Ports.BackendRPC}} \ + --l2.jwt-secret {{.Env.BackendDataPath}}/base_archive/backend/jwtsecret \ + --p2p.bootnodes enr:-J24QNz9lbrKbN4iSmmjtnr7SjUMk4zB7f1krHZcTZx-JRKZd0kA2gjufUROD6T3sOWDVDnFJRvqBBo62zuF-hYCohOGAYiOoEyEgmlkgnY0gmlwhAPniryHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQKNVFlCxh_B-716tTs-h1vMzZkSs1FTu_OYTNjgufplG4N0Y3CCJAaDdWRwgiQG,enr:-J24QH-f1wt99sfpHy4c0QJM-NfmsIfmlLAMMcgZCUEgKG_BBYFc6FwYgaMJMQN5dsRBJApIok0jFn-9CS842lGpLmqGAYiOoDRAgmlkgnY0gmlwhLhIgb2Hb3BzdGFja4OFQgCJc2VjcDI1NmsxoQJ9FTIv8B9myn1MWaC_2lJ-sMoeCDkusCsk4BYHjjCq04N0Y3CCJAaDdWRwgiQG,enr:-J24QDXyyxvQYsd0yfsN0cRr1lZ1N11zGTplMNlW4xNEc7LkPXh0NAJ9iSOVdRO95GPYAIc6xmyoCCG6_0JxdL3a0zaGAYiOoAjFgmlkgnY0gmlwhAPckbGHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQJwoS7tzwxqXSyFL7g0JM-KWVbgvjfB8JA__T7yY_cYboN0Y3CCJAaDdWRwgiQG,enr:-J24QHmGyBwUZXIcsGYMaUqGGSl4CFdx9Tozu-vQCn5bHIQbR7On7dZbU61vYvfrJr30t0iahSqhc64J46MnUO2JvQaGAYiOoCKKgmlkgnY0gmlwhAPnCzSHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQINc4fSijfbNIiGhcgvwjsjxVFJHUstK9L1T8OTKUjgloN0Y3CCJAaDdWRwgiQG,enr:-J24QG3ypT4xSu0gjb5PABCmVxZqBjVw9ca7pvsI8jl4KATYAnxBmfkaIuEqy9sKvDHKuNCsy57WwK9wTt2aQgcaDDyGAYiOoGAXgmlkgnY0gmlwhDbGmZaHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQIeAK_--tcLEiu7HvoUlbV52MspE0uCocsx1f_rYvRenIN0Y3CCJAaDdWRwgiQG \ + --p2p.useragent base \ + --rollup.load-protocol-versions=true \ + --verifier.l1-confs 4 + +{{end}} diff --git a/build/templates/backend/scripts/base_op_node.sh b/build/templates/backend/scripts/base_op_node.sh new file mode 100644 index 0000000000..4254b8972e --- /dev/null +++ b/build/templates/backend/scripts/base_op_node.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +{{define "main" -}} + +set -e + +BIN={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/op-node + +$BIN \ + --network base-mainnet \ + --l1 http://127.0.0.1:8136 \ + --l1.beacon http://127.0.0.1:7536 \ + --l1.trustrpc \ + --l1.rpckind debug_geth \ + --l2 http://127.0.0.1:8409 \ + --rpc.addr 127.0.0.1 \ + --rpc.port {{.Ports.BackendRPC}} \ + --l2.jwt-secret {{.Env.BackendDataPath}}/base/backend/jwtsecret \ + --p2p.bootnodes enr:-J24QNz9lbrKbN4iSmmjtnr7SjUMk4zB7f1krHZcTZx-JRKZd0kA2gjufUROD6T3sOWDVDnFJRvqBBo62zuF-hYCohOGAYiOoEyEgmlkgnY0gmlwhAPniryHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQKNVFlCxh_B-716tTs-h1vMzZkSs1FTu_OYTNjgufplG4N0Y3CCJAaDdWRwgiQG,enr:-J24QH-f1wt99sfpHy4c0QJM-NfmsIfmlLAMMcgZCUEgKG_BBYFc6FwYgaMJMQN5dsRBJApIok0jFn-9CS842lGpLmqGAYiOoDRAgmlkgnY0gmlwhLhIgb2Hb3BzdGFja4OFQgCJc2VjcDI1NmsxoQJ9FTIv8B9myn1MWaC_2lJ-sMoeCDkusCsk4BYHjjCq04N0Y3CCJAaDdWRwgiQG,enr:-J24QDXyyxvQYsd0yfsN0cRr1lZ1N11zGTplMNlW4xNEc7LkPXh0NAJ9iSOVdRO95GPYAIc6xmyoCCG6_0JxdL3a0zaGAYiOoAjFgmlkgnY0gmlwhAPckbGHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQJwoS7tzwxqXSyFL7g0JM-KWVbgvjfB8JA__T7yY_cYboN0Y3CCJAaDdWRwgiQG,enr:-J24QHmGyBwUZXIcsGYMaUqGGSl4CFdx9Tozu-vQCn5bHIQbR7On7dZbU61vYvfrJr30t0iahSqhc64J46MnUO2JvQaGAYiOoCKKgmlkgnY0gmlwhAPnCzSHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQINc4fSijfbNIiGhcgvwjsjxVFJHUstK9L1T8OTKUjgloN0Y3CCJAaDdWRwgiQG,enr:-J24QG3ypT4xSu0gjb5PABCmVxZqBjVw9ca7pvsI8jl4KATYAnxBmfkaIuEqy9sKvDHKuNCsy57WwK9wTt2aQgcaDDyGAYiOoGAXgmlkgnY0gmlwhDbGmZaHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQIeAK_--tcLEiu7HvoUlbV52MspE0uCocsx1f_rYvRenIN0Y3CCJAaDdWRwgiQG \ + --p2p.useragent base \ + --rollup.load-protocol-versions=true \ + --verifier.l1-confs 4 + +{{end}} diff --git a/configs/coins/base.json b/configs/coins/base.json new file mode 100644 index 0000000000..83578785be --- /dev/null +++ b/configs/coins/base.json @@ -0,0 +1,67 @@ +{ + "coin": { + "name": "Base", + "shortcut": "ETH", + "network": "BASE", + "label": "Base", + "alias": "base" + }, + "ports": { + "backend_rpc": 8309, + "backend_p2p": 38409, + "backend_http": 8209, + "backend_authrpc": 8409, + "blockbook_internal": 9209, + "blockbook_public": 9309 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-base", + "package_revision": "satoshilabs-1", + "system_user": "base", + "version": "1.101411.3", + "docker_image": "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101411.3", + "verification_type": "docker", + "verification_source": "aefecdb139d8e3ed3128e7e3c87abb71198dc6a44ef21f012f391af52679e2c5", + "extract_command": "docker cp extract:/usr/local/bin/geth backend/geth", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/base_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "base.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-base", + "system_user": "blockbook-base", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "additional_params": { + "mempoolTxTimeoutHours": 48, + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"base\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} \ No newline at end of file diff --git a/configs/coins/base_archive.json b/configs/coins/base_archive.json new file mode 100644 index 0000000000..877225787c --- /dev/null +++ b/configs/coins/base_archive.json @@ -0,0 +1,70 @@ +{ + "coin": { + "name": "Base Archive", + "shortcut": "ETH", + "network": "BASE", + "label": "Base", + "alias": "base_archive" + }, + "ports": { + "backend_rpc": 8211, + "backend_p2p": 38411, + "backend_http": 8311, + "backend_authrpc": 8411, + "blockbook_internal": 9211, + "blockbook_public": 9311 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-base-archive", + "package_revision": "satoshilabs-1", + "system_user": "base", + "version": "1.101411.3", + "docker_image": "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101411.3", + "verification_type": "docker", + "verification_source": "aefecdb139d8e3ed3128e7e3c87abb71198dc6a44ef21f012f391af52679e2c5", + "extract_command": "docker cp extract:/usr/local/bin/geth backend/geth", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/base_archive_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "base_archive.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "blockbook": { + "package_name": "blockbook-base-archive", + "system_user": "blockbook-base", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "-workers=16", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 600, + "additional_params": { + "address_aliases": true, + "mempoolTxTimeoutHours": 48, + "processInternalTransactions": true, + "queryBackendOnMempoolResync": false, + "fiat_rates": "coingecko", + "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"base\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} \ No newline at end of file diff --git a/configs/coins/base_archive_op_node.json b/configs/coins/base_archive_op_node.json new file mode 100644 index 0000000000..85a4c5dbe1 --- /dev/null +++ b/configs/coins/base_archive_op_node.json @@ -0,0 +1,38 @@ +{ + "coin": { + "name": "Base Archive Op-Node", + "shortcut": "ETH", + "label": "Base", + "alias": "base_archive_op_node" + }, + "ports": { + "backend_rpc": 8212, + "blockbook_internal": 9212, + "blockbook_public": 9312 + }, + "backend": { + "package_name": "backend-base-archive-op-node", + "package_revision": "satoshilabs-1", + "system_user": "base", + "version": "1.10.1", + "docker_image": "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:v1.10.1", + "verification_type": "docker", + "verification_source": "8f40714868fbdc788f67251383a0c0b78a3a937f07b2303bc7d33df5df6297d9", + "extract_command": "docker cp extract:/usr/local/bin/op-node backend/op-node", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/base_archive_op_node_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "base_archive_op_node.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} \ No newline at end of file diff --git a/configs/coins/base_op_node.json b/configs/coins/base_op_node.json new file mode 100644 index 0000000000..426d718069 --- /dev/null +++ b/configs/coins/base_op_node.json @@ -0,0 +1,38 @@ +{ + "coin": { + "name": "Base Op-Node", + "shortcut": "ETH", + "label": "Base", + "alias": "base_op_node" + }, + "ports": { + "backend_rpc": 8210, + "blockbook_internal": 9210, + "blockbook_public": 9310 + }, + "backend": { + "package_name": "backend-base-op-node", + "package_revision": "satoshilabs-1", + "system_user": "base", + "version": "1.10.1", + "docker_image": "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:v1.10.1", + "verification_type": "docker", + "verification_source": "8f40714868fbdc788f67251383a0c0b78a3a937f07b2303bc7d33df5df6297d9", + "extract_command": "docker cp extract:/usr/local/bin/op-node backend/op-node", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/base_op_node_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "exec_script": "base_op_node.sh", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "", + "client_config_file": "" + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} \ No newline at end of file diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index f52d705b07..e6845e0ed6 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -177,21 +177,21 @@ func unpackAddrContracts(buf []byte, addrDesc bchain.AddressDescriptor) (*AddrCo contract := append(bchain.AddressDescriptor(nil), buf[:eth.EthereumTypeAddressDescriptorLen]...) txs, l := unpackVaruint(buf[eth.EthereumTypeAddressDescriptorLen:]) buf = buf[eth.EthereumTypeAddressDescriptorLen+l:] - ttt := bchain.TokenStandard(txs & 3) + standard := bchain.TokenStandard(txs & 3) txs >>= 2 ac := AddrContract{ - Standard: ttt, + Standard: standard, Contract: contract, Txs: txs, } - if ttt == bchain.FungibleToken { + if standard == bchain.FungibleToken { b, ll := unpackBigint(buf) buf = buf[ll:] ac.Value = b } else { len, ll := unpackVaruint(buf) buf = buf[ll:] - if ttt == bchain.NonFungibleToken { + if standard == bchain.NonFungibleToken { ac.Ids = make(Ids, len) for i := uint(0); i < len; i++ { b, ll := unpackBigint(buf) @@ -391,7 +391,7 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address type ethBlockTxContract struct { from, to, contract bchain.AddressDescriptor - transferType bchain.TokenStandard + transferStandard bchain.TokenStandard value big.Int idValues []bchain.MultiTokenValue } @@ -566,7 +566,7 @@ func (d *RocksDB) processContractTransfers(blockTx *ethBlockTx, tx *bchain.Tx, a return err } bc := &blockTx.contracts[i] - bc.transferType = t.Standard + bc.transferStandard = t.Standard bc.from = from bc.to = to bc.contract = contract @@ -890,9 +890,9 @@ func (d *RocksDB) GetContractInfoForAddress(address string) (*bchain.ContractInf return d.GetContractInfo(contract, "") } -// GetContractInfo gets contract from cache or DB and possibly updates the type from typeFromContext -// it is hard to guess the type of the contract using API, it is easier to set it the first time the contract is processed in a tx -func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, typeFromContext bchain.TokenStandardName) (*bchain.ContractInfo, error) { +// GetContractInfo gets contract from cache or DB and possibly updates the standard from standardFromContext +// it is hard to guess the standard of the contract using API, it is easier to set it the first time the contract is processed in a tx +func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, standardFromContext bchain.TokenStandardName) (*bchain.ContractInfo, error) { cacheKey := string(contract) cachedContractsMux.Lock() contractInfo, found := cachedContracts[cacheKey] @@ -912,10 +912,10 @@ func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, typeFromCon if len(addresses) > 0 { contractInfo.Contract = addresses[0] } - // if the type is specified and stored contractInfo has unknown type, set and store it - if typeFromContext != bchain.UnknownTokenStandard && contractInfo.Standard == bchain.UnknownTokenStandard { - contractInfo.Standard = typeFromContext - contractInfo.Type = typeFromContext + // if the standard is specified and stored contractInfo has unknown standard, set and store it + if standardFromContext != bchain.UnknownTokenStandard && contractInfo.Standard == bchain.UnknownTokenStandard { + contractInfo.Standard = standardFromContext + contractInfo.Type = standardFromContext err = d.db.PutCF(d.wo, d.cfh[cfContracts], contract, packContractInfo(contractInfo)) if err != nil { return nil, err @@ -980,9 +980,9 @@ func packBlockTx(buf []byte, blockTx *ethBlockTx) []byte { buf = appendAddress(buf, c.from) buf = appendAddress(buf, c.to) buf = appendAddress(buf, c.contract) - l = packVaruint(uint(c.transferType), varBuf) + l = packVaruint(uint(c.transferStandard), varBuf) buf = append(buf, varBuf[:l]...) - if c.transferType == bchain.MultiToken { + if c.transferStandard == bchain.MultiToken { l = packVaruint(uint(len(c.idValues)), varBuf) buf = append(buf, varBuf[:l]...) for i := range c.idValues { @@ -1144,9 +1144,9 @@ func unpackBlockTx(buf []byte, pos int) (*ethBlockTx, int, error) { return nil, 0, err } cc, l = unpackVaruint(buf[pos:]) - c.transferType = bchain.TokenStandard(cc) + c.transferStandard = bchain.TokenStandard(cc) pos += l - if c.transferType == bchain.MultiToken { + if c.transferStandard == bchain.MultiToken { cc, l = unpackVaruint(buf[pos:]) pos += l c.idValues = make([]bchain.MultiTokenValue, cc) @@ -1259,7 +1259,7 @@ func (d *RocksDB) disconnectAddress(btxID []byte, internal bool, addrDesc bchain index = transferTo } addToContract(addrContract, contractIndex, index, btxContract.contract, &bchain.TokenTransfer{ - Standard: btxContract.transferType, + Standard: btxContract.transferStandard, Value: btxContract.value, MultiTokenValues: btxContract.idValues, }, false) diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index 6dc2303ab8..880a096bfb 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -1150,11 +1150,11 @@ func Test_packUnpackBlockTx(t *testing.T) { to: addressToAddrDesc(dbtestdata.EthAddr55, parser), contracts: []ethBlockTxContract{ { - from: addressToAddrDesc(dbtestdata.EthAddr20, parser), - to: addressToAddrDesc(dbtestdata.EthAddr5d, parser), - contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), - transferType: bchain.FungibleToken, - value: *big.NewInt(10000), + from: addressToAddrDesc(dbtestdata.EthAddr20, parser), + to: addressToAddrDesc(dbtestdata.EthAddr5d, parser), + contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), + transferStandard: bchain.FungibleToken, + value: *big.NewInt(10000), }, }, }, @@ -1168,24 +1168,24 @@ func Test_packUnpackBlockTx(t *testing.T) { to: addressToAddrDesc(dbtestdata.EthAddr55, parser), contracts: []ethBlockTxContract{ { - from: addressToAddrDesc(dbtestdata.EthAddr20, parser), - to: addressToAddrDesc(dbtestdata.EthAddr3e, parser), - contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), - transferType: bchain.FungibleToken, - value: *big.NewInt(987654321), + from: addressToAddrDesc(dbtestdata.EthAddr20, parser), + to: addressToAddrDesc(dbtestdata.EthAddr3e, parser), + contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), + transferStandard: bchain.FungibleToken, + value: *big.NewInt(987654321), }, { - from: addressToAddrDesc(dbtestdata.EthAddr4b, parser), - to: addressToAddrDesc(dbtestdata.EthAddr55, parser), - contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), - transferType: bchain.NonFungibleToken, - value: *big.NewInt(13), + from: addressToAddrDesc(dbtestdata.EthAddr4b, parser), + to: addressToAddrDesc(dbtestdata.EthAddr55, parser), + contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser), + transferStandard: bchain.NonFungibleToken, + value: *big.NewInt(13), }, { - from: addressToAddrDesc(dbtestdata.EthAddr5d, parser), - to: addressToAddrDesc(dbtestdata.EthAddr7b, parser), - contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), - transferType: bchain.MultiToken, + from: addressToAddrDesc(dbtestdata.EthAddr5d, parser), + to: addressToAddrDesc(dbtestdata.EthAddr7b, parser), + contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser), + transferStandard: bchain.MultiToken, idValues: []bchain.MultiTokenValue{ { Id: *big.NewInt(1234), diff --git a/docs/ports.md b/docs/ports.md index fe51bbb8ef..b99743c669 100644 --- a/docs/ports.md +++ b/docs/ports.md @@ -1,7 +1,7 @@ # Registry of ports | coin | blockbook public | blockbook internal | backend rpc | backend service ports (zmq) | -| -------------------------------- | ---------------- | ------------------ | ----------- | --------------------------------------------------- | +|----------------------------------|------------------|--------------------|-------------|-----------------------------------------------------| | Ethereum Archive | 9116 | 9016 | 8016 | 38316 p2p, 8116 http, 8516 authrpc | | Bitcoin | 9130 | 9030 | 8030 | 38330 | | Bitcoin Cash | 9131 | 9031 | 8031 | 38331 | @@ -59,6 +59,8 @@ | Arbitrum Archive | 9306 | 9206 | 8306 | 38406 p2p | | Arbitrum Nova | 9307 | 9207 | 8207 | 38407 p2p, 8307 http | | Arbitrum Nova Archive | 9308 | 9208 | 8308 | 38408 p2p | +| Base | 9309 | 9209 | 8309 | 38409 p2p, 8209 http, 8409 authrpc | +| Base Archive | 9311 | 9211 | 8211 | 38411 p2p, 8311 http, 8411 authrpc | | Ethereum Testnet Holesky | 19116 | 19016 | 18016 | 18116 http, 18516 authrpc, 48316 p2p | | Bitcoin Signet | 19120 | 19020 | 18020 | 48320 | | Bitcoin Regtest | 19121 | 19021 | 18021 | 48321 | diff --git a/server/public.go b/server/public.go index 9b7a1df07e..6ec9fb1aa8 100644 --- a/server/public.go +++ b/server/public.go @@ -745,7 +745,7 @@ func isOwnAddress(td *TemplateData, a string) bool { func tokenTransfersCount(tx *api.Tx, t bchain.TokenStandardName) int { count := 0 for i := range tx.TokenTransfers { - if tx.TokenTransfers[i].Type == t { + if tx.TokenTransfers[i].Standard == t { count++ } } @@ -756,7 +756,7 @@ func tokenTransfersCount(tx *api.Tx, t bchain.TokenStandardName) int { func tokenCount(tokens []api.Token, t bchain.TokenStandardName) int { count := 0 for i := range tokens { - if tokens[i].Type == t { + if tokens[i].Standard == t { count++ } } From 95e965d5dfd12b3bdeeeaa6278f04f2c9ee08945 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 21 Feb 2025 19:32:23 +0100 Subject: [PATCH 424/530] Return 503 ServiceUnavailable from public interface if not synced --- server/public.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/server/public.go b/server/public.go index 6ec9fb1aa8..84ba710b5a 100644 --- a/server/public.go +++ b/server/public.go @@ -59,6 +59,7 @@ type PublicServer struct { is *common.InternalState fiatRates *fiat.FiatRates useSatsAmountFormat bool + isFullInterface bool } // NewPublicServer creates new public server http interface to blockbook and returns its handle @@ -216,6 +217,7 @@ func (s *PublicServer) ConnectFullPublicInterface() { serveMux.Handle(path+"socket.io/", s.socketio.GetHandler()) // websocket interface serveMux.Handle(path+"websocket", s.websocket.GetHandler()) + s.isFullInterface = true } // Close closes the server @@ -998,6 +1000,11 @@ func (s *PublicServer) explorerBlock(w http.ResponseWriter, r *http.Request) (tp } func (s *PublicServer) explorerIndex(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) { + if !s.isFullInterface && r.URL.Path != "/" { + w.WriteHeader(http.StatusServiceUnavailable) + w.Write([]byte("Service unavailable")) + return noTpl, nil, nil + } var si *api.SystemInfo var err error s.metrics.ExplorerViews.With(common.Labels{"action": "index"}).Inc() @@ -1142,6 +1149,9 @@ func getPagingRange(page int, total int) ([]int, int, int) { } func (s *PublicServer) apiIndex(r *http.Request, apiVersion int) (interface{}, error) { + if !s.isFullInterface && r.URL.Path != "/api/" { + return nil, api.NewAPIError("Service unavailable", false) + } s.metrics.ExplorerViews.With(common.Labels{"action": "api-index"}).Inc() return s.api.GetSystemInfo(false) } From c1be4504e69534fb633cb0340e7d4ab5782d5161 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 21 Feb 2025 21:19:59 +0100 Subject: [PATCH 425/530] Initialize block times asynchronously to speed up server startup --- common/internalstate.go | 20 ++++++++++++++++---- db/bulkconnect.go | 14 ++++++-------- db/rocksdb.go | 31 ++++++++++++++++++++++--------- db/rocksdb_ethereumtype_test.go | 16 ++++++++-------- db/rocksdb_test.go | 16 ++++++++-------- 5 files changed, 60 insertions(+), 37 deletions(-) diff --git a/common/internalstate.go b/common/internalstate.go index 29a7a3339f..9e452ce7e3 100644 --- a/common/internalstate.go +++ b/common/internalstate.go @@ -240,17 +240,29 @@ func (is *InternalState) GetLastBlockTime() uint32 { func (is *InternalState) SetBlockTimes(blockTimes []uint32) uint32 { is.mux.Lock() defer is.mux.Unlock() - is.BlockTimes = blockTimes + if len(is.BlockTimes) < len(blockTimes) { + // no new block was set + is.BlockTimes = blockTimes + } else { + copy(is.BlockTimes, blockTimes) + } is.computeAvgBlockPeriod() glog.Info("set ", len(is.BlockTimes), " block times, average block period ", is.AvgBlockPeriod, "s") return is.AvgBlockPeriod } -// AppendBlockTime appends block time to BlockTimes, returns AvgBlockPeriod -func (is *InternalState) AppendBlockTime(time uint32) uint32 { +// SetBlockTime sets block time to BlockTimes, allocating the slice as necessary, returns AvgBlockPeriod +func (is *InternalState) SetBlockTime(height uint32, time uint32) uint32 { is.mux.Lock() defer is.mux.Unlock() - is.BlockTimes = append(is.BlockTimes, time) + if int(height) >= len(is.BlockTimes) { + extend := int(height) - len(is.BlockTimes) + 1 + for i := 0; i < extend; i++ { + is.BlockTimes = append(is.BlockTimes, time) + } + } else { + is.BlockTimes[height] = time + } is.computeAvgBlockPeriod() return is.AvgBlockPeriod } diff --git a/db/bulkconnect.go b/db/bulkconnect.go index 8c2095bf7e..03528c1d80 100644 --- a/db/bulkconnect.go +++ b/db/bulkconnect.go @@ -443,15 +443,13 @@ func (b *BulkConnect) Close() error { } glog.Info("rocksdb: bulk connect closed, db set to open state") - bt, err := b.d.loadBlockTimes() - if err != nil { - return err - } - avg := b.d.is.SetBlockTimes(bt) - if b.d.metrics != nil { - b.d.metrics.AvgBlockPeriod.Set(float64(avg)) + // set block times asynchronously (if not in unit test), it slows server startup for chains with large number of blocks + if b.d.is.Coin == "coin-unittest" { + b.d.setBlockTimes() + } else { + go b.d.setBlockTimes() } - glog.Info("rocksdb: processed block times") + b.d = nil return nil } diff --git a/db/rocksdb.go b/db/rocksdb.go index 58e269d743..413659375c 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -401,7 +401,7 @@ func (d *RocksDB) ConnectBlock(block *bchain.Block) error { if err := d.WriteBatch(wb); err != nil { return err } - avg := d.is.AppendBlockTime(uint32(block.Time)) + avg := d.is.SetBlockTime(block.Height, uint32(block.Time)) if d.metrics != nil { d.metrics.AvgBlockPeriod.Set(float64(avg)) } @@ -1848,6 +1848,20 @@ func (d *RocksDB) loadBlockTimes() ([]uint32, error) { return times, nil } +func (d *RocksDB) setBlockTimes() { + start := time.Now() + bt, err := d.loadBlockTimes() + if err != nil { + glog.Error("rocksdb: cannot load block times ", err) + return + } + avg := d.is.SetBlockTimes(bt) + if d.metrics != nil { + d.metrics.AvgBlockPeriod.Set(float64(avg)) + } + glog.Info("rocksdb: processed block times in ", time.Since(start)) +} + func (d *RocksDB) checkColumns(is *common.InternalState) ([]common.InternalStateColumn, error) { // make sure that column stats match the columns sc := is.DbColumns @@ -1932,15 +1946,14 @@ func (d *RocksDB) LoadInternalState(config *common.Config) (*common.InternalStat return nil, err } is.DbColumns = nc - bt, err := d.loadBlockTimes() - if err != nil { - return nil, err - } - avg := is.SetBlockTimes(bt) - if d.metrics != nil { - d.metrics.AvgBlockPeriod.Set(float64(avg)) - } + d.is = is + // set block times asynchronously (if not in unit test), it slows server startup for chains with large number of blocks + if is.Coin == "coin-unittest" { + d.setBlockTimes() + } else { + go d.setBlockTimes() + } // after load, reset the synchronization data is.IsSynchronized = false is.IsMempoolSynchronized = false diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index 880a096bfb..4d00780c4d 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -409,8 +409,8 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { } verifyAfterEthereumTypeBlock1(t, d, false) - if len(d.is.BlockTimes) != 1 { - t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) + if len(d.is.BlockTimes) != 4321001 { + t.Fatal("Expecting is.BlockTimes 4321001, got ", len(d.is.BlockTimes)) } // connect 2nd block, simulate InternalDataError and AddressAlias @@ -421,8 +421,8 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { verifyAfterEthereumTypeBlock2(t, d, true) block2.CoinSpecificData = nil - if len(d.is.BlockTimes) != 2 { - t.Fatal("Expecting is.BlockTimes 2, got ", len(d.is.BlockTimes)) + if len(d.is.BlockTimes) != 4321002 { + t.Fatal("Expecting is.BlockTimes 4321002, got ", len(d.is.BlockTimes)) } // get transactions for various addresses / low-high ranges @@ -551,8 +551,8 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { } } - if len(d.is.BlockTimes) != 1 { - t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) + if len(d.is.BlockTimes) != 4321001 { + t.Fatal("Expecting is.BlockTimes 4321001, got ", len(d.is.BlockTimes)) } // connect block again and verify the state of db @@ -561,8 +561,8 @@ func TestRocksDB_Index_EthereumType(t *testing.T) { } verifyAfterEthereumTypeBlock2(t, d, false) - if len(d.is.BlockTimes) != 2 { - t.Fatal("Expecting is.BlockTimes 2, got ", len(d.is.BlockTimes)) + if len(d.is.BlockTimes) != 4321002 { + t.Fatal("Expecting is.BlockTimes 4321002, got ", len(d.is.BlockTimes)) } } diff --git a/db/rocksdb_test.go b/db/rocksdb_test.go index 1204b0c3c0..aeb4c666af 100644 --- a/db/rocksdb_test.go +++ b/db/rocksdb_test.go @@ -547,8 +547,8 @@ func TestRocksDB_Index_BitcoinType(t *testing.T) { } verifyAfterBitcoinTypeBlock1(t, d, false) - if len(d.is.BlockTimes) != 1 { - t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) + if len(d.is.BlockTimes) != 225494 { + t.Fatal("Expecting is.BlockTimes 225494, got ", len(d.is.BlockTimes)) } // connect 2nd block - use some outputs from the 1st block as the inputs and 1 input uses tx from the same block @@ -558,8 +558,8 @@ func TestRocksDB_Index_BitcoinType(t *testing.T) { } verifyAfterBitcoinTypeBlock2(t, d) - if len(d.is.BlockTimes) != 2 { - t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) + if len(d.is.BlockTimes) != 225495 { + t.Fatal("Expecting is.BlockTimes 225495, got ", len(d.is.BlockTimes)) } // get transactions for various addresses / low-high ranges @@ -667,8 +667,8 @@ func TestRocksDB_Index_BitcoinType(t *testing.T) { } } - if len(d.is.BlockTimes) != 1 { - t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) + if len(d.is.BlockTimes) != 225494 { + t.Fatal("Expecting is.BlockTimes 225494, got ", len(d.is.BlockTimes)) } // connect block again and verify the state of db @@ -677,8 +677,8 @@ func TestRocksDB_Index_BitcoinType(t *testing.T) { } verifyAfterBitcoinTypeBlock2(t, d) - if len(d.is.BlockTimes) != 2 { - t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) + if len(d.is.BlockTimes) != 225495 { + t.Fatal("Expecting is.BlockTimes 225495, got ", len(d.is.BlockTimes)) } // test public methods for address balance and tx addresses From da584eb7b5555b0ca979f22c233c758c3ddf808b Mon Sep 17 00:00:00 2001 From: beforetech Date: Sun, 23 Feb 2025 21:34:23 +0800 Subject: [PATCH 426/530] chore: fix some comments Signed-off-by: beforetech --- fiat/fiat_rates.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fiat/fiat_rates.go b/fiat/fiat_rates.go index d6ead4bb0e..8a2d4bd464 100644 --- a/fiat/fiat_rates.go +++ b/fiat/fiat_rates.go @@ -372,7 +372,7 @@ func (fr *FiatRates) tickersToMap(tickers *[]common.CurrencyRatesTicker, granula return m, from, to } -// setCurrentTicker sets hourly tickers +// setHourlyTickers sets hourly tickers func (fr *FiatRates) setHourlyTickers(t *[]common.CurrencyRatesTicker) { fr.db.FiatRatesStoreSpecialTickers(hourlyTickersKey, t) fr.mux.Lock() @@ -380,7 +380,7 @@ func (fr *FiatRates) setHourlyTickers(t *[]common.CurrencyRatesTicker) { fr.hourlyTickers, fr.hourlyTickersFrom, fr.hourlyTickersTo = fr.tickersToMap(t, secondsInHour) } -// setCurrentTicker sets hourly tickers +// setFiveMinutesTickers sets five minutes tickers func (fr *FiatRates) setFiveMinutesTickers(t *[]common.CurrencyRatesTicker) { fr.db.FiatRatesStoreSpecialTickers(fiveMinutesTickersKey, t) fr.mux.Lock() From a1ae09d300f02a3cc1240b93e5450c9a773a806f Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 25 Feb 2025 12:04:33 +0100 Subject: [PATCH 427/530] polygon-bor 1.5.5 -> 2.0.0 --- configs/coins/polygon.json | 20 +++++++++++++------- configs/coins/polygon_archive.json | 20 +++++++++++++------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/configs/coins/polygon.json b/configs/coins/polygon.json index 4502e3abb5..2904634ab6 100644 --- a/configs/coins/polygon.json +++ b/configs/coins/polygon.json @@ -21,22 +21,28 @@ "package_name": "backend-polygon-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.5.5", - "binary_url": "https://github.com/maticnetwork/bor/archive/refs/tags/v1.5.5.tar.gz", + "version": "2.0.0", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.0/bor-v2.0.0-amd64.deb", "verification_type": "sha256", - "verification_source": "43ed5036b6c337e32b6c49a1e299bea817f1a7e236fffb401fa63f17063492da", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.5.5.tar.gz && cd backend/source && make bor && mv build/bin/bor ../ && rm -rf ../source && echo", + "verification_source": "942667f84732e25474b48f1fa6a13b513194f954344e3fa005d7165a8dd15d9a", + "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v1.5.5/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, "mainnet": true, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.0/bor-v2.0.0-arm64.deb", + "verification_source": "914d17ff48258396d228b5feb4aeac90dbd47d272619a87e384c97385b53ffcc" + } + } }, "blockbook": { "package_name": "blockbook-polygon", @@ -63,4 +69,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index e3d01c0786..2d45921073 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -21,22 +21,28 @@ "package_name": "backend-polygon-archive-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.5.5", - "binary_url": "https://github.com/maticnetwork/bor/archive/refs/tags/v1.5.5.tar.gz", + "version": "2.0.0", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.0/bor-v2.0.0-amd64.deb", "verification_type": "sha256", - "verification_source": "43ed5036b6c337e32b6c49a1e299bea817f1a7e236fffb401fa63f17063492da", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.5.5.tar.gz && cd backend/source && make bor && mv build/bin/bor ../ && rm -rf ../source && echo", + "verification_source": "942667f84732e25474b48f1fa6a13b513194f954344e3fa005d7165a8dd15d9a", + "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v1.5.5/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, "mainnet": true, "server_config_file": "", - "client_config_file": "" + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.0/bor-v2.0.0-arm64.deb", + "verification_source": "914d17ff48258396d228b5feb4aeac90dbd47d272619a87e384c97385b53ffcc" + } + } }, "blockbook": { "package_name": "blockbook-polygon-archive", @@ -69,4 +75,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} From f6d1718d0c96e653341e399da1e8f9e95d44b4e6 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 25 Feb 2025 12:05:12 +0100 Subject: [PATCH 428/530] polygon-bor: optionally use non archive PebbleDB --- build/templates/backend/scripts/polygon_archive_bor.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build/templates/backend/scripts/polygon_archive_bor.sh b/build/templates/backend/scripts/polygon_archive_bor.sh index d239a27488..340e981cf4 100644 --- a/build/templates/backend/scripts/polygon_archive_bor.sh +++ b/build/templates/backend/scripts/polygon_archive_bor.sh @@ -9,11 +9,18 @@ DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend BOR_BIN=$INSTALL_DIR/bor +if [ -z "${BOR_PEBBLE_DB}" ]; then + ARCHIVE_FLAGS="--gcmode archive --db.engine leveldb --state.scheme hash" +else + ARCHIVE_FLAGS="--db.engine pebble" +fi + # --bor.heimdall = backend-polygon-heimdall-archive ports.backend_http $BOR_BIN server \ --chain $INSTALL_DIR/genesis.json \ --syncmode full \ --datadir $DATA_DIR \ + $ARCHIVE_FLAGS \ --bor.heimdall http://127.0.0.1:8173 \ --maxpeers 200 \ --bootnodes enode://76316d1cb93c8ed407d3332d595233401250d48f8fbb1d9c65bd18c0495eca1b43ec38ee0ea1c257c0abb7d1f25d649d359cdfe5a805842159cfe36c5f66b7e8@52.78.36.216:30303,enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303,enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303,enode://681ebac58d8dd2d8a6eef15329dfbad0ab960561524cf2dfde40ad646736fe5c244020f20b87e7c1520820bc625cfb487dd71d63a3a3bf0baea2dbb8ec7c79f1@34.240.245.39:30303,enode://93faa5d49ba61fa03f43f7e3c76907a9c72953e8628650eef09f5bddc646d9012916824cdd60da989fd954a852205df9a1fd9661379504c92e103a1ada4c2ceb@148.251.142.52:30314,enode://91f6d9873ee2ceee27b4054ec70844e21fa7c525e8d820d6a09989473f4f883951da75a09ef098d544c0c8a71e9ddd2e649e5b455b137260ba8657b2f96cad2c@178.63.148.12:30308,enode://2776f6f0d1c1e4dfddeb9a4b1c3b1a8777fbb3054b92fc55b405d35603667e974e9cad4408f1036cfc17af03dd1a6270c5cb40f854b94760474516b2d8c0f185@88.198.101.172:30308,enode://157321664e79855ee0f914fd05b21cc29ae3a7e805114d1c26efa1d4d2781f5d5bc4e76ed9d00f26d6138f80cc84ea183894c390fcb0e07100a845aed02f6f40@136.243.210.177:30303,enode://6a5e65c6ef3356bc79a780cf0c7534c299fb8cd7b37db80155830478c1e29d35336fe52a888efdf53c0e9bb9b94e20b5349d68798860f1cf36ae96da2b3826cc@178.63.247.234:30304,enode://d6da5ad18e51d492481b29443bd0f588b59d3f72f0da43a722b07fe2a9223a717c976a1cfe00ad86c557756b2bf297ea56c64a1f3d09bebcb9b81290689d8e33@178.63.197.250:30320,enode://51cbc8b750e28d5a4f250d141c032cf282ea873eb1c533c5156cfc51e6a5117d465b7b39b4e0088ee597ee87b89e06cc6c1ed5e6e050b1c3f638765ee584c4f4@178.63.163.68:30310,enode://6484d4394215c222257c97ac74fdcd6f77ecf00e896c38ef35cc41a44add96da64649139b37cc094e88bb985eb84b04d4c6c78f86bf205c9e112c31254cdc443@54.38.217.112:30303?discport=30346,enode://eb3b67d68daef47badfa683c8b04a1cba6a7c431613b8d7619a013aad38bd8d405eb1d0e41279b4f6fe15b264bd388e88282a77a908247b2d1e0198bd4def57b@148.251.224.230:30315,enode://aa228d96217dd91564e13536f3c2808d2040115c7c50509f26f836275e8e65d1bf9400bce3294760be18c9e00a4bf47026e661ba8d8ce1cf2ced30f0a70e5da8@89.187.163.132:30303?discport=30356,enode://c10ab147ba266a80f34dbc423cd12689434cb2cc1f18ced8f4e5828e23d6943a666c2db0f8464983ccc95666b36099b513d1e45d5df94139e42fbecde25832fa@87.249.137.89:30303?discport=30436,enode://e68049c37b182a36c8913fc0780aea5196c1841c917cbd76f83f1a3a8ae99fcfbd2dfa44e36081668120354439008fe4325ffc0d0176771ec2c1863033d4769e@65.108.199.236:30303,enode://a4c74da28447bacd2b3e8443d0917cca7798bca39dbb48b0e210f0fb6685538ba9d1608a2493424086363f04be5e6a99e6eabb70946ed503448d6b282056f87a@198.244.213.85:30303?discport=30315,enode://e28fce95f52cf3368b7b624c6f83379dec858fcebf6a7ff07e97aa9b9445736a165bf1c51cad7bdf6e3167e2b00b11c7911fc330dabb484998d899a1b01d75cf@148.251.194.252:30303?discport=30892,enode://412fdb01125f6868a188f472cf15f07c8f93d606395b909dd5010f2a4a2702739102cea18abb6437fbacd12e695982a77f28edd9bbdd36635b04e9b3c2948f8d@34.203.27.246:30303?discport=30388,enode://9703d9591cb1013b4fa6ea889e8effe7579aa59c324a6e019d690a13e108ef9b4419698347e4305f05291e644a713518a91b0fc32a3442c1394619e2a9b8251e@79.127.216.33:30303?discport=30349 \ @@ -29,7 +36,6 @@ $BOR_BIN server \ --ws.port {{.Ports.BackendRPC}} \ --ws.api eth,net,web3,debug,txpool,bor \ --ws.origins '*' \ - --gcmode archive \ --txlookuplimit 0 \ --cache 4096 {{end}} \ No newline at end of file From 756b814b6d942a6874eed49ceb01492e1bdf53d4 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Wed, 26 Feb 2025 16:22:49 +0100 Subject: [PATCH 429/530] =?UTF-8?q?prysm=205.2.0=20=E2=86=92=205.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum_archive_consensus.json | 10 +++++----- configs/coins/ethereum_consensus.json | 10 +++++----- .../ethereum_testnet_holesky_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_consensus.json | 10 +++++----- .../ethereum_testnet_sepolia_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_consensus.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 51f757ad53..33ed2c3976 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.2.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", + "version": "5.3.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", "verification_type": "sha256", - "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", + "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", - "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", + "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" } } }, diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index c6213955ba..400b69a2b0 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.2.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", + "version": "5.3.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", "verification_type": "sha256", - "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", + "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", - "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", + "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive_consensus.json b/configs/coins/ethereum_testnet_holesky_archive_consensus.json index 848775b296..9cbc0b2289 100644 --- a/configs/coins/ethereum_testnet_holesky_archive_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.2.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", + "version": "5.3.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", "verification_type": "sha256", - "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", + "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13636 --p2p-udp-port=12636 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", - "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", + "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_consensus.json b/configs/coins/ethereum_testnet_holesky_consensus.json index 69583957bd..9e3a06e708 100644 --- a/configs/coins/ethereum_testnet_holesky_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.2.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", + "version": "5.3.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", "verification_type": "sha256", - "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", + "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", - "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", + "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json index 8563a8e943..4423e5b61e 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.2.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", + "version": "5.3.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", "verification_type": "sha256", - "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", + "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", - "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", + "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json index 1a700fe70d..b64c80935f 100644 --- a/configs/coins/ethereum_testnet_sepolia_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.2.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-amd64", + "version": "5.3.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", "verification_type": "sha256", - "verification_source": "bd8c8756943a75f4b6d120b5a9b215a56d071a4fc986ff91af2a4b01e1ac6aea", + "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.2.0/beacon-chain-v5.2.0-linux-arm64", - "verification_source": "fb5b46749abe8ebfd8cd074215b350a8db305bceda624e70d7ee9e432e480dac" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", + "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" } } }, From 68efe9dec3d4f3b254298dd42811314f4f717255 Mon Sep 17 00:00:00 2001 From: costcould Date: Tue, 25 Feb 2025 00:27:41 +0800 Subject: [PATCH 430/530] chore: remove redundant word for CONTRIBUTING.md Signed-off-by: costcould --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8dd2c2f878..43fe3f9f5b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -75,7 +75,7 @@ also in [build guide](/docs/build.md#on-naming-conventions-and-versioning). You *mainnet* option. In the section *blockbook* update information how to build and configure Blockbook service. Usually they are only -*package_name*, *system_user* and *explorer_url* options. Naming conventions are are described +*package_name*, *system_user* and *explorer_url* options. Naming conventions are described [here](/docs/build.md#on-naming-conventions-and-versioning). Update *package_maintainer* and *package_maintainer_email* options in the section *meta*. From 5fba77fa50fdd76cd868deb3ec201418b71e8e8d Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Wed, 5 Mar 2025 12:29:52 -0700 Subject: [PATCH 431/530] upgrade go-ethereum to v1.15.5 and remove ava-labs coreth dependency --- bchain/coins/avalanche/avalancherpc.go | 38 +-- bchain/coins/avalanche/evm.go | 26 +- bchain/coins/avalanche/types.go | 339 +++++++++++++++++++++++++ bchain/evm_interface.go | 22 ++ go.mod | 67 ++--- go.sum | 218 ++++++---------- 6 files changed, 466 insertions(+), 244 deletions(-) create mode 100644 bchain/coins/avalanche/types.go diff --git a/bchain/coins/avalanche/avalancherpc.go b/bchain/coins/avalanche/avalancherpc.go index 1d12623709..eec69d3e80 100644 --- a/bchain/coins/avalanche/avalancherpc.go +++ b/bchain/coins/avalanche/avalancherpc.go @@ -7,12 +7,9 @@ import ( "net/url" jsontypes "github.com/ava-labs/avalanchego/utils/json" - "github.com/ava-labs/coreth/core/types" - "github.com/ava-labs/coreth/ethclient" - "github.com/ava-labs/coreth/interfaces" - "github.com/ava-labs/coreth/rpc" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" "github.com/golang/glog" "github.com/juju/errors" "github.com/trezor/blockbook/bchain" @@ -52,7 +49,7 @@ func (b *AvalancheRPC) Initialize() error { return nil, nil, err } rc := &AvalancheRPCClient{Client: r} - c := &AvalancheClient{Client: ethclient.NewClient(r)} + c := &AvalancheClient{Client: ethclient.NewClient(r), AvalancheRPCClient: rc} return rc, c, nil } @@ -81,7 +78,7 @@ func (b *AvalancheRPC) Initialize() error { b.RPC = rpcClient b.info = infoClient b.MainNetChainID = MainNet - b.NewBlock = &AvalancheNewBlock{channel: make(chan *types.Header)} + b.NewBlock = &AvalancheNewBlock{channel: make(chan *Header)} b.NewTx = &AvalancheNewTx{channel: make(chan common.Hash)} ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) @@ -134,30 +131,3 @@ func (b *AvalancheRPC) GetChainInfo() (*bchain.ChainInfo, error) { return ci, nil } - -// EthereumTypeEstimateGas returns estimation of gas consumption for given transaction parameters -func (b *AvalancheRPC) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) { - ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) - defer cancel() - msg := interfaces.CallMsg{} - if s, ok := eth.GetStringFromMap("from", params); ok && len(s) > 0 { - msg.From = common.HexToAddress(s) - } - if s, ok := eth.GetStringFromMap("to", params); ok && len(s) > 0 { - a := common.HexToAddress(s) - msg.To = &a - } - if s, ok := eth.GetStringFromMap("data", params); ok && len(s) > 0 { - msg.Data = common.FromHex(s) - } - if s, ok := eth.GetStringFromMap("value", params); ok && len(s) > 0 { - msg.Value, _ = hexutil.DecodeBig(s) - } - if s, ok := eth.GetStringFromMap("gas", params); ok && len(s) > 0 { - msg.Gas, _ = hexutil.DecodeUint64(s) - } - if s, ok := eth.GetStringFromMap("gasPrice", params); ok && len(s) > 0 { - msg.GasPrice, _ = hexutil.DecodeBig(s) - } - return b.Client.EstimateGas(ctx, msg) -} diff --git a/bchain/coins/avalanche/evm.go b/bchain/coins/avalanche/evm.go index 435c0fd5b9..ffff75559c 100644 --- a/bchain/coins/avalanche/evm.go +++ b/bchain/coins/avalanche/evm.go @@ -5,32 +5,32 @@ import ( "math/big" "strings" - "github.com/ava-labs/coreth/core/types" - "github.com/ava-labs/coreth/ethclient" - "github.com/ava-labs/coreth/interfaces" - "github.com/ava-labs/coreth/rpc" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" "github.com/trezor/blockbook/bchain" ) // AvalancheClient wraps a client to implement the EVMClient interface type AvalancheClient struct { - ethclient.Client + *ethclient.Client + *AvalancheRPCClient } // HeaderByNumber returns a block header that implements the EVMHeader interface func (c *AvalancheClient) HeaderByNumber(ctx context.Context, number *big.Int) (bchain.EVMHeader, error) { - h, err := c.Client.HeaderByNumber(ctx, number) - if err != nil { - return nil, err + var head *Header + err := c.AvalancheRPCClient.CallContext(ctx, &head, "eth_getBlockByNumber", bchain.ToBlockNumArg(number), false) + if err == nil && head == nil { + err = ethereum.NotFound } - - return &AvalancheHeader{Header: h}, nil + return &AvalancheHeader{Header: head}, err } // EstimateGas returns the current estimated gas cost for executing a transaction func (c *AvalancheClient) EstimateGas(ctx context.Context, msg interface{}) (uint64, error) { - return c.Client.EstimateGas(ctx, msg.(interfaces.CallMsg)) + return c.Client.EstimateGas(ctx, msg.(ethereum.CallMsg)) } // BalanceAt returns the balance for the given account at a specific block, or latest known block if no block number is provided @@ -72,7 +72,7 @@ func (c *AvalancheRPCClient) CallContext(ctx context.Context, result interface{} // AvalancheHeader wraps a block header to implement the EVMHeader interface type AvalancheHeader struct { - *types.Header + *Header } // Hash returns the block hash as a hex string @@ -102,7 +102,7 @@ type AvalancheClientSubscription struct { // AvalancheNewBlock wraps a block header channel to implement the EVMNewBlockSubscriber interface type AvalancheNewBlock struct { - channel chan *types.Header + channel chan *Header } // Channel returns the underlying channel as an empty interface diff --git a/bchain/coins/avalanche/types.go b/bchain/coins/avalanche/types.go new file mode 100644 index 0000000000..69c7a88727 --- /dev/null +++ b/bchain/coins/avalanche/types.go @@ -0,0 +1,339 @@ +package avalanche + +import ( + "encoding/json" + "errors" + "io" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/crypto/sha3" +) + +var hasherPool = sync.Pool{ + New: func() interface{} { return sha3.NewLegacyKeccak256() }, +} + +func rlpHash(x interface{}) (h common.Hash) { + sha := hasherPool.Get().(crypto.KeccakState) + defer hasherPool.Put(sha) + sha.Reset() + _ = rlp.Encode(sha, x) + _, _ = sha.Read(h[:]) + return h +} + +// Header represents a block header in the Avalanche blockchain. +type Header struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom types.Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *big.Int `json:"difficulty" gencodec:"required"` + Number *big.Int `json:"number" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + Time uint64 `json:"timestamp" gencodec:"required"` + Extra []byte `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash"` + Nonce types.BlockNonce `json:"nonce"` + ExtDataHash common.Hash `json:"extDataHash" gencodec:"required"` + + // BaseFee was added by EIP-1559 and is ignored in legacy headers. + BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` + + // ExtDataGasUsed was added by Apricot Phase 4 and is ignored in legacy + // headers. + // + // It is not a uint64 like GasLimit or GasUsed because it is not possible to + // correctly encode this field optionally with uint64. + ExtDataGasUsed *big.Int `json:"extDataGasUsed" rlp:"optional"` + + // BlockGasCost was added by Apricot Phase 4 and is ignored in legacy + // headers. + BlockGasCost *big.Int `json:"blockGasCost" rlp:"optional"` + + // BlobGasUsed was added by EIP-4844 and is ignored in legacy headers. + BlobGasUsed *uint64 `json:"blobGasUsed" rlp:"optional"` + + // ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers. + ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"` + + // ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers. + ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` +} + +// MarshalJSON marshals as JSON. +func (h Header) MarshalJSON() ([]byte, error) { + type Header struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom types.Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash"` + Nonce types.BlockNonce `json:"nonce"` + ExtDataHash common.Hash `json:"extDataHash" gencodec:"required"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + ExtDataGasUsed *hexutil.Big `json:"extDataGasUsed" rlp:"optional"` + BlockGasCost *hexutil.Big `json:"blockGasCost" rlp:"optional"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` + ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` + Hash common.Hash `json:"hash"` + } + var enc Header + enc.ParentHash = h.ParentHash + enc.UncleHash = h.UncleHash + enc.Coinbase = h.Coinbase + enc.Root = h.Root + enc.TxHash = h.TxHash + enc.ReceiptHash = h.ReceiptHash + enc.Bloom = h.Bloom + enc.Difficulty = (*hexutil.Big)(h.Difficulty) + enc.Number = (*hexutil.Big)(h.Number) + enc.GasLimit = hexutil.Uint64(h.GasLimit) + enc.GasUsed = hexutil.Uint64(h.GasUsed) + enc.Time = hexutil.Uint64(h.Time) + enc.Extra = h.Extra + enc.MixDigest = h.MixDigest + enc.Nonce = h.Nonce + enc.ExtDataHash = h.ExtDataHash + enc.BaseFee = (*hexutil.Big)(h.BaseFee) + enc.ExtDataGasUsed = (*hexutil.Big)(h.ExtDataGasUsed) + enc.BlockGasCost = (*hexutil.Big)(h.BlockGasCost) + enc.BlobGasUsed = (*hexutil.Uint64)(h.BlobGasUsed) + enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas) + enc.ParentBeaconRoot = h.ParentBeaconRoot + enc.Hash = h.Hash() + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (h *Header) UnmarshalJSON(input []byte) error { + type Header struct { + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase *common.Address `json:"miner" gencodec:"required"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom *types.Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest *common.Hash `json:"mixHash"` + Nonce *types.BlockNonce `json:"nonce"` + ExtDataHash *common.Hash `json:"extDataHash" gencodec:"required"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + ExtDataGasUsed *hexutil.Big `json:"extDataGasUsed" rlp:"optional"` + BlockGasCost *hexutil.Big `json:"blockGasCost" rlp:"optional"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` + ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` + } + var dec Header + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ParentHash == nil { + return errors.New("missing required field 'parentHash' for Header") + } + h.ParentHash = *dec.ParentHash + if dec.UncleHash == nil { + return errors.New("missing required field 'sha3Uncles' for Header") + } + h.UncleHash = *dec.UncleHash + if dec.Coinbase == nil { + return errors.New("missing required field 'miner' for Header") + } + h.Coinbase = *dec.Coinbase + if dec.Root == nil { + return errors.New("missing required field 'stateRoot' for Header") + } + h.Root = *dec.Root + if dec.TxHash == nil { + return errors.New("missing required field 'transactionsRoot' for Header") + } + h.TxHash = *dec.TxHash + if dec.ReceiptHash == nil { + return errors.New("missing required field 'receiptsRoot' for Header") + } + h.ReceiptHash = *dec.ReceiptHash + if dec.Bloom == nil { + return errors.New("missing required field 'logsBloom' for Header") + } + h.Bloom = *dec.Bloom + if dec.Difficulty == nil { + return errors.New("missing required field 'difficulty' for Header") + } + h.Difficulty = (*big.Int)(dec.Difficulty) + if dec.Number == nil { + return errors.New("missing required field 'number' for Header") + } + h.Number = (*big.Int)(dec.Number) + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for Header") + } + h.GasLimit = uint64(*dec.GasLimit) + if dec.GasUsed == nil { + return errors.New("missing required field 'gasUsed' for Header") + } + h.GasUsed = uint64(*dec.GasUsed) + if dec.Time == nil { + return errors.New("missing required field 'timestamp' for Header") + } + h.Time = uint64(*dec.Time) + if dec.Extra == nil { + return errors.New("missing required field 'extraData' for Header") + } + h.Extra = *dec.Extra + if dec.MixDigest != nil { + h.MixDigest = *dec.MixDigest + } + if dec.Nonce != nil { + h.Nonce = *dec.Nonce + } + if dec.ExtDataHash == nil { + return errors.New("missing required field 'extDataHash' for Header") + } + h.ExtDataHash = *dec.ExtDataHash + if dec.BaseFee != nil { + h.BaseFee = (*big.Int)(dec.BaseFee) + } + if dec.ExtDataGasUsed != nil { + h.ExtDataGasUsed = (*big.Int)(dec.ExtDataGasUsed) + } + if dec.BlockGasCost != nil { + h.BlockGasCost = (*big.Int)(dec.BlockGasCost) + } + if dec.BlobGasUsed != nil { + h.BlobGasUsed = (*uint64)(dec.BlobGasUsed) + } + if dec.ExcessBlobGas != nil { + h.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas) + } + if dec.ParentBeaconRoot != nil { + h.ParentBeaconRoot = dec.ParentBeaconRoot + } + return nil +} + +func (obj *Header) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + _tmp0 := w.List() + w.WriteBytes(obj.ParentHash[:]) + w.WriteBytes(obj.UncleHash[:]) + w.WriteBytes(obj.Coinbase[:]) + w.WriteBytes(obj.Root[:]) + w.WriteBytes(obj.TxHash[:]) + w.WriteBytes(obj.ReceiptHash[:]) + w.WriteBytes(obj.Bloom[:]) + if obj.Difficulty == nil { + _, _ = w.Write(rlp.EmptyString) + } else { + if obj.Difficulty.Sign() == -1 { + return rlp.ErrNegativeBigInt + } + w.WriteBigInt(obj.Difficulty) + } + if obj.Number == nil { + _, _ = w.Write(rlp.EmptyString) + } else { + if obj.Number.Sign() == -1 { + return rlp.ErrNegativeBigInt + } + w.WriteBigInt(obj.Number) + } + w.WriteUint64(obj.GasLimit) + w.WriteUint64(obj.GasUsed) + w.WriteUint64(obj.Time) + w.WriteBytes(obj.Extra) + w.WriteBytes(obj.MixDigest[:]) + w.WriteBytes(obj.Nonce[:]) + w.WriteBytes(obj.ExtDataHash[:]) + _tmp1 := obj.BaseFee != nil + _tmp2 := obj.ExtDataGasUsed != nil + _tmp3 := obj.BlockGasCost != nil + _tmp4 := obj.BlobGasUsed != nil + _tmp5 := obj.ExcessBlobGas != nil + _tmp6 := obj.ParentBeaconRoot != nil + if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 { + if obj.BaseFee == nil { + _, _ = w.Write(rlp.EmptyString) + } else { + if obj.BaseFee.Sign() == -1 { + return rlp.ErrNegativeBigInt + } + w.WriteBigInt(obj.BaseFee) + } + } + if _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 { + if obj.ExtDataGasUsed == nil { + _, _ = w.Write(rlp.EmptyString) + } else { + if obj.ExtDataGasUsed.Sign() == -1 { + return rlp.ErrNegativeBigInt + } + w.WriteBigInt(obj.ExtDataGasUsed) + } + } + if _tmp3 || _tmp4 || _tmp5 || _tmp6 { + if obj.BlockGasCost == nil { + _, _ = w.Write(rlp.EmptyString) + } else { + if obj.BlockGasCost.Sign() == -1 { + return rlp.ErrNegativeBigInt + } + w.WriteBigInt(obj.BlockGasCost) + } + } + if _tmp4 || _tmp5 || _tmp6 { + if obj.BlobGasUsed == nil { + _, _ = w.Write([]byte{0x80}) + } else { + w.WriteUint64((*obj.BlobGasUsed)) + } + } + if _tmp5 || _tmp6 { + if obj.ExcessBlobGas == nil { + _, _ = w.Write([]byte{0x80}) + } else { + w.WriteUint64((*obj.ExcessBlobGas)) + } + } + if _tmp6 { + if obj.ParentBeaconRoot == nil { + _, _ = w.Write([]byte{0x80}) + } else { + w.WriteBytes(obj.ParentBeaconRoot[:]) + } + } + w.ListEnd(_tmp0) + return w.Flush() +} + +// Hash returns the block hash of the header, which is simply the keccak256 hash of its +// RLP encoding. +func (h *Header) Hash() common.Hash { + return rlpHash(h) +} diff --git a/bchain/evm_interface.go b/bchain/evm_interface.go index 1338b01290..8eb94f54a1 100644 --- a/bchain/evm_interface.go +++ b/bchain/evm_interface.go @@ -2,7 +2,11 @@ package bchain import ( "context" + "fmt" "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" ) // EVMClient provides the necessary client functionality for evm chain sync @@ -57,3 +61,21 @@ type EVMNewTxSubscriber interface { EVMSubscriber Read() (EVMHash, bool) } + +// ToBlockNumArg converts a big.Int to an appropriate string representation of the number if possible +// - valid return values: (hex string, "latest", "pending", "earliest", "finalized", or "safe") +// - invalid return value: "invalid" +func ToBlockNumArg(number *big.Int) string { + if number == nil { + return "latest" + } + if number.Sign() >= 0 { + return hexutil.EncodeBig(number) + } + // It's negative. + if number.IsInt64() { + return rpc.BlockNumber(number.Int64()).String() + } + // It's negative and large, which is invalid. + return fmt.Sprintf("", number) +} diff --git a/go.mod b/go.mod index ecc47fc67b..ba3973bdeb 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,9 @@ module github.com/trezor/blockbook -go 1.22.8 +go 1.23.0 require ( github.com/ava-labs/avalanchego v1.12.1 - github.com/ava-labs/coreth v0.14.0 github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e github.com/deckarep/golang-set v1.8.0 github.com/decred/dcrd/chaincfg/chainhash v1.0.2 @@ -14,7 +13,7 @@ require ( github.com/decred/dcrd/dcrutil/v3 v3.0.0 github.com/decred/dcrd/hdkeychain/v3 v3.0.0 github.com/decred/dcrd/txscript/v3 v3.0.0 - github.com/ethereum/go-ethereum v1.13.14 + github.com/ethereum/go-ethereum v1.15.5 github.com/golang/glog v1.2.1 github.com/gorilla/websocket v1.5.0 github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 @@ -29,32 +28,28 @@ require ( github.com/prometheus/client_golang v1.16.0 github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a github.com/tkrajina/typescriptify-golang-structs v0.1.11 - golang.org/x/crypto v0.31.0 + golang.org/x/crypto v0.32.0 google.golang.org/protobuf v1.34.2 ) require ( - github.com/BurntSushi/toml v1.4.0 // indirect - github.com/DataDog/zstd v1.5.2 // indirect github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29 // indirect - github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/aead/siphash v1.0.1 // indirect github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.10.0 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/bits-and-blooms/bitset v1.17.0 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/consensys/bavard v0.1.13 // indirect - github.com/consensys/gnark-crypto v0.12.1 // indirect - github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect + github.com/consensys/bavard v0.1.22 // indirect + github.com/consensys/gnark-crypto v0.14.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect + github.com/crate-crypto/go-kzg-4844 v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dchest/blake256 v1.0.0 // indirect github.com/dchest/siphash v1.2.1 // indirect - github.com/deckarep/golang-set/v2 v2.1.0 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/decred/base58 v1.0.3 // indirect github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect github.com/decred/dcrd/crypto/ripemd160 v1.0.1 // indirect @@ -63,62 +58,32 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/decred/dcrd/wire v1.4.0 // indirect github.com/decred/slog v1.1.0 // indirect - github.com/ethereum/c-kzg-4844 v0.4.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect - github.com/go-logr/stdr v1.2.2 // indirect + github.com/ethereum/c-kzg-4844 v1.0.0 // indirect + github.com/ethereum/go-verkle v0.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect - github.com/google/renameio/v2 v2.0.0 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/gorilla/rpc v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect - github.com/holiman/uint256 v1.2.4 // indirect + github.com/holiman/uint256 v1.3.2 // indirect github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect - github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/stretchr/testify v1.9.0 // indirect - github.com/supranational/blst v0.3.13 // indirect + github.com/stretchr/testify v1.10.0 // indirect + github.com/supranational/blst v0.3.14 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/tkrajina/go-reflector v0.5.5 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opentelemetry.io/otel v1.22.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 // indirect - go.opentelemetry.io/otel/metric v1.22.0 // indirect - go.opentelemetry.io/otel/sdk v1.22.0 // indirect - go.opentelemetry.io/otel/trace v1.22.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect - go.uber.org/mock v0.4.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect - golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.28.0 // indirect golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect - google.golang.org/grpc v1.66.0 // indirect + golang.org/x/sys v0.29.0 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 86b6c2c60b..6d714b38e1 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,25 @@ -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c h1:8bYNmjELeCj7DEh/dN7zFzkJ0upK3GkbOC/0u1HMQ5s= github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c/go.mod h1:DwgC62sAn4RgH4L+O8REgcE7f0XplHPNeRYFy+ffy1M= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29 h1:B11BryeZQ1LrAzzM0lCpblwleB7SyxPfvN2AsNbyvQc= github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29/go.mod h1:+39XiGr9m9TPY49sG4XIH5CVaRxHGFWT0U4MOY6dy3o= -github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= -github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= +github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= -github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= -github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/ava-labs/avalanchego v1.12.1 h1:NL04K5+gciC2XqGZbDcIu0nuVApEddzc6YyujRBv+u8= github.com/ava-labs/avalanchego v1.12.1/go.mod h1:xnVvN86jhxndxfS8e0U7v/0woyfx9BhX/feld7XDjDE= -github.com/ava-labs/coreth v0.14.0 h1:zOrgWXp67LBFj9UMcoXn0UTEv7GINyzUPjh9NrF8qm0= -github.com/ava-labs/coreth v0.14.0/go.mod h1:SN79q6EKPd0viuUZMIg0xxZyUfnvSalZRo8HDJR3JHs= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= -github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.17.0 h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI= +github.com/bits-and-blooms/bitset v1.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e h1:D64GF/Xr5zSUnM3q1Jylzo4sK7szhP/ON+nb2DB5XJA= github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= @@ -41,33 +31,30 @@ github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= -github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= -github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= -github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= -github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= +github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= -github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= -github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= -github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= -github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= -github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/consensys/bavard v0.1.22 h1:Uw2CGvbXSZWhqK59X0VG/zOjpTFuOMcPLStrp1ihI0A= +github.com/consensys/bavard v0.1.22/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs= +github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E= +github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= +github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4= +github.com/crate-crypto/go-kzg-4844 v1.1.0/go.mod h1:JolLjpSff1tCCJKaJx4psrlEdlXuJEC996PL3tTAFks= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -78,8 +65,8 @@ github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= -github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= -github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/base58 v1.0.3 h1:KGZuh8d1WEMIrK0leQRM47W85KqCAdl2N+uagbctdDI= github.com/decred/base58 v1.0.3/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbiSM78E= github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU= @@ -111,25 +98,14 @@ github.com/decred/dcrd/wire v1.4.0 h1:KmSo6eTQIvhXS0fLBQ/l7hG7QLcSJQKSwSyzSqJYDk github.com/decred/dcrd/wire v1.4.0/go.mod h1:WxC/0K+cCAnBh+SKsRjIX9YPgvrjhmE+6pZlel1G7Ro= github.com/decred/slog v1.1.0 h1:uz5ZFfmaexj1rEDgZvzQ7wjGkoSPjw2LCh8K+K1VrW4= github.com/decred/slog v1.1.0/go.mod h1:kVXlGnt6DHy2fV5OjSeuvCJ0OmlmTF6LFpEPMu/fOY0= -github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= -github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.13.14 h1:EwiY3FZP94derMCIam1iW4HFVrSgIcpsu0HwTQtm6CQ= -github.com/ethereum/go-ethereum v1.13.14/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= -github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= -github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= -github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= -github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= -github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= +github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.15.5 h1:Fo2TbBWC61lWVkFw9tsMoHCNX1ndpuaQBRJ8H6xLUPo= +github.com/ethereum/go-ethereum v1.15.5/go.mod h1:1LG2LnMOx2yPRHR/S+xuipXH29vPr6BIH6GElD8N/fo= +github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= +github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= @@ -137,42 +113,33 @@ github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= -github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= -github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= @@ -191,16 +158,16 @@ github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b/go.mod h1:63prj8cnj0t github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc h1:I1QApI4r4SG8Hh45H0yRjVnThWRn1oOwod76rrAe5KE= github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= -github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= +github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= github.com/linxGnu/grocksdb v1.7.7 h1:b6o8gagb4FL+P55qUzPchBR/C0u1lWjJOWQSWbhvTWg= github.com/linxGnu/grocksdb v1.7.7/go.mod h1:0hTf+iA+GOr0jDX4CgIYyJZxqOH9XlBh6KVj8+zmF34= github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe h1:khZWpHuxJNh2EGzBbaS6EQ2d6KxgK31WeG0TnlTMUD4= @@ -216,8 +183,8 @@ github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde h1:Tz7 github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde/go.mod h1:p35TWcm7GkAwvPcUCEq4H+yTm0gA8Aq7UvGnbK6olQk= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -231,8 +198,6 @@ github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqky github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E= -github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= @@ -241,6 +206,16 @@ github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/pebbe/zmq4 v1.2.1 h1:jrXQW3mD8Si2mcSY/8VBs2nNkK/sKCOEM0rHAfxyc8c= github.com/pebbe/zmq4 v1.2.1/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM= +github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= +github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= +github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= +github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= +github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29 h1:awILOeL107zIYvPB1zhkz6ZTp0AaMpLGMoV16DMairA= github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29/go.mod h1:ATZjpmb9u55Kcrd5M/ca/40H73BZLhduMzCmGwpfWw0= github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e h1:WrnL52yXO0jNpHC7UbthJl9mnHPHY7bW3xzmWIuWzh8= @@ -265,25 +240,18 @@ github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sanity-io/litter v1.5.1 h1:dwnrSypP6q56o3lFxTU+t2fwQ9A+U5qrXVO4Qg9KwVU= -github.com/sanity-io/litter v1.5.1/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0= github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a h1:q2+wHBv8gDQRRPfxvRez8etJUp9VNnBDQhiUW4W5AKg= github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a/go.mod h1:FdhEqBlgflrdbBs+Wh94EXSNJT+s6DTVvsHGMo0+u80= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= -github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= -github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= +github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/thepudds/fzgen v0.4.2 h1:HlEHl5hk2/cqEomf2uK5SA/FeJc12s/vIHmOG+FbACw= -github.com/thepudds/fzgen v0.4.2/go.mod h1:kHCWdsv5tdnt32NIHYDdgq083m6bMtaY0M+ipiO9xWE= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= @@ -292,82 +260,40 @@ github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQ github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= github.com/tkrajina/typescriptify-golang-structs v0.1.11 h1:zEIVczF/iWgs4eTY7NQqbBe23OVlFVk9sWLX/FDYi4Q= github.com/tkrajina/typescriptify-golang-structs v0.1.11/go.mod h1:sjU00nti/PMEOZb07KljFlR+lJ+RotsC0GBQMv9EKls= -github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= -github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= -go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= -go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= -go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= -go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= -go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= -go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= -go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= -google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -377,8 +303,8 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 9793299381568c9909db66fd8bf163631dd4fa40 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 6 Mar 2025 16:28:25 +0100 Subject: [PATCH 432/530] Disable warnings as errors for rocksdb build --- build/docker/bin/Dockerfile | 2 +- build/docker/bin/Makefile | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/build/docker/bin/Dockerfile b/build/docker/bin/Dockerfile index f56d828b31..62720f4c0c 100644 --- a/build/docker/bin/Dockerfile +++ b/build/docker/bin/Dockerfile @@ -39,7 +39,7 @@ RUN echo -n "GOPATH: " && echo $GOPATH # install rocksdb RUN cd /opt && git clone -b $ROCKSDB_VERSION --depth 1 https://github.com/facebook/rocksdb.git -RUN cd /opt/rocksdb && CFLAGS=-fPIC CXXFLAGS=-fPIC PORTABLE=$PORTABLE_ROCKSDB make -j 4 release +RUN cd /opt/rocksdb && CFLAGS=-fPIC CXXFLAGS=-fPIC PORTABLE=$PORTABLE_ROCKSDB DISABLE_WARNING_AS_ERROR=1 make -j 4 release RUN strip /opt/rocksdb/ldb /opt/rocksdb/sst_dump && \ cp /opt/rocksdb/ldb /opt/rocksdb/sst_dump /build diff --git a/build/docker/bin/Makefile b/build/docker/bin/Makefile index 3636ddd130..96402edd9f 100644 --- a/build/docker/bin/Makefile +++ b/build/docker/bin/Makefile @@ -38,4 +38,3 @@ prepare-sources: mkdir -p $(BLOCKBOOK_BASE) cp -r /src $(BLOCKBOOK_SRC) cd $(BLOCKBOOK_SRC) && go mod download - sed -i 's/wsMessageSizeLimit\ =\ 15\ \*\ 1024\ \*\ 1024/wsMessageSizeLimit = 80 * 1024 * 1024/g' $(GOPATH)/pkg/mod/github.com/ava-labs/coreth*/rpc/websocket.go From 1d105b95094736de67045db217ce560f26ed62eb Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 6 Mar 2025 17:17:03 +0100 Subject: [PATCH 433/530] Improve error handling in sync block loop --- db/sync.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/db/sync.go b/db/sync.go index f04512ec10..5c3edc52e9 100644 --- a/db/sync.go +++ b/db/sync.go @@ -336,7 +336,11 @@ func (w *SyncWorker) ConnectBlocksParallel(lower, higher uint32) error { glog.Error("getBlockWorker ", i, " connect block error ", err, ". Exiting...") return } - glog.Error("getBlockWorker ", i, " connect block error ", err, ". Retrying...") + if err == bchain.ErrBlockNotFound { + glog.Error("getBlockWorker ", i, " connect block ", hh.height, " ", hh.hash, " error ", err, ". Retrying...") + } else { + glog.Error("getBlockWorker ", i, " connect block error ", err, ". Retrying...") + } w.metrics.IndexResyncErrors.With(common.Labels{"error": "failure"}).Inc() time.Sleep(time.Millisecond * 500) } else { From 523724eadcc2695948ef5cdd61b690ffcd4e224b Mon Sep 17 00:00:00 2001 From: JoHnY Date: Fri, 7 Mar 2025 13:03:30 +0100 Subject: [PATCH 434/530] =?UTF-8?q?eth=20(+testnets)=202.61.1=20=E2=86=92?= =?UTF-8?q?=202.61.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_holesky.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_archive.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 24c5a56ac8..c21dfaa20f 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.1", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", + "version": "2.61.3", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", + "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", - "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", + "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index b89009dacf..eb0bb87c12 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.1", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", + "version": "2.61.3", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", + "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", - "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", + "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" } } }, diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index 9aa77b3061..8b5971ae5b 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.1", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", + "version": "2.61.3", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", + "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", - "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", + "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 001974ece2..0cb9a2310f 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.1", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", + "version": "2.61.3", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", + "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", - "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", + "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 575f7374a8..3972b98923 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.1", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", + "version": "2.61.3", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", + "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", - "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", + "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 69d6ec7d35..13ebd2e987 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.1", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_amd64.tar.gz", + "version": "2.61.3", + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "d92ae402d47a3564a231448bbc0365dde7bb5ea32b2f24a7b841eddf070ca09a", + "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.1/erigon_v2.61.1_linux_arm64.tar.gz", - "verification_source": "a368f4199e1f6db51f055c27b1a71925aecda458e2142b13a4f30ecc66a7a7a3" + "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", + "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" } } }, From 0562cab0184b09b47f911c8096fa3d967b1abb49 Mon Sep 17 00:00:00 2001 From: TheTrunk Date: Sun, 16 Mar 2025 10:28:59 +0800 Subject: [PATCH 435/530] update flux addnodes --- configs/coins/flux.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/configs/coins/flux.json b/configs/coins/flux.json index ec85012425..5752d3a5f1 100644 --- a/configs/coins/flux.json +++ b/configs/coins/flux.json @@ -39,10 +39,12 @@ "client_config_file": "bitcoin_like_client.conf", "additional_params": { "addnode": [ - "explorer.zel.cash", - "explorer2.zel.cash", - "explorer.zelcash.online", - "explorer-asia.zel.cash" + "explorer.runonflux.com", + "explorer.runonflux.io", + "blockbook.runonflux.com", + "blockbook.runonflux.io", + "explorer.flux.zelcore.io", + "blockbook.flux.zelcore.io" ] } }, From 304ddc2967288b325abd029c89737ed981611678 Mon Sep 17 00:00:00 2001 From: yudrywet Date: Wed, 12 Mar 2025 22:37:02 +0800 Subject: [PATCH 436/530] chore: make function comment match function name Signed-off-by: yudrywet --- api/worker.go | 2 +- common/currencyrateticker.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/worker.go b/api/worker.go index e10817628f..42909198f0 100644 --- a/api/worker.go +++ b/api/worker.go @@ -2282,7 +2282,7 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) { }, nil } -// GetBlock returns paged data about block +// GetBlockRaw returns paged data about block func (w *Worker) GetBlockRaw(bid string) (*BlockRaw, error) { hash := w.getBlockHashBlockID(bid) if hash == "" { diff --git a/common/currencyrateticker.go b/common/currencyrateticker.go index 7043f6ebda..2c1afe534c 100644 --- a/common/currencyrateticker.go +++ b/common/currencyrateticker.go @@ -57,7 +57,7 @@ func (t *CurrencyRatesTicker) ConvertTokenToBase(value float64, token string) fl return 0 } -// ConvertTokenToBase converts token value to toCurrency currency +// ConvertToken converts token value to toCurrency currency func (t *CurrencyRatesTicker) ConvertToken(value float64, token string, toCurrency string) float64 { baseValue := t.ConvertTokenToBase(value, token) if baseValue > 0 { From 4bb7744ac47a812fd55beecc192f2e15dd80c6d4 Mon Sep 17 00:00:00 2001 From: David Kedves Date: Thu, 20 Mar 2025 10:12:09 +0100 Subject: [PATCH 437/530] Add UnmarshalJSON method to Amount --- api/types.go | 17 +++++++++++++++++ api/types_test.go | 9 +++++++++ 2 files changed, 26 insertions(+) diff --git a/api/types.go b/api/types.go index 0525e3ce50..7d47ba305f 100644 --- a/api/types.go +++ b/api/types.go @@ -3,8 +3,10 @@ package api import ( "encoding/json" "errors" + "fmt" "math/big" "sort" + "strings" "time" "github.com/trezor/blockbook/bchain" @@ -87,6 +89,21 @@ func (a *Amount) MarshalJSON() (out []byte, err error) { return []byte(`"` + (*big.Int)(a).String() + `"`), nil } +func (a *Amount) UnmarshalJSON(data []byte) error { + s := strings.Trim(string(data), "\"") + if len(s) > 0 { + bigValue, parsed := new(big.Int).SetString(s, 10) + if !parsed { + return fmt.Errorf("couldn't parse number: %s", s) + } + *a = Amount(*bigValue) + } else { + // assuming empty string means zero + *a = Amount{} + } + return nil +} + func (a *Amount) String() string { if a == nil { return "" diff --git a/api/types_test.go b/api/types_test.go index d2d53380a3..12bb8fec9f 100644 --- a/api/types_test.go +++ b/api/types_test.go @@ -47,6 +47,15 @@ func TestAmount_MarshalJSON(t *testing.T) { if !reflect.DeepEqual(string(b), tt.want) { t.Errorf("json.Marshal() = %v, want %v", string(b), tt.want) } + var parsed amounts + err = json.Unmarshal(b, &parsed) + if err != nil { + t.Errorf("json.Unmarshal() error = %v", err) + return + } + if !reflect.DeepEqual(parsed, tt.a) { + t.Errorf("json.Unmarshal() = %v, want %v", parsed, tt.a) + } }) } } From 8c1ff8813785f48200689b2b752be4bc887ae5c0 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Tue, 18 Mar 2025 09:22:11 +0100 Subject: [PATCH 438/530] =?UTF-8?q?eth=20(+testnets)=202.61.3=20=E2=86=92?= =?UTF-8?q?=203.0.0-rc3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_holesky.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_archive.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index c21dfaa20f..7094fc9764 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.3", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", + "version": "3.0.0-rc3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", + "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", - "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", + "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index eb0bb87c12..d3dc143f34 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.3", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", + "version": "3.0.0-rc3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", + "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", - "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", + "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" } } }, diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index 8b5971ae5b..69dc6e3046 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.3", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", + "version": "3.0.0-rc3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", + "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", - "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", + "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 0cb9a2310f..d9598d6217 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.3", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", + "version": "3.0.0-rc3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", + "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", - "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", + "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 3972b98923..00632d6332 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.3", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", + "version": "3.0.0-rc3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", + "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", - "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", + "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 13ebd2e987..7f41646920 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "2.61.3", - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_amd64.tar.gz", + "version": "3.0.0-rc3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "51223910eec38212b601a5b919ca28e4428630bd3cffa112490d79ccc37d0b05", + "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ledgerwatch/erigon/releases/download/v2.61.3/erigon_v2.61.3_linux_arm64.tar.gz", - "verification_source": "d40340ca469929be8e3f8ddbf3e0159904d6ed3cf2744301eaf25e21a3f4fef5" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", + "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" } } }, From 175c2c37822408648a32f2ecacc4e8597e45fd55 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 18 Mar 2025 11:58:18 +0100 Subject: [PATCH 439/530] Update flags for erigon 3.0.0 --- configs/coins/ethereum.json | 4 ++-- configs/coins/ethereum_archive.json | 4 ++-- configs/coins/ethereum_testnet_holesky.json | 4 ++-- configs/coins/ethereum_testnet_holesky_archive.json | 4 ++-- configs/coins/ethereum_testnet_sepolia.json | 4 ++-- configs/coins/ethereum_testnet_sepolia_archive.json | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 7094fc9764..3321fb7501 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -28,7 +28,7 @@ "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -73,4 +73,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index d3dc143f34..6b63229920 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -28,7 +28,7 @@ "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -76,4 +76,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index 69dc6e3046..80cbea283a 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -28,7 +28,7 @@ "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -68,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index d9598d6217..622171cee7 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -29,7 +29,7 @@ "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -76,4 +76,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 00632d6332..4a615ce730 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -28,7 +28,7 @@ "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -68,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 7f41646920..65f1a9e822 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -29,7 +29,7 @@ "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --prune c --prune.c.older 1000000 -torrent.download.rate 32mb --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -74,4 +74,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} From 714af6d88c5f30f1e1bc084602d7814e86910c4f Mon Sep 17 00:00:00 2001 From: JoHnY Date: Wed, 26 Mar 2025 10:24:52 +0100 Subject: [PATCH 440/530] =?UTF-8?q?eth=20(+testnets)=203.0.0-rc3=20?= =?UTF-8?q?=E2=86=92=203.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 12 ++++++------ configs/coins/ethereum_archive.json | 12 ++++++------ configs/coins/ethereum_testnet_holesky.json | 12 ++++++------ configs/coins/ethereum_testnet_holesky_archive.json | 12 ++++++------ configs/coins/ethereum_testnet_sepolia.json | 12 ++++++------ configs/coins/ethereum_testnet_sepolia_archive.json | 12 ++++++------ 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 3321fb7501..3880bc15c3 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0-rc3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", + "version": "3.0.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", + "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", - "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", + "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" } } }, @@ -73,4 +73,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 6b63229920..be38f9dd85 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0-rc3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", + "version": "3.0.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", + "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", - "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", + "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" } } }, @@ -76,4 +76,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index 80cbea283a..c8cc39c91b 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0-rc3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", + "version": "3.0.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", + "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", - "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", + "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" } } }, @@ -68,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 622171cee7..86c328f968 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0-rc3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", + "version": "3.0.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", + "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", - "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", + "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" } } }, @@ -76,4 +76,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 4a615ce730..3a9e84ca96 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0-rc3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", + "version": "3.0.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", + "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", - "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", + "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" } } }, @@ -68,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 65f1a9e822..49c376bb23 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0-rc3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_amd64.tar.gz", + "version": "3.0.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "b6db16ce2eb8785861ab7e67b2dd7becb51f20d6c2127d9114aed1966dcd2213", + "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0-rc3/erigon_v3.0.0-rc3_linux_arm64.tar.gz", - "verification_source": "c686f5d683dddab2e1b48d8de412f3114dba14d0fd3c57db91f3bc8cee9797dd" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", + "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" } } }, @@ -74,4 +74,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file From f98c643df7720ec6b878d0f53a417b082bfa55c7 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Thu, 27 Mar 2025 10:40:30 +0100 Subject: [PATCH 441/530] =?UTF-8?q?polygon-heimdall=201.2.0=20=E2=86=92=20?= =?UTF-8?q?1.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/polygon_heimdall.json | 10 +++++----- configs/coins/polygon_heimdall_archive.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/configs/coins/polygon_heimdall.json b/configs/coins/polygon_heimdall.json index 80a2201450..5a18874b40 100644 --- a/configs/coins/polygon_heimdall.json +++ b/configs/coins/polygon_heimdall.json @@ -16,16 +16,16 @@ "package_name": "backend-polygon-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.2.0", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.0.tar.gz", + "version": "1.2.1", + "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.1.tar.gz", "verification_type": "sha256", - "verification_source": "8d49e6e9e4115d46ce3cc7ddf2d8ab3c471eb54c6278759ce27b3fdce96cc736", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.0.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "d8c34ce7c5ebc5b709f1ca4903209e35d238c3cef4b6b77bde2ee0e49f5123b4", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.1.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, diff --git a/configs/coins/polygon_heimdall_archive.json b/configs/coins/polygon_heimdall_archive.json index 81fa3f447b..b26da1dc5c 100644 --- a/configs/coins/polygon_heimdall_archive.json +++ b/configs/coins/polygon_heimdall_archive.json @@ -16,16 +16,16 @@ "package_name": "backend-polygon-archive-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.2.0", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.0.tar.gz", + "version": "1.2.1", + "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.1.tar.gz", "verification_type": "sha256", - "verification_source": "8d49e6e9e4115d46ce3cc7ddf2d8ab3c471eb54c6278759ce27b3fdce96cc736", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.0.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "d8c34ce7c5ebc5b709f1ca4903209e35d238c3cef4b6b77bde2ee0e49f5123b4", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.1.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, From ac7e287c8e57b8578438b96b0b55cf6467f7078d Mon Sep 17 00:00:00 2001 From: JoHnY Date: Thu, 27 Mar 2025 11:18:10 +0100 Subject: [PATCH 442/530] =?UTF-8?q?polygon-bor=202.0.0=20=E2=86=92=202.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/polygon.json | 12 ++++++------ configs/coins/polygon_archive.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/polygon.json b/configs/coins/polygon.json index 2904634ab6..4f24eb0bd4 100644 --- a/configs/coins/polygon.json +++ b/configs/coins/polygon.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "2.0.0", - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.0/bor-v2.0.0-amd64.deb", + "version": "2.0.1", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.1/bor-v2.0.1-amd64.deb", "verification_type": "sha256", - "verification_source": "942667f84732e25474b48f1fa6a13b513194f954344e3fa005d7165a8dd15d9a", + "verification_source": "879d72f58a1d779d00c27446b4e5652f8e22a123e8ea09f5b5757092920109fd", "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.0/bor-v2.0.0-arm64.deb", - "verification_source": "914d17ff48258396d228b5feb4aeac90dbd47d272619a87e384c97385b53ffcc" + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.1/bor-v2.0.1-arm64.deb", + "verification_source": "9a77d0cdaa7d0c2d3152fd184a47905b24b6b05777d44f5cda2cb406fb82a3c5" } } }, diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index 2d45921073..4621e39f68 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-archive-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "2.0.0", - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.0/bor-v2.0.0-amd64.deb", + "version": "2.0.1", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.1/bor-v2.0.1-amd64.deb", "verification_type": "sha256", - "verification_source": "942667f84732e25474b48f1fa6a13b513194f954344e3fa005d7165a8dd15d9a", + "verification_source": "879d72f58a1d779d00c27446b4e5652f8e22a123e8ea09f5b5757092920109fd", "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.0/bor-v2.0.0-arm64.deb", - "verification_source": "914d17ff48258396d228b5feb4aeac90dbd47d272619a87e384c97385b53ffcc" + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.1/bor-v2.0.1-arm64.deb", + "verification_source": "9a77d0cdaa7d0c2d3152fd184a47905b24b6b05777d44f5cda2cb406fb82a3c5" } } }, From 20331693f470d2bce21aaa8e8d4076113f89f73c Mon Sep 17 00:00:00 2001 From: evenevent Date: Thu, 27 Mar 2025 17:53:10 +0800 Subject: [PATCH 443/530] refactor: use the built-in max/min to simplify the code Signed-off-by: evenevent --- contrib/scripts/check-and-generate-port-registry.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/contrib/scripts/check-and-generate-port-registry.go b/contrib/scripts/check-and-generate-port-registry.go index 88ab727704..9b2eebc6f8 100755 --- a/contrib/scripts/check-and-generate-port-registry.go +++ b/contrib/scripts/check-and-generate-port-registry.go @@ -304,7 +304,7 @@ func writeTable(w io.Writer, header []string, slice PortInfoSlice) { padding[column] = len(header[column]) for _, row := range rows { - padding[column] = maxInt(padding[column], len(row[column])) + padding[column] = max(padding[column], len(row[column])) } } @@ -322,13 +322,6 @@ func writeTable(w io.Writer, header []string, slice PortInfoSlice) { } } -func maxInt(a, b int) int { - if a > b { - return a - } - return b -} - func paddedRow(row []string, padding []int) []string { out := make([]string, len(row)) for i := 0; i < len(row); i++ { From a6c6ef0abcf24e73de37b83103c32b830716b37c Mon Sep 17 00:00:00 2001 From: JoHnY Date: Thu, 27 Mar 2025 13:50:56 +0100 Subject: [PATCH 444/530] =?UTF-8?q?prysm=205.3.0=20=E2=86=92=205.3.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum_archive_consensus.json | 10 +++++----- configs/coins/ethereum_consensus.json | 10 +++++----- .../ethereum_testnet_holesky_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_consensus.json | 10 +++++----- .../ethereum_testnet_sepolia_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_consensus.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 33ed2c3976..b41f5ae557 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", + "version": "5.3.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", "verification_type": "sha256", - "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", + "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", - "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", + "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" } } }, diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 400b69a2b0..fc8ec2995d 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", + "version": "5.3.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", "verification_type": "sha256", - "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", + "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", - "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", + "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive_consensus.json b/configs/coins/ethereum_testnet_holesky_archive_consensus.json index 9cbc0b2289..4c4d7c422d 100644 --- a/configs/coins/ethereum_testnet_holesky_archive_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", + "version": "5.3.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", "verification_type": "sha256", - "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", + "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13636 --p2p-udp-port=12636 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", - "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", + "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_consensus.json b/configs/coins/ethereum_testnet_holesky_consensus.json index 9e3a06e708..e72a474252 100644 --- a/configs/coins/ethereum_testnet_holesky_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", + "version": "5.3.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", "verification_type": "sha256", - "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", + "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", - "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", + "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json index 4423e5b61e..198196e268 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", + "version": "5.3.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", "verification_type": "sha256", - "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", + "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", - "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", + "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json index b64c80935f..1cfd74fb35 100644 --- a/configs/coins/ethereum_testnet_sepolia_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-amd64", + "version": "5.3.2", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", "verification_type": "sha256", - "verification_source": "76e48dafd14e3d7f9e762e4aec3423c6e21ff0459f35ee99f8eee314cd8ea408", + "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.0/beacon-chain-v5.3.0-linux-arm64", - "verification_source": "40c0994942185ba6776b38af20a81cf15a102bedcfcb557aff94fd2f74d9cbd4" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", + "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" } } }, From 74e38bb40c78f5d6d48119686a2496da9690b3d6 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 2 Apr 2025 14:33:52 +0200 Subject: [PATCH 445/530] Update ethereum fix contracts metadata json --- configs/contract-fix/ethereum.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/contract-fix/ethereum.json b/configs/contract-fix/ethereum.json index 5b6dc03efc..767f09681e 100644 --- a/configs/contract-fix/ethereum.json +++ b/configs/contract-fix/ethereum.json @@ -1 +1 @@ -[{"type":"ERC20","contract":"0xC19B6A4Ac7C7Cc24459F08984Bbd09664af17bD1","name":"Sensorium","symbol":"SENSO","decimals":0,"createdInBlock":11098997}] +[{"standard":"ERC20","contract":"0xC19B6A4Ac7C7Cc24459F08984Bbd09664af17bD1","name":"Sensorium","symbol":"SENSO","decimals":0,"createdInBlock":11098997},{"standard":"ERC20","contract":"0xd5F7838F5C461fefF7FE49ea5ebaF7728bB0ADfa","name":"mETH","symbol":"mETH","decimals":18,"createdInBlock":18290587}] From fbe602e925c3e49024336abd73a8c25b515ad0b5 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Thu, 10 Apr 2025 12:49:10 +0200 Subject: [PATCH 446/530] =?UTF-8?q?eth=20(+testnets)=203.0.0=20=E2=86=92?= =?UTF-8?q?=203.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_holesky.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_archive.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 3880bc15c3..0749118bf1 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", + "version": "3.0.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", + "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", - "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", + "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index be38f9dd85..5310556a22 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", + "version": "3.0.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", + "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", - "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", + "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" } } }, diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index c8cc39c91b..3540affc47 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", + "version": "3.0.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", + "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", - "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", + "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 86c328f968..a54111fa8c 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", + "version": "3.0.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", + "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", - "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", + "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 3a9e84ca96..7d7924f612 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", + "version": "3.0.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", + "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", - "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", + "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 49c376bb23..fbf4db9b5a 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_amd64.tar.gz", + "version": "3.0.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "553b5e33d96caf9f07341c78387c55ade18c73d65a2e006bc93b761d05f03218", + "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.0/erigon_v3.0.0_linux_arm64.tar.gz", - "verification_source": "50f360f0f7b3bf8efe12adbede1122c2e5ab4b87cb7c089a6716811c139bc1f0" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", + "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" } } }, From 632864d2fe4f0e7d4b736eba4a2f9a50db37cb05 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Wed, 2 Apr 2025 13:07:10 +0200 Subject: [PATCH 447/530] heimdall 1.2.1 -> 1.2.2 --- configs/coins/polygon_heimdall.json | 12 ++++++------ configs/coins/polygon_heimdall_archive.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/polygon_heimdall.json b/configs/coins/polygon_heimdall.json index 5a18874b40..9b723e82da 100644 --- a/configs/coins/polygon_heimdall.json +++ b/configs/coins/polygon_heimdall.json @@ -16,16 +16,16 @@ "package_name": "backend-polygon-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.2.1", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.1.tar.gz", + "version": "1.2.2", + "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.2.tar.gz", "verification_type": "sha256", - "verification_source": "d8c34ce7c5ebc5b709f1ca4903209e35d238c3cef4b6b77bde2ee0e49f5123b4", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.1.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "ada133877c9a3e18dd2f7ad24c66f7110013e1c59489ad76cf78af53ddc518b2", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.2.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.2/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -37,4 +37,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/polygon_heimdall_archive.json b/configs/coins/polygon_heimdall_archive.json index b26da1dc5c..8f93492b76 100644 --- a/configs/coins/polygon_heimdall_archive.json +++ b/configs/coins/polygon_heimdall_archive.json @@ -16,16 +16,16 @@ "package_name": "backend-polygon-archive-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.2.1", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.1.tar.gz", + "version": "1.2.2", + "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.2.tar.gz", "verification_type": "sha256", - "verification_source": "d8c34ce7c5ebc5b709f1ca4903209e35d238c3cef4b6b77bde2ee0e49f5123b4", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.1.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "ada133877c9a3e18dd2f7ad24c66f7110013e1c59489ad76cf78af53ddc518b2", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.2.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.2/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -37,4 +37,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} From bcc68b89399d86c43b4e430455579450815a102a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrej=20=C5=A0korupa?= Date: Tue, 15 Apr 2025 15:19:37 +0200 Subject: [PATCH 448/530] =?UTF-8?q?btc=20(+testnet):=2028.1=20=E2=86=92=20?= =?UTF-8?q?29.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/bitcoin.json | 10 +++++----- configs/coins/bitcoin_regtest.json | 10 +++++----- configs/coins/bitcoin_signet.json | 10 +++++----- configs/coins/bitcoin_testnet.json | 10 +++++----- configs/coins/bitcoin_testnet4.json | 10 +++++----- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index 4148605c58..79bd98266b 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": "28.1", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-linux-gnu.tar.gz", + "version": "29.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "07f77afd326639145b9ba9562912b2ad2ccec47b8a305bd075b4f4cb127b7ed7", + "verification_source": "a681e4f6ce524c338a105f214613605bac6c33d58c31dc5135bbc02bc458bb6c", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -43,8 +43,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-aarch64-linux-gnu.tar.gz", - "verification_source": "6ddb6990690bd4c9a9f4319ed6f6e9c995c85ce5530ee9f120e80ce09e090c44" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-aarch64-linux-gnu.tar.gz", + "verification_source": "7922ac99363dd28f79e57ef7098581fd48ebd1119b412b07e73b1fd19fd0443f" } } }, diff --git a/configs/coins/bitcoin_regtest.json b/configs/coins/bitcoin_regtest.json index 825dbd8bd1..680247808b 100644 --- a/configs/coins/bitcoin_regtest.json +++ b/configs/coins/bitcoin_regtest.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-regtest", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "28.1", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-linux-gnu.tar.gz", + "version": "29.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "07f77afd326639145b9ba9562912b2ad2ccec47b8a305bd075b4f4cb127b7ed7", + "verification_source": "a681e4f6ce524c338a105f214613605bac6c33d58c31dc5135bbc02bc458bb6c", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-aarch64-linux-gnu.tar.gz", - "verification_source": "6ddb6990690bd4c9a9f4319ed6f6e9c995c85ce5530ee9f120e80ce09e090c44" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-aarch64-linux-gnu.tar.gz", + "verification_source": "7922ac99363dd28f79e57ef7098581fd48ebd1119b412b07e73b1fd19fd0443f" } } }, diff --git a/configs/coins/bitcoin_signet.json b/configs/coins/bitcoin_signet.json index 58768197fa..c88cccdc66 100644 --- a/configs/coins/bitcoin_signet.json +++ b/configs/coins/bitcoin_signet.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-signet", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "28.1", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-linux-gnu.tar.gz", + "version": "29.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "07f77afd326639145b9ba9562912b2ad2ccec47b8a305bd075b4f4cb127b7ed7", + "verification_source": "a681e4f6ce524c338a105f214613605bac6c33d58c31dc5135bbc02bc458bb6c", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-aarch64-linux-gnu.tar.gz", - "verification_source": "6ddb6990690bd4c9a9f4319ed6f6e9c995c85ce5530ee9f120e80ce09e090c44" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-aarch64-linux-gnu.tar.gz", + "verification_source": "7922ac99363dd28f79e57ef7098581fd48ebd1119b412b07e73b1fd19fd0443f" } } }, diff --git a/configs/coins/bitcoin_testnet.json b/configs/coins/bitcoin_testnet.json index 0db14d8934..dbf955bd41 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": "28.1", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-linux-gnu.tar.gz", + "version": "29.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "07f77afd326639145b9ba9562912b2ad2ccec47b8a305bd075b4f4cb127b7ed7", + "verification_source": "a681e4f6ce524c338a105f214613605bac6c33d58c31dc5135bbc02bc458bb6c", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-aarch64-linux-gnu.tar.gz", - "verification_source": "6ddb6990690bd4c9a9f4319ed6f6e9c995c85ce5530ee9f120e80ce09e090c44" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-aarch64-linux-gnu.tar.gz", + "verification_source": "7922ac99363dd28f79e57ef7098581fd48ebd1119b412b07e73b1fd19fd0443f" } } }, diff --git a/configs/coins/bitcoin_testnet4.json b/configs/coins/bitcoin_testnet4.json index 426829c239..235aaa6385 100644 --- a/configs/coins/bitcoin_testnet4.json +++ b/configs/coins/bitcoin_testnet4.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-testnet4", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "28.1", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-linux-gnu.tar.gz", + "version": "29.0", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "07f77afd326639145b9ba9562912b2ad2ccec47b8a305bd075b4f4cb127b7ed7", + "verification_source": "a681e4f6ce524c338a105f214613605bac6c33d58c31dc5135bbc02bc458bb6c", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-aarch64-linux-gnu.tar.gz", - "verification_source": "6ddb6990690bd4c9a9f4319ed6f6e9c995c85ce5530ee9f120e80ce09e090c44" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-aarch64-linux-gnu.tar.gz", + "verification_source": "7922ac99363dd28f79e57ef7098581fd48ebd1119b412b07e73b1fd19fd0443f" } } }, From 6f9de0ec1c2dcaf5caf8aacfa7a928f506ad0a83 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 26 Feb 2025 17:28:09 +0100 Subject: [PATCH 449/530] Synchronize connect block with reconnect internal data --- db/rocksdb.go | 30 +++++++++++++++++------------- db/rocksdb_ethereumtype.go | 3 +++ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/db/rocksdb.go b/db/rocksdb.go index 413659375c..4024f1adda 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -59,18 +59,19 @@ const ( // RocksDB handle type RocksDB struct { - path string - db *grocksdb.DB - wo *grocksdb.WriteOptions - ro *grocksdb.ReadOptions - cfh []*grocksdb.ColumnFamilyHandle - chainParser bchain.BlockChainParser - is *common.InternalState - metrics *common.Metrics - cache *grocksdb.Cache - maxOpenFiles int - cbs connectBlockStats - extendedIndex bool + path string + db *grocksdb.DB + wo *grocksdb.WriteOptions + ro *grocksdb.ReadOptions + cfh []*grocksdb.ColumnFamilyHandle + chainParser bchain.BlockChainParser + is *common.InternalState + metrics *common.Metrics + cache *grocksdb.Cache + maxOpenFiles int + cbs connectBlockStats + extendedIndex bool + connectBlockMux sync.Mutex } const ( @@ -149,7 +150,7 @@ func NewRocksDB(path string, cacheSize, maxOpenFiles int, parser bchain.BlockCha } wo := grocksdb.NewDefaultWriteOptions() ro := grocksdb.NewDefaultReadOptions() - return &RocksDB{path, db, wo, ro, cfh, parser, nil, metrics, c, maxOpenFiles, connectBlockStats{}, extendedIndex}, nil + return &RocksDB{path, db, wo, ro, cfh, parser, nil, metrics, c, maxOpenFiles, connectBlockStats{}, extendedIndex, sync.Mutex{}}, nil } func (d *RocksDB) closeDB() error { @@ -333,6 +334,9 @@ const ( // ConnectBlock indexes addresses in the block and stores them in db func (d *RocksDB) ConnectBlock(block *bchain.Block) error { + d.connectBlockMux.Lock() + defer d.connectBlockMux.Unlock() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index e6845e0ed6..11eba90305 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -606,6 +606,9 @@ func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses ad // ReconnectInternalDataToBlockEthereumType adds missing internal data to the block and stores them in db func (d *RocksDB) ReconnectInternalDataToBlockEthereumType(block *bchain.Block) error { + d.connectBlockMux.Lock() + defer d.connectBlockMux.Unlock() + wb := grocksdb.NewWriteBatch() defer wb.Destroy() if d.chainParser.GetChainType() != bchain.ChainEthereumType { From 1448a11d5dae375fb0d29ada8b23bc797ea1a645 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sat, 1 Mar 2025 09:48:03 +0100 Subject: [PATCH 450/530] Upgrade rocksdb to v9.10.0 and go to v1.23.7 --- build/docker/bin/Dockerfile | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/docker/bin/Dockerfile b/build/docker/bin/Dockerfile index 62720f4c0c..df4670a3fc 100644 --- a/build/docker/bin/Dockerfile +++ b/build/docker/bin/Dockerfile @@ -11,8 +11,8 @@ RUN apt-get update && \ libzstd-dev liblz4-dev graphviz && \ apt-get clean ARG GOLANG_VERSION -ENV GOLANG_VERSION=go1.22.8 -ENV ROCKSDB_VERSION=v7.7.2 +ENV GOLANG_VERSION=go1.23.7 +ENV ROCKSDB_VERSION=v9.10.0 ENV GOPATH=/go ENV PATH=$PATH:$GOPATH/bin ENV CGO_CFLAGS="-I/opt/rocksdb/include" diff --git a/go.mod b/go.mod index ba3973bdeb..1958e8d74f 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/golang/glog v1.2.1 github.com/gorilla/websocket v1.5.0 github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 - github.com/linxGnu/grocksdb v1.7.7 + github.com/linxGnu/grocksdb v1.9.8 github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe github.com/martinboehm/btcd v0.0.0-20221101112928-408689e15809 github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 diff --git a/go.sum b/go.sum index 6d714b38e1..9e55e196a1 100644 --- a/go.sum +++ b/go.sum @@ -168,8 +168,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= -github.com/linxGnu/grocksdb v1.7.7 h1:b6o8gagb4FL+P55qUzPchBR/C0u1lWjJOWQSWbhvTWg= -github.com/linxGnu/grocksdb v1.7.7/go.mod h1:0hTf+iA+GOr0jDX4CgIYyJZxqOH9XlBh6KVj8+zmF34= +github.com/linxGnu/grocksdb v1.9.8 h1:vOIKv9/+HKiqJAElJIEYv3ZLcihRxyP7Suu/Mu8Dxjs= +github.com/linxGnu/grocksdb v1.9.8/go.mod h1:C3CNe9UYc9hlEM2pC82AqiGS3LRW537u9LFV4wIZuHk= github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe h1:khZWpHuxJNh2EGzBbaS6EQ2d6KxgK31WeG0TnlTMUD4= github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe/go.mod h1:0hw4tpGU+9slqN/DrevhjTMb0iR9esxzpCdx8I6/UzU= github.com/martinboehm/btcd v0.0.0-20190104121910-8e7c0427fee5/go.mod h1:rKQj/jGwFruYjpM6vN+syReFoR0DsLQaajhyH/5mwUE= From a9be4a06ea49f884a9ad295126cff8e714e388c9 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 27 Feb 2025 22:13:20 +0100 Subject: [PATCH 451/530] Optimize slice handling of pack/unpack addressContracts Will trigger database migration, which can take minutes/hours. During migration Blockbook is not syncing and providing any data --- db/rocksdb.go | 108 ++++++++++++++++++++--- db/rocksdb_ethereumtype.go | 106 +++++++++++++++++++++- db/rocksdb_ethereumtype_test.go | 152 +++++++++++++++++++++++++++----- 3 files changed, 329 insertions(+), 37 deletions(-) diff --git a/db/rocksdb.go b/db/rocksdb.go index 4024f1adda..5d368b3a37 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -22,7 +22,7 @@ import ( "github.com/trezor/blockbook/common" ) -const dbVersion = 6 +const dbVersion = 7 const packedHeightBytes = 4 const maxAddrDescLen = 1024 @@ -868,7 +868,7 @@ func (d *RocksDB) cleanupBlockTxs(wb *grocksdb.WriteBatch, block *bchain.Block) break } val.Free() - d.db.DeleteCF(d.wo, d.cfh[cfBlockTxs], key) + wb.DeleteCF(d.cfh[cfBlockTxs], key) } } return nil @@ -1866,6 +1866,85 @@ func (d *RocksDB) setBlockTimes() { glog.Info("rocksdb: processed block times in ", time.Since(start)) } +func (d *RocksDB) migrateVersion5To6(sc, nc *common.InternalStateColumn) error { + // upgrade of DB 5 to 6 for BitcoinType coins is possible + // columns transactions and fiatRates must be cleared as they are not compatible + if d.chainParser.GetChainType() == bchain.ChainBitcoinType { + if nc.Name == "transactions" { + d.db.DeleteRangeCF(d.wo, d.cfh[cfTransactions], []byte{0}, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) + } else if nc.Name == "fiatRates" { + d.db.DeleteRangeCF(d.wo, d.cfh[cfFiatRates], []byte{0}, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) + } + glog.Infof("Column %s upgraded from v%d to v%d", nc.Name, sc.Version, dbVersion) + } else { + return errors.Errorf("DB version %v of column '%v' does not match the required version %v. DB is not compatible.", sc.Version, sc.Name, dbVersion) + } + return nil +} + +func (d *RocksDB) migrateAddrContractsToV7(approxRows int64) error { + glog.Info("MigrateAddrContracts: starting, will process approximately ", approxRows, " rows") + var row int64 + var seekKey []byte + // do not use cache + ro := grocksdb.NewDefaultReadOptions() + ro.SetFillCache(false) + for { + var addrDesc bchain.AddressDescriptor + it := d.db.NewIteratorCF(ro, d.cfh[cfAddressContracts]) + if row == 0 { + it.SeekToFirst() + } else { + glog.Info("MigrateAddrContracts: row ", row) + it.Seek(seekKey) + it.Next() + } + + wb := grocksdb.NewWriteBatch() + for count := 0; it.Valid() && count < refreshIterator; it.Next() { + addrDesc = append([]byte{}, it.Key().Data()...) + buf := it.Value().Data() + count++ + row++ + acs, err := unpackAddrContractsV6(buf, addrDesc) + if err != nil { + glog.Error(err, ", ", hex.EncodeToString(buf)) + acs = &AddrContracts{} + } + repacked := packAddrContracts(acs) + wb.PutCF(d.cfh[cfAddressContracts], addrDesc, repacked) + } + err := d.WriteBatch(wb) + wb.Destroy() + if err != nil { + return errors.Errorf("error storing repacked data %v", err) + } + + seekKey = addrDesc + valid := it.Valid() + it.Close() + if !valid { + break + } + } + glog.Info("MigrateAddrContracts: finished, migrated ", row, " rows") + return nil +} + +func (d *RocksDB) migrateVersion6To7(sc, nc *common.InternalStateColumn) error { + // DB v7 must migrate ethereum type column addressContracts + if d.chainParser.GetChainType() == bchain.ChainEthereumType { + if nc.Name == "addressContracts" { + err := d.migrateAddrContractsToV7(sc.Rows) + if err != nil { + return err + } + } + glog.Infof("Column %s migrated from v%d to v%d", nc.Name, sc.Version, dbVersion) + } + return nil +} + func (d *RocksDB) checkColumns(is *common.InternalState) ([]common.InternalStateColumn, error) { // make sure that column stats match the columns sc := is.DbColumns @@ -1877,15 +1956,16 @@ func (d *RocksDB) checkColumns(is *common.InternalState) ([]common.InternalState if sc[j].Name == nc[i].Name { // check the version of the column, if it does not match, the db is not compatible if sc[j].Version != dbVersion { - // upgrade of DB 5 to 6 for BitcoinType coins is possible - // columns transactions and fiatRates must be cleared as they are not compatible - if sc[j].Version == 5 && dbVersion == 6 && d.chainParser.GetChainType() == bchain.ChainBitcoinType { - if nc[i].Name == "transactions" { - d.db.DeleteRangeCF(d.wo, d.cfh[cfTransactions], []byte{0}, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) - } else if nc[i].Name == "fiatRates" { - d.db.DeleteRangeCF(d.wo, d.cfh[cfFiatRates], []byte{0}, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) + if sc[j].Version == 5 && dbVersion == 6 { + err := d.migrateVersion5To6(&sc[j], &nc[i]) + if err != nil { + return nil, err + } + } else if sc[j].Version == 6 && dbVersion == 7 { + err := d.migrateVersion6To7(&sc[j], &nc[i]) + if err != nil { + return nil, err } - glog.Infof("Column %s upgraded from v%d to v%d", nc[i].Name, sc[j].Version, dbVersion) } else { return nil, errors.Errorf("DB version %v of column '%v' does not match the required version %v. DB is not compatible.", sc[j].Version, sc[j].Name, dbVersion) } @@ -2050,13 +2130,13 @@ func (d *RocksDB) computeColumnSize(col int, stopCompute chan os.Signal) (int64, return 0, 0, 0, errors.New("Interrupted") default: } - key = it.Key().Data() + key = append([]byte{}, it.Key().Data()...) count++ rows++ keysSum += int64(len(key)) valuesSum += int64(len(it.Value().Data())) } - seekKey = append([]byte{}, key...) + seekKey = key valid := it.Valid() it.Close() if !valid { @@ -2231,7 +2311,7 @@ func (d *RocksDB) FixUtxos(stop chan os.Signal) error { return errors.New("Interrupted") default: } - addrDesc = it.Key().Data() + addrDesc = append([]byte{}, it.Key().Data()...) buf := it.Value().Data() count++ row++ @@ -2258,7 +2338,7 @@ func (d *RocksDB) FixUtxos(stop chan os.Signal) error { fixedCount++ } } - seekKey = append([]byte{}, addrDesc...) + seekKey = addrDesc valid := it.Valid() it.Close() if !valid { diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index 11eba90305..785585dc83 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -125,7 +125,7 @@ type AddrContracts struct { } // packAddrContracts packs AddrContracts into a byte buffer -func packAddrContracts(acs *AddrContracts) []byte { +func packAddrContractsV6(acs *AddrContracts) []byte { buf := make([]byte, 0, 128) varBuf := make([]byte, maxPackedBigintBytes) l := packVaruint(acs.TotalTxs, varBuf) @@ -162,14 +162,114 @@ func packAddrContracts(acs *AddrContracts) []byte { return buf } -func unpackAddrContracts(buf []byte, addrDesc bchain.AddressDescriptor) (*AddrContracts, error) { +// packAddrContracts packs AddrContracts into a byte buffer +func packAddrContracts(acs *AddrContracts) []byte { + buf := make([]byte, 0, 8+len(acs.Contracts)*(eth.EthereumTypeAddressDescriptorLen+12)) + varBuf := make([]byte, maxPackedBigintBytes) + l := packVaruint(acs.TotalTxs, varBuf) + buf = append(buf, varBuf[:l]...) + l = packVaruint(acs.NonContractTxs, varBuf) + buf = append(buf, varBuf[:l]...) + l = packVaruint(acs.InternalTxs, varBuf) + buf = append(buf, varBuf[:l]...) + l = packVaruint(uint(len(acs.Contracts)), varBuf) + buf = append(buf, varBuf[:l]...) + for _, ac := range acs.Contracts { + buf = append(buf, ac.Contract...) + l = packVaruint(uint(ac.Standard)+ac.Txs<<2, varBuf) + buf = append(buf, varBuf[:l]...) + if ac.Standard == bchain.FungibleToken { + l = packBigint(&ac.Value, varBuf) + buf = append(buf, varBuf[:l]...) + } else if ac.Standard == bchain.NonFungibleToken { + l = packVaruint(uint(len(ac.Ids)), varBuf) + buf = append(buf, varBuf[:l]...) + for i := range ac.Ids { + l = packBigint(&ac.Ids[i], varBuf) + buf = append(buf, varBuf[:l]...) + } + } else { // bchain.ERC1155 + l = packVaruint(uint(len(ac.MultiTokenValues)), varBuf) + buf = append(buf, varBuf[:l]...) + for i := range ac.MultiTokenValues { + l = packBigint(&ac.MultiTokenValues[i].Id, varBuf) + buf = append(buf, varBuf[:l]...) + l = packBigint(&ac.MultiTokenValues[i].Value, varBuf) + buf = append(buf, varBuf[:l]...) + } + } + } + return buf +} + +func unpackAddrContractsV6(buf []byte, addrDesc bchain.AddressDescriptor) (acs *AddrContracts, err error) { + tt, l := unpackVaruint(buf) + buf = buf[l:] + nct, l := unpackVaruint(buf) + buf = buf[l:] + ict, l := unpackVaruint(buf) + buf = buf[l:] + c := make([]AddrContract, 0, len(buf)/30+4) + for len(buf) > 0 { + if len(buf) < eth.EthereumTypeAddressDescriptorLen { + return nil, errors.New("Invalid data stored in cfAddressContracts for AddrDesc " + addrDesc.String()) + } + contract := append(bchain.AddressDescriptor(nil), buf[:eth.EthereumTypeAddressDescriptorLen]...) + txs, l := unpackVaruint(buf[eth.EthereumTypeAddressDescriptorLen:]) + buf = buf[eth.EthereumTypeAddressDescriptorLen+l:] + standard := bchain.TokenStandard(txs & 3) + txs >>= 2 + ac := AddrContract{ + Standard: standard, + Contract: contract, + Txs: txs, + } + if standard == bchain.FungibleToken { + b, ll := unpackBigint(buf) + buf = buf[ll:] + ac.Value = b + } else { + len, ll := unpackVaruint(buf) + buf = buf[ll:] + if standard == bchain.NonFungibleToken { + ac.Ids = make(Ids, len) + for i := uint(0); i < len; i++ { + b, ll := unpackBigint(buf) + buf = buf[ll:] + ac.Ids[i] = b + } + } else { + ac.MultiTokenValues = make(MultiTokenValues, len) + for i := uint(0); i < len; i++ { + b, ll := unpackBigint(buf) + buf = buf[ll:] + ac.MultiTokenValues[i].Id = b + b, ll = unpackBigint(buf) + buf = buf[ll:] + ac.MultiTokenValues[i].Value = b + } + } + } + c = append(c, ac) + } + return &AddrContracts{ + TotalTxs: tt, + NonContractTxs: nct, + InternalTxs: ict, + Contracts: c, + }, nil +} + +func unpackAddrContracts(buf []byte, addrDesc bchain.AddressDescriptor) (acs *AddrContracts, err error) { tt, l := unpackVaruint(buf) buf = buf[l:] nct, l := unpackVaruint(buf) buf = buf[l:] ict, l := unpackVaruint(buf) buf = buf[l:] - c := make([]AddrContract, 0, 4) + cl, l := unpackVaruint(buf) + buf = buf[l:] + c := make([]AddrContract, 0, cl) for len(buf) > 0 { if len(buf) < eth.EthereumTypeAddressDescriptorLen { return nil, errors.New("Invalid data stored in cfAddressContracts for AddrDesc " + addrDesc.String()) diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index 4d00780c4d..9594ada069 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -55,17 +55,17 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo } if err := checkColumn(d, cfAddressContracts, []keyPair{ - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "020102", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "02010200", nil}, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), - "020100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("10000000000000000000000"), nil, + "02010001" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("10000000000000000000000"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), - "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintToHex(big.NewInt(0)), nil, + "01010001" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintToHex(big.NewInt(0)), nil, }, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "010002", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010101", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "01000200", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "01010100", nil}, }); err != nil { { t.Fatal(err) @@ -177,51 +177,51 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa if err := checkColumn(d, cfAddressContracts, []keyPair{ { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), - "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintToHex(big.NewInt(0)), nil, + "01010001" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintToHex(big.NewInt(0)), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), - "030202" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(1) + bigintFromStringToHex("150") + bigintFromStringToHex("1"), nil, + "03020201" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(1) + bigintFromStringToHex("150") + bigintFromStringToHex("1"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), - "010101" + + "01010102" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(2<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("8086") + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("871180000950184"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), - "050300" + + "05030003" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("10000000854307892726464") + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("0") + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("0"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser), - "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(2) + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10"), nil, + "01010001" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(2) + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser), - "020000" + + "02000003" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("0") + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("7674999999999991915") + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.NonFungibleToken)) + varuintToHex(1) + bigintFromStringToHex("1"), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser), - "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.NonFungibleToken)) + varuintToHex(0), nil, + "01010001" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.NonFungibleToken)) + varuintToHex(0), nil, }, { dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrA3, d.chainParser), - "010000" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(0), nil, + "01000001" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(0), nil, }, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr92, d.chainParser), "010100", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "030104", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser), "010001", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser), "010100", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "020102", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser), "010100", nil}, - {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser), "010100", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr92, d.chainParser), "01010000", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "03010400", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser), "01000100", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser), "01010000", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "02010200", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser), "01010000", nil}, + {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser), "01010000", nil}, }); err != nil { { t.Fatal(err) @@ -729,6 +729,100 @@ func Test_packUnpackEthInternalData(t *testing.T) { } } +func generateAddrContracts(f, nf, nfc, m, mc int) []AddrContract { + parser := ethereumTestnetParser() + rv := make([]AddrContract, f+nf+m) + i := 0 + for ; i < f; i++ { + rv[i] = AddrContract{ + Standard: bchain.FungibleToken, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract0d, parser), + Txs: uint(i + 100000), + Value: *big.NewInt(793201132 + int64(i*1000)), + } + } + for ; i < f+nf; i++ { + ids := make(Ids, nfc) + for j := 0; j < nfc; j++ { + ids[j] = *big.NewInt(int64(i*100000) + int64(j*100)) + } + rv[i] = AddrContract{ + Standard: bchain.NonFungibleToken, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser), + Txs: uint(i + 100000), + Ids: ids, + } + } + for ; i < f+nf+m; i++ { + mtv := make(MultiTokenValues, mc) + for j := 0; j < nfc; j++ { + mtv[j] = bchain.MultiTokenValue{ + Id: *big.NewInt(int64(j)), + Value: *big.NewInt(4231521 + int64(i*1000000) + int64(j*1000)), + } + } + rv[i] = AddrContract{ + Standard: bchain.MultiToken, + Contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser), + Txs: uint(i + 100000), + MultiTokenValues: mtv, + } + } + return rv +} + +func Benchmark_packUnpackAddrContractsV6_Fungible(b *testing.B) { + addrContracts := AddrContracts{ + TotalTxs: 3333330, + NonContractTxs: 2222220, + InternalTxs: 1111110, + Contracts: generateAddrContracts(100_000, 1, 1, 1, 1), + } + for i := 0; i < b.N; i++ { + packed := packAddrContractsV6(&addrContracts) + unpackAddrContractsV6(packed, nil) + } +} + +func Benchmark_packUnpackAddrContracts_Fungible(b *testing.B) { + addrContracts := AddrContracts{ + TotalTxs: 3333330, + NonContractTxs: 2222220, + InternalTxs: 1111110, + Contracts: generateAddrContracts(100_000, 1, 1, 1, 1), + } + for i := 0; i < b.N; i++ { + packed := packAddrContracts(&addrContracts) + unpackAddrContracts(packed, nil) + } +} + +func Benchmark_packUnpackAddrContractsV6_All(b *testing.B) { + addrContracts := AddrContracts{ + TotalTxs: 3333330, + NonContractTxs: 2222220, + InternalTxs: 1111110, + Contracts: generateAddrContracts(100_000, 1, 1_000_000, 1, 1_000_000), + } + for i := 0; i < b.N; i++ { + packed := packAddrContractsV6(&addrContracts) + unpackAddrContractsV6(packed, nil) + } +} + +func Benchmark_packUnpackAddrContracts_All(b *testing.B) { + addrContracts := AddrContracts{ + TotalTxs: 3333330, + NonContractTxs: 2222220, + InternalTxs: 1111110, + Contracts: generateAddrContracts(100_000, 1, 1_000_000, 1, 1_000_000), + } + for i := 0; i < b.N; i++ { + packed := packAddrContracts(&addrContracts) + unpackAddrContracts(packed, nil) + } +} + func Test_packUnpackAddrContracts(t *testing.T) { parser := ethereumTestnetParser() type args struct { @@ -791,6 +885,24 @@ func Test_packUnpackAddrContracts(t *testing.T) { }, }, }, + { + name: "generated", + data: AddrContracts{ + TotalTxs: 3333330, + NonContractTxs: 2222220, + InternalTxs: 1111110, + Contracts: generateAddrContracts(10, 1, 1_000, 1, 1_000), + }, + }, + { + name: "huge", + data: AddrContracts{ + TotalTxs: 3333330, + NonContractTxs: 2222220, + InternalTxs: 1111110, + Contracts: generateAddrContracts(10000, 1, 1_000_000, 1, 1_000_000), + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From d61a113685fa2d883a30d0c2fc6453f9760fec4a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 10 Mar 2025 09:27:04 +0100 Subject: [PATCH 452/530] Unpack addressContracts partially during connect block to improve performance --- db/bulkconnect.go | 12 +- db/rocksdb.go | 8 +- db/rocksdb_ethereumtype.go | 283 +++++++++++++++++++++++++++++--- db/rocksdb_ethereumtype_test.go | 81 +++++---- 4 files changed, 324 insertions(+), 60 deletions(-) diff --git a/db/bulkconnect.go b/db/bulkconnect.go index 03528c1d80..faa49632a4 100644 --- a/db/bulkconnect.go +++ b/db/bulkconnect.go @@ -29,7 +29,7 @@ type BulkConnect struct { txAddressesMap map[string]*TxAddresses blockFilters map[string][]byte balances map[string]*AddrBalance - addressContracts map[string]*AddrContracts + addressContracts map[string]*unpackedAddrContracts height uint32 } @@ -51,7 +51,7 @@ func (d *RocksDB) InitBulkConnect() (*BulkConnect, error) { chainType: d.chainParser.GetChainType(), txAddressesMap: make(map[string]*TxAddresses), balances: make(map[string]*AddrBalance), - addressContracts: make(map[string]*AddrContracts), + addressContracts: make(map[string]*unpackedAddrContracts), blockFilters: make(map[string][]byte), } if err := d.SetInconsistentState(true); err != nil { @@ -264,12 +264,12 @@ func (b *BulkConnect) connectBlockBitcoinType(block *bchain.Block, storeBlockTxs } func (b *BulkConnect) storeAddressContracts(wb *grocksdb.WriteBatch, all bool) (int, error) { - var ac map[string]*AddrContracts + var ac map[string]*unpackedAddrContracts if all { ac = b.addressContracts - b.addressContracts = make(map[string]*AddrContracts) + b.addressContracts = make(map[string]*unpackedAddrContracts) } else { - ac = make(map[string]*AddrContracts) + ac = make(map[string]*unpackedAddrContracts) // store some random address contracts for k, a := range b.addressContracts { ac[k] = a @@ -279,7 +279,7 @@ func (b *BulkConnect) storeAddressContracts(wb *grocksdb.WriteBatch, all bool) ( } } } - if err := b.d.storeAddressContracts(wb, ac); err != nil { + if err := b.d.storeUnpackedAddressContracts(wb, ac); err != nil { return 0, err } return len(ac), nil diff --git a/db/rocksdb.go b/db/rocksdb.go index 5d368b3a37..7739de3eb8 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -379,12 +379,12 @@ func (d *RocksDB) ConnectBlock(block *bchain.Block) error { } } } else if chainType == bchain.ChainEthereumType { - addressContracts := make(map[string]*AddrContracts) + addressContracts := make(map[string]*unpackedAddrContracts) blockTxs, err := d.processAddressesEthereumType(block, addresses, addressContracts) if err != nil { return err } - if err := d.storeAddressContracts(wb, addressContracts); err != nil { + if err := d.storeUnpackedAddressContracts(wb, addressContracts); err != nil { return err } if err := d.storeInternalDataEthereumType(wb, blockTxs); err != nil { @@ -2501,6 +2501,10 @@ func packBigint(bi *big.Int, buf []byte) int { return fb + 1 } +func packedBigintLen(buf []byte) int { + return int(buf[0]) + 1 +} + func unpackBigint(buf []byte) (big.Int, int) { var r big.Int l := int(buf[0]) + 1 diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index 785585dc83..bb2798e0fe 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -347,7 +347,7 @@ func (d *RocksDB) GetAddrDescContracts(addrDesc bchain.AddressDescriptor) (*Addr return unpackAddrContracts(buf, addrDesc) } -func findContractInAddressContracts(contract bchain.AddressDescriptor, contracts []AddrContract) (int, bool) { +func findContractInAddressContracts(contract bchain.AddressDescriptor, contracts []unpackedAddrContract) (int, bool) { for i := range contracts { if bytes.Equal(contract, contracts[i].Contract) { return i, true @@ -398,7 +398,7 @@ func addToAddressesMapEthereumType(addresses addressesMap, strAddrDesc string, b return false } -func addToContract(c *AddrContract, contractIndex int, index int32, contract bchain.AddressDescriptor, transfer *bchain.TokenTransfer, addTxCount bool) int32 { +func addToContract(c *unpackedAddrContract, contractIndex int, index int32, contract bchain.AddressDescriptor, transfer *bchain.TokenTransfer, addTxCount bool) int32 { var aggregate AggregateFn // index 0 is for ETH transfers, index 1 (InternalTxIndexOffset) is for internal transfers, contract indexes start with 2 (ContractIndexOffset) if index < 0 { @@ -417,7 +417,7 @@ func addToContract(c *AddrContract, contractIndex int, index int32, contract bch } } if transfer.Standard == bchain.FungibleToken { - aggregate(&c.Value, &transfer.Value) + aggregate(c.Value.get(), &transfer.Value) } else if transfer.Standard == bchain.NonFungibleToken { if index < 0 { c.Ids.remove(transfer.Value) @@ -435,17 +435,17 @@ func addToContract(c *AddrContract, contractIndex int, index int32, contract bch return index } -func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.AddressDescriptor, btxID []byte, index int32, contract bchain.AddressDescriptor, transfer *bchain.TokenTransfer, addTxCount bool, addresses addressesMap, addressContracts map[string]*AddrContracts) error { +func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.AddressDescriptor, btxID []byte, index int32, contract bchain.AddressDescriptor, transfer *bchain.TokenTransfer, addTxCount bool, addresses addressesMap, addressContracts map[string]*unpackedAddrContracts) error { var err error strAddrDesc := string(addrDesc) ac, e := addressContracts[strAddrDesc] if !e { - ac, err = d.GetAddrDescContracts(addrDesc) + ac, err = d.getUnpackedAddrDescContracts(addrDesc) if err != nil { return err } if ac == nil { - ac = &AddrContracts{} + ac = &unpackedAddrContracts{} } addressContracts[strAddrDesc] = ac d.cbs.balancesMiss++ @@ -467,7 +467,7 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address contractIndex, found := findContractInAddressContracts(contract, ac.Contracts) if !found { contractIndex = len(ac.Contracts) - ac.Contracts = append(ac.Contracts, AddrContract{ + ac.Contracts = append(ac.Contracts, unpackedAddrContract{ Contract: contract, Standard: transfer.Standard, }) @@ -516,7 +516,7 @@ type ethBlockTx struct { internalData *ethInternalData } -func (d *RocksDB) processBaseTxData(blockTx *ethBlockTx, tx *bchain.Tx, addresses addressesMap, addressContracts map[string]*AddrContracts) error { +func (d *RocksDB) processBaseTxData(blockTx *ethBlockTx, tx *bchain.Tx, addresses addressesMap, addressContracts map[string]*unpackedAddrContracts) error { var from, to bchain.AddressDescriptor var err error // there is only one output address in EthereumType transaction, store it in format txid 0 @@ -567,7 +567,7 @@ func (d *RocksDB) setAddressTxIndexesToAddressMap(addrDesc bchain.AddressDescrip } // existingBlock signals that internal data are reconnected to already indexed block after they failed during standard sync -func (d *RocksDB) processInternalData(blockTx *ethBlockTx, tx *bchain.Tx, id *bchain.EthereumInternalData, addresses addressesMap, addressContracts map[string]*AddrContracts, existingBlock bool) error { +func (d *RocksDB) processInternalData(blockTx *ethBlockTx, tx *bchain.Tx, id *bchain.EthereumInternalData, addresses addressesMap, addressContracts map[string]*unpackedAddrContracts, existingBlock bool) error { blockTx.internalData = ðInternalData{ internalType: id.Type, errorMsg: id.Error, @@ -639,7 +639,7 @@ func (d *RocksDB) processInternalData(blockTx *ethBlockTx, tx *bchain.Tx, id *bc return nil } -func (d *RocksDB) processContractTransfers(blockTx *ethBlockTx, tx *bchain.Tx, addresses addressesMap, addressContracts map[string]*AddrContracts) error { +func (d *RocksDB) processContractTransfers(blockTx *ethBlockTx, tx *bchain.Tx, addresses addressesMap, addressContracts map[string]*unpackedAddrContracts) error { tokenTransfers, err := d.chainParser.EthereumTypeGetTokenTransfersFromTx(tx) if err != nil { glog.Warningf("rocksdb: processContractTransfers %v, tx %v", err, tx.Txid) @@ -676,7 +676,7 @@ func (d *RocksDB) processContractTransfers(blockTx *ethBlockTx, tx *bchain.Tx, a return nil } -func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses addressesMap, addressContracts map[string]*AddrContracts) ([]ethBlockTx, error) { +func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses addressesMap, addressContracts map[string]*unpackedAddrContracts) ([]ethBlockTx, error) { blockTxs := make([]ethBlockTx, len(block.Txs)) for txi := range block.Txs { tx := &block.Txs[txi] @@ -716,7 +716,7 @@ func (d *RocksDB) ReconnectInternalDataToBlockEthereumType(block *bchain.Block) } addresses := make(addressesMap) - addressContracts := make(map[string]*AddrContracts) + addressContracts := make(map[string]*unpackedAddrContracts) // process internal data blockTxs := make([]ethBlockTx, len(block.Txs)) @@ -737,7 +737,7 @@ func (d *RocksDB) ReconnectInternalDataToBlockEthereumType(block *bchain.Block) } } - if err := d.storeAddressContracts(wb, addressContracts); err != nil { + if err := d.storeUnpackedAddressContracts(wb, addressContracts); err != nil { return err } if err := d.storeInternalDataEthereumType(wb, blockTxs); err != nil { @@ -1296,7 +1296,7 @@ func (d *RocksDB) getBlockTxsEthereumType(height uint32) ([]ethBlockTx, error) { return bt, nil } -func (d *RocksDB) disconnectAddress(btxID []byte, internal bool, addrDesc bchain.AddressDescriptor, btxContract *ethBlockTxContract, addresses map[string]map[string]struct{}, contracts map[string]*AddrContracts) error { +func (d *RocksDB) disconnectAddress(btxID []byte, internal bool, addrDesc bchain.AddressDescriptor, btxContract *ethBlockTxContract, addresses map[string]map[string]struct{}, contracts map[string]*unpackedAddrContracts) error { var err error // do not process empty address if len(addrDesc) == 0 { @@ -1318,7 +1318,7 @@ func (d *RocksDB) disconnectAddress(btxID []byte, internal bool, addrDesc bchain } addrContracts, fc := contracts[s] if !fc { - addrContracts, err = d.GetAddrDescContracts(addrDesc) + addrContracts, err = d.getUnpackedAddrDescContracts(addrDesc) if err != nil { return err } @@ -1384,7 +1384,7 @@ func (d *RocksDB) disconnectAddress(btxID []byte, internal bool, addrDesc bchain return nil } -func (d *RocksDB) disconnectInternalData(btxID []byte, addresses map[string]map[string]struct{}, contracts map[string]*AddrContracts) error { +func (d *RocksDB) disconnectInternalData(btxID []byte, addresses map[string]map[string]struct{}, contracts map[string]*unpackedAddrContracts) error { internalData, err := d.getEthereumInternalData(btxID) if err != nil { return err @@ -1423,7 +1423,7 @@ func (d *RocksDB) disconnectInternalData(btxID []byte, addresses map[string]map[ return nil } -func (d *RocksDB) disconnectBlockTxsEthereumType(wb *grocksdb.WriteBatch, height uint32, blockTxs []ethBlockTx, contracts map[string]*AddrContracts) error { +func (d *RocksDB) disconnectBlockTxsEthereumType(wb *grocksdb.WriteBatch, height uint32, blockTxs []ethBlockTx, contracts map[string]*unpackedAddrContracts) error { glog.Info("Disconnecting block ", height, " containing ", len(blockTxs), " transactions") addresses := make(map[string]map[string]struct{}) for i := range blockTxs { @@ -1482,7 +1482,7 @@ func (d *RocksDB) DisconnectBlockRangeEthereumType(lower uint32, higher uint32) } wb := grocksdb.NewWriteBatch() defer wb.Destroy() - contracts := make(map[string]*AddrContracts) + contracts := make(map[string]*unpackedAddrContracts) for height := higher; height >= lower; height-- { if err := d.disconnectBlockTxsEthereumType(wb, height, blocks[height-lower], contracts); err != nil { return err @@ -1492,7 +1492,7 @@ func (d *RocksDB) DisconnectBlockRangeEthereumType(lower uint32, higher uint32) wb.DeleteCF(d.cfh[cfHeight], key) wb.DeleteCF(d.cfh[cfBlockInternalDataErrors], key) } - d.storeAddressContracts(wb, contracts) + d.storeUnpackedAddressContracts(wb, contracts) err := d.WriteBatch(wb) if err == nil { d.is.RemoveLastBlockTimes(int(higher-lower) + 1) @@ -1558,3 +1558,248 @@ func (d *RocksDB) SortAddressContracts(stop chan os.Signal) error { glog.Infof("SortAddressContracts: finished - scanned %d rows, sorted %d ids and %d multi token value", rowCount, idsSortedCount, multiTokenValuesSortedCount) return nil } + +type unpackedBigInt struct { + Slice []byte + Value *big.Int +} +type unpackedIds []unpackedBigInt + +type unpackedAddrContract struct { + Standard bchain.TokenStandard + Contract bchain.AddressDescriptor + Txs uint + Value unpackedBigInt // single value of ERC20 + Ids unpackedIds // multiple ERC721 tokens + MultiTokenValues unpackedMultiTokenValues // multiple ERC1155 tokens +} + +func (b *unpackedBigInt) get() *big.Int { + if b.Value == nil { + if len(b.Slice) == 0 { + b.Value = big.NewInt(0) + } else { + bi, _ := unpackBigint(b.Slice) + b.Value = &bi + } + } + return b.Value +} + +type unpackedAddrContracts struct { + Packed []byte + TotalTxs uint + NonContractTxs uint + InternalTxs uint + Contracts []unpackedAddrContract +} + +func (s *unpackedIds) search(id big.Int) int { + // attempt to find id using a binary search + return sort.Search(len(*s), func(i int) bool { + return (*s)[i].get().CmpAbs(&id) >= 0 + }) +} + +// insert id in ascending order +func (s *unpackedIds) insert(id big.Int) { + i := s.search(id) + if i == len(*s) { + *s = append(*s, unpackedBigInt{Value: &id}) + } else { + *s = append((*s)[:i+1], (*s)[i:]...) + (*s)[i] = unpackedBigInt{Value: &id} + } +} + +func (s *unpackedIds) remove(id big.Int) { + i := s.search(id) + // remove id if found + if i < len(*s) && (*s)[i].get().CmpAbs(&id) == 0 { + *s = append((*s)[:i], (*s)[i+1:]...) + } +} + +type unpackedMultiTokenValue struct { + Id unpackedBigInt + Value unpackedBigInt +} + +type unpackedMultiTokenValues []unpackedMultiTokenValue + +// search for multi token value using a binary seach on id +func (s *unpackedMultiTokenValues) search(m bchain.MultiTokenValue) int { + return sort.Search(len(*s), func(i int) bool { + return (*s)[i].Id.get().CmpAbs(&m.Id) >= 0 + }) +} + +func (s *unpackedMultiTokenValues) upsert(m bchain.MultiTokenValue, index int32, aggregate AggregateFn) { + i := s.search(m) + if i < len(*s) && (*s)[i].Id.get().CmpAbs(&m.Id) == 0 { + aggregate((*s)[i].Value.get(), &m.Value) + // if transfer from, remove if the value is zero + if index < 0 && len((*s)[i].Value.get().Bits()) == 0 { + *s = append((*s)[:i], (*s)[i+1:]...) + } + return + } + if index >= 0 { + elem := unpackedMultiTokenValue{ + Id: unpackedBigInt{Value: &m.Id}, + Value: unpackedBigInt{Value: new(big.Int).Set(&m.Value)}, + } + if i == len(*s) { + *s = append(*s, elem) + } else { + *s = append((*s)[:i+1], (*s)[i:]...) + (*s)[i] = elem + } + } +} + +// getUnpackedAddrDescContracts returns partially unpacked AddrContracts for given addrDesc +func (d *RocksDB) getUnpackedAddrDescContracts(addrDesc bchain.AddressDescriptor) (*unpackedAddrContracts, error) { + val, err := d.db.GetCF(d.ro, d.cfh[cfAddressContracts], addrDesc) + if err != nil { + return nil, err + } + defer val.Free() + buf := val.Data() + if len(buf) == 0 { + return nil, nil + } + return partiallyUnpackAddrContracts(buf) +} + +// to speed up import of blocks, the unpacking of big ints is deferred to time when they are needed +func partiallyUnpackAddrContracts(buf []byte) (acs *unpackedAddrContracts, err error) { + // make copy of the slice to avoid subsequent allocation of smaller slices + buf = append([]byte{}, buf...) + index := 0 + tt, l := unpackVaruint(buf) + index += l + nct, l := unpackVaruint(buf[index:]) + index += l + ict, l := unpackVaruint(buf[index:]) + index += l + cl, l := unpackVaruint(buf[index:]) + index += l + c := make([]unpackedAddrContract, 0, cl) + for index < len(buf) { + contract := buf[index : index+eth.EthereumTypeAddressDescriptorLen] + index += eth.EthereumTypeAddressDescriptorLen + txs, l := unpackVaruint(buf[index:]) + index += l + standard := bchain.TokenStandard(txs & 3) + txs >>= 2 + ac := unpackedAddrContract{ + Standard: standard, + Contract: contract, + Txs: txs, + } + if standard == bchain.FungibleToken { + l := packedBigintLen(buf[index:]) + ac.Value = unpackedBigInt{Slice: buf[index : index+l]} + index += l + } else { + len, ll := unpackVaruint(buf[index:]) + index += ll + if standard == bchain.NonFungibleToken { + ac.Ids = make(unpackedIds, len) + for i := uint(0); i < len; i++ { + ll := packedBigintLen(buf[index:]) + ac.Ids[i] = unpackedBigInt{Slice: buf[index : index+ll]} + index += ll + } + } else { + ac.MultiTokenValues = make(unpackedMultiTokenValues, len) + for i := uint(0); i < len; i++ { + ll := packedBigintLen(buf[index:]) + ac.MultiTokenValues[i].Id = unpackedBigInt{Slice: buf[index : index+ll]} + index += ll + ll = packedBigintLen(buf[index:]) + ac.MultiTokenValues[i].Value = unpackedBigInt{Slice: buf[index : index+ll]} + index += ll + } + } + } + c = append(c, ac) + } + return &unpackedAddrContracts{ + Packed: buf, + TotalTxs: tt, + NonContractTxs: nct, + InternalTxs: ict, + Contracts: c, + }, nil +} + +// packUnpackedAddrContracts packs unpackedAddrContracts into a byte buffer +func packUnpackedAddrContracts(acs *unpackedAddrContracts) []byte { + buf := make([]byte, 0, len(acs.Packed)+eth.EthereumTypeAddressDescriptorLen+12) + varBuf := make([]byte, maxPackedBigintBytes) + l := packVaruint(acs.TotalTxs, varBuf) + buf = append(buf, varBuf[:l]...) + l = packVaruint(acs.NonContractTxs, varBuf) + buf = append(buf, varBuf[:l]...) + l = packVaruint(acs.InternalTxs, varBuf) + buf = append(buf, varBuf[:l]...) + l = packVaruint(uint(len(acs.Contracts)), varBuf) + buf = append(buf, varBuf[:l]...) + for _, ac := range acs.Contracts { + buf = append(buf, ac.Contract...) + l = packVaruint(uint(ac.Standard)+ac.Txs<<2, varBuf) + buf = append(buf, varBuf[:l]...) + if ac.Standard == bchain.FungibleToken { + if ac.Value.Value != nil { + l = packBigint(ac.Value.Value, varBuf) + buf = append(buf, varBuf[:l]...) + } else { + buf = append(buf, ac.Value.Slice...) + } + } else if ac.Standard == bchain.NonFungibleToken { + l = packVaruint(uint(len(ac.Ids)), varBuf) + buf = append(buf, varBuf[:l]...) + for i := range ac.Ids { + if ac.Ids[i].Value != nil { + l = packBigint(ac.Ids[i].Value, varBuf) + buf = append(buf, varBuf[:l]...) + } else { + buf = append(buf, ac.Ids[i].Slice...) + } + } + } else { // bchain.ERC1155 + l = packVaruint(uint(len(ac.MultiTokenValues)), varBuf) + buf = append(buf, varBuf[:l]...) + for i := range ac.MultiTokenValues { + if ac.MultiTokenValues[i].Id.Value != nil { + l = packBigint(ac.MultiTokenValues[i].Id.Value, varBuf) + buf = append(buf, varBuf[:l]...) + } else { + buf = append(buf, ac.MultiTokenValues[i].Id.Slice...) + } + if ac.MultiTokenValues[i].Value.Value != nil { + l = packBigint(ac.MultiTokenValues[i].Value.Value, varBuf) + buf = append(buf, varBuf[:l]...) + } else { + buf = append(buf, ac.MultiTokenValues[i].Value.Slice...) + } + } + } + } + return buf +} + +func (d *RocksDB) storeUnpackedAddressContracts(wb *grocksdb.WriteBatch, acm map[string]*unpackedAddrContracts) error { + for addrDesc, acs := range acm { + // address with 0 contracts is removed from db - happens on disconnect + if acs == nil || (acs.NonContractTxs == 0 && acs.InternalTxs == 0 && len(acs.Contracts) == 0) { + wb.DeleteCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc)) + } else { + buf := packUnpackedAddrContracts(acs) + wb.PutCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc), buf) + } + } + return nil +} diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go index 9594ada069..23791bfccb 100644 --- a/db/rocksdb_ethereumtype_test.go +++ b/db/rocksdb_ethereumtype_test.go @@ -771,58 +771,66 @@ func generateAddrContracts(f, nf, nfc, m, mc int) []AddrContract { return rv } +var fungibleContracts = AddrContracts{ + TotalTxs: 3333330, + NonContractTxs: 2222220, + InternalTxs: 1111110, + Contracts: generateAddrContracts(100_000, 1, 1, 1, 1), +} +var packedFungibleContracts = packAddrContracts(&fungibleContracts) +var unpackedFungibleContracts, _ = partiallyUnpackAddrContracts(packedFungibleContracts) + +var mixedContracts = AddrContracts{ + TotalTxs: 3333330, + NonContractTxs: 2222220, + InternalTxs: 1111110, + Contracts: generateAddrContracts(100_000, 1, 1_000_000, 1, 1_000_000), +} +var packedMixedContracts = packAddrContracts(&mixedContracts) +var unpackedMixedContracts, _ = partiallyUnpackAddrContracts(packedMixedContracts) + func Benchmark_packUnpackAddrContractsV6_Fungible(b *testing.B) { - addrContracts := AddrContracts{ - TotalTxs: 3333330, - NonContractTxs: 2222220, - InternalTxs: 1111110, - Contracts: generateAddrContracts(100_000, 1, 1, 1, 1), - } for i := 0; i < b.N; i++ { - packed := packAddrContractsV6(&addrContracts) + packed := packAddrContractsV6(&fungibleContracts) unpackAddrContractsV6(packed, nil) } } func Benchmark_packUnpackAddrContracts_Fungible(b *testing.B) { - addrContracts := AddrContracts{ - TotalTxs: 3333330, - NonContractTxs: 2222220, - InternalTxs: 1111110, - Contracts: generateAddrContracts(100_000, 1, 1, 1, 1), - } for i := 0; i < b.N; i++ { - packed := packAddrContracts(&addrContracts) + packed := packAddrContracts(&fungibleContracts) unpackAddrContracts(packed, nil) } } -func Benchmark_packUnpackAddrContractsV6_All(b *testing.B) { - addrContracts := AddrContracts{ - TotalTxs: 3333330, - NonContractTxs: 2222220, - InternalTxs: 1111110, - Contracts: generateAddrContracts(100_000, 1, 1_000_000, 1, 1_000_000), +func Benchmark_packUnpackUnpackedkAddrContracts_Fungible(b *testing.B) { + for i := 0; i < b.N; i++ { + packed := packUnpackedAddrContracts(unpackedFungibleContracts) + partiallyUnpackAddrContracts(packed) } +} + +func Benchmark_packUnpackAddrContractsV6_Mixed(b *testing.B) { for i := 0; i < b.N; i++ { - packed := packAddrContractsV6(&addrContracts) + packed := packAddrContractsV6(&mixedContracts) unpackAddrContractsV6(packed, nil) } } -func Benchmark_packUnpackAddrContracts_All(b *testing.B) { - addrContracts := AddrContracts{ - TotalTxs: 3333330, - NonContractTxs: 2222220, - InternalTxs: 1111110, - Contracts: generateAddrContracts(100_000, 1, 1_000_000, 1, 1_000_000), - } +func Benchmark_packUnpackAddrContracts_Mixed(b *testing.B) { for i := 0; i < b.N; i++ { - packed := packAddrContracts(&addrContracts) + packed := packAddrContracts(&mixedContracts) unpackAddrContracts(packed, nil) } } +func Benchmark_packUnpackUnpackedkAddrContracts_Mixed(b *testing.B) { + for i := 0; i < b.N; i++ { + packed := packUnpackedAddrContracts(unpackedMixedContracts) + partiallyUnpackAddrContracts(packed) + } +} + func Test_packUnpackAddrContracts(t *testing.T) { parser := ethereumTestnetParser() type args struct { @@ -1219,17 +1227,24 @@ func Test_addToContracts(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - contractIndex, found := findContractInAddressContracts(tt.args.contract, addrContracts.Contracts) + // convert addrContracts to partially unpacked form which is used for block import + buf := packAddrContracts(addrContracts) + unpackedAddrContracts, _ := partiallyUnpackAddrContracts(buf) + // check logic + contractIndex, found := findContractInAddressContracts(tt.args.contract, unpackedAddrContracts.Contracts) if !found { - contractIndex = len(addrContracts.Contracts) - addrContracts.Contracts = append(addrContracts.Contracts, AddrContract{ + contractIndex = len(unpackedAddrContracts.Contracts) + unpackedAddrContracts.Contracts = append(unpackedAddrContracts.Contracts, unpackedAddrContract{ Contract: tt.args.contract, Standard: tt.args.transfer.Standard, }) } - if got := addToContract(&addrContracts.Contracts[contractIndex], contractIndex, tt.args.index, tt.args.contract, tt.args.transfer, tt.args.addTxCount); got != tt.wantIndex { + if got := addToContract(&unpackedAddrContracts.Contracts[contractIndex], contractIndex, tt.args.index, tt.args.contract, tt.args.transfer, tt.args.addTxCount); got != tt.wantIndex { t.Errorf("addToContracts() = %v, want %v", got, tt.wantIndex) } + // convert from partially unpacked form to final form used by API + buf = packUnpackedAddrContracts(unpackedAddrContracts) + addrContracts, _ = unpackAddrContracts(buf, nil) if !reflect.DeepEqual(addrContracts, tt.wantAddrContracts) { t.Errorf("addToContracts() = %+v, want %+v", addrContracts, tt.wantAddrContracts) } From 0790f8810e5e5328e5f6cb25efd6c7fbb58c7741 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 17 Mar 2025 16:01:10 +0100 Subject: [PATCH 453/530] Add parallel connect of blocks for EthereumType coins --- blockbook.go | 2 +- db/sync.go | 213 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 161 insertions(+), 54 deletions(-) diff --git a/blockbook.go b/blockbook.go index 6675aec32d..fa0cfdd7e8 100644 --- a/blockbook.go +++ b/blockbook.go @@ -345,7 +345,7 @@ func mainWithExitCode() int { until := uint32(*blockUntil) if !*synchronize { - if err = syncWorker.ConnectBlocksParallel(height, until); err != nil { + if err = syncWorker.BulkConnectBlocks(height, until); err != nil { if err != db.ErrOperationInterrupted { glog.Error("connectBlocksParallel ", err) return exitCodeFatal diff --git a/db/sync.go b/db/sync.go index 5c3edc52e9..e0ba75fc38 100644 --- a/db/sync.go +++ b/db/sync.go @@ -153,7 +153,8 @@ func (w *SyncWorker) resyncIndex(onNewBlock bchain.OnNewBlockFunc, initialSync b // if parallel operation is enabled and the number of blocks to be connected is large, // use parallel routine to load majority of blocks // use parallel sync only in case of initial sync because it puts the db to inconsistent state - if w.syncWorkers > 1 && initialSync { + // or in case of ChainEthereumType if the tip is farther + if w.syncWorkers > 1 && (initialSync || w.chain.GetChainParser().GetChainType() == bchain.ChainEthereumType) { remoteBestHeight, err := w.chain.GetBestBlockHeight() if err != nil { return err @@ -162,15 +163,30 @@ func (w *SyncWorker) resyncIndex(onNewBlock bchain.OnNewBlockFunc, initialSync b glog.Error("resync: error - remote best height ", remoteBestHeight, " less than sync start height ", w.startHeight) return errors.New("resync: remote best height error") } - if remoteBestHeight-w.startHeight > uint32(w.syncChunk) { - glog.Infof("resync: parallel sync of blocks %d-%d, using %d workers", w.startHeight, remoteBestHeight, w.syncWorkers) - err = w.ConnectBlocksParallel(w.startHeight, remoteBestHeight) - if err != nil { - return err + if initialSync { + if remoteBestHeight-w.startHeight > uint32(w.syncChunk) { + glog.Infof("resync: bulk sync of blocks %d-%d, using %d workers", w.startHeight, remoteBestHeight, w.syncWorkers) + err = w.BulkConnectBlocks(w.startHeight, remoteBestHeight) + if err != nil { + return err + } + // after parallel load finish the sync using standard way, + // new blocks may have been created in the meantime + return w.resyncIndex(onNewBlock, initialSync) + } + } + if w.chain.GetChainParser().GetChainType() == bchain.ChainEthereumType { + syncWorkers := uint32(4) + if remoteBestHeight-w.startHeight >= syncWorkers { + glog.Infof("resync: parallel sync of blocks %d-%d, using %d workers", w.startHeight, remoteBestHeight, syncWorkers) + err = w.ParallelConnectBlocks(onNewBlock, w.startHeight, remoteBestHeight, syncWorkers) + if err != nil { + return err + } + // after parallel load finish the sync using standard way, + // new blocks may have been created in the meantime + return w.resyncIndex(onNewBlock, initialSync) } - // after parallel load finish the sync using standard way, - // new blocks may have been created in the meantime - return w.resyncIndex(onNewBlock, initialSync) } } err = w.connectBlocks(onNewBlock, initialSync) @@ -184,7 +200,7 @@ func (w *SyncWorker) handleFork(localBestHeight uint32, localBestHash string, on // find forked blocks, disconnect them and then synchronize again var height uint32 hashes := []string{localBestHash} - for height = localBestHeight - 1; height >= 0; height-- { + for height = localBestHeight - 1; ; height-- { local, err := w.db.GetBlockHash(height) if err != nil { return err @@ -271,12 +287,140 @@ func (w *SyncWorker) connectBlocks(onNewBlock bchain.OnNewBlockFunc, initialSync return nil } -// ConnectBlocksParallel uses parallel goroutines to get data from blockchain daemon -func (w *SyncWorker) ConnectBlocksParallel(lower, higher uint32) error { - type hashHeight struct { - hash string - height uint32 +type hashHeight struct { + hash string + height uint32 +} + +// ParallelConnectBlocks uses parallel goroutines to get data from blockchain daemon but keeps Blockbook in +func (w *SyncWorker) ParallelConnectBlocks(onNewBlock bchain.OnNewBlockFunc, lower, higher uint32, syncWorkers uint32) error { + var err error + var wg sync.WaitGroup + bch := make([]chan *bchain.Block, syncWorkers) + for i := 0; i < int(syncWorkers); i++ { + bch[i] = make(chan *bchain.Block) + } + hch := make(chan hashHeight, syncWorkers) + hchClosed := atomic.Value{} + hchClosed.Store(false) + writeBlockDone := make(chan struct{}) + terminating := make(chan struct{}) + writeBlockWorker := func() { + defer close(writeBlockDone) + lastBlock := lower - 1 + WriteBlockLoop: + for { + select { + case b := <-bch[(lastBlock+1)%syncWorkers]: + if b == nil { + // channel is closed and empty - work is done + break WriteBlockLoop + } + if b.Height != lastBlock+1 { + glog.Fatal("writeBlockWorker skipped block, expected block ", lastBlock+1, ", new block ", b.Height) + } + err := w.db.ConnectBlock(b) + if err != nil { + glog.Fatal("writeBlockWorker ", b.Height, " ", b.Hash, " error ", err) + } + + if onNewBlock != nil { + onNewBlock(b.Hash, b.Height) + } + w.metrics.BlockbookBestHeight.Set(float64(b.Height)) + + if b.Height > 0 && b.Height%1000 == 0 { + glog.Info("connected block ", b.Height, " ", b.Hash) + } + + lastBlock = b.Height + case <-terminating: + break WriteBlockLoop + } + } + if err != nil { + glog.Error("sync: ParallelConnectBlocks.Close error ", err) + } + glog.Info("WriteBlock exiting...") + } + for i := 0; i < int(syncWorkers); i++ { + wg.Add(1) + go w.getBlockWorker(i, syncWorkers, &wg, hch, bch, &hchClosed, terminating) } + go writeBlockWorker() + var hash string +ConnectLoop: + for h := lower; h <= higher; { + select { + case <-w.chanOsSignal: + glog.Info("connectBlocksParallel interrupted at height ", h) + err = ErrOperationInterrupted + // signal all workers to terminate their loops (error loops are interrupted below) + close(terminating) + break ConnectLoop + default: + hash, err = w.chain.GetBlockHash(h) + if err != nil { + glog.Error("GetBlockHash error ", err) + w.metrics.IndexResyncErrors.With(common.Labels{"error": "failure"}).Inc() + time.Sleep(time.Millisecond * 500) + continue + } + hch <- hashHeight{hash, h} + h++ + } + } + close(hch) + // signal stop to workers that are in a error loop + hchClosed.Store(true) + // wait for workers and close bch that will stop writer loop + wg.Wait() + for i := 0; i < int(syncWorkers); i++ { + close(bch[i]) + } + <-writeBlockDone + return err +} + +func (w *SyncWorker) getBlockWorker(i int, syncWorkers uint32, wg *sync.WaitGroup, hch chan hashHeight, bch []chan *bchain.Block, hchClosed *atomic.Value, terminating chan struct{}) { + defer wg.Done() + var err error + var block *bchain.Block +GetBlockLoop: + for hh := range hch { + for { + block, err = w.chain.GetBlock(hh.hash, hh.height) + if err != nil { + // signal came while looping in the error loop + if hchClosed.Load() == true { + glog.Error("getBlockWorker ", i, " connect block error ", err, ". Exiting...") + return + } + if err == bchain.ErrBlockNotFound { + glog.Error("getBlockWorker ", i, " connect block ", hh.height, " ", hh.hash, " error ", err, ". Retrying...") + } else { + glog.Error("getBlockWorker ", i, " connect block error ", err, ". Retrying...") + } + w.metrics.IndexResyncErrors.With(common.Labels{"error": "failure"}).Inc() + time.Sleep(time.Millisecond * 500) + } else { + break + } + } + if w.dryRun { + continue + } + select { + case bch[hh.height%syncWorkers] <- block: + case <-terminating: + break GetBlockLoop + } + } + glog.Info("getBlockWorker ", i, " exiting...") +} + +// BulkConnectBlocks uses parallel goroutines to get data from blockchain daemon +func (w *SyncWorker) BulkConnectBlocks(lower, higher uint32) error { var err error var wg sync.WaitGroup bch := make([]chan *bchain.Block, w.syncWorkers) @@ -322,45 +466,9 @@ func (w *SyncWorker) ConnectBlocksParallel(lower, higher uint32) error { } glog.Info("WriteBlock exiting...") } - getBlockWorker := func(i int) { - defer wg.Done() - var err error - var block *bchain.Block - GetBlockLoop: - for hh := range hch { - for { - block, err = w.chain.GetBlock(hh.hash, hh.height) - if err != nil { - // signal came while looping in the error loop - if hchClosed.Load() == true { - glog.Error("getBlockWorker ", i, " connect block error ", err, ". Exiting...") - return - } - if err == bchain.ErrBlockNotFound { - glog.Error("getBlockWorker ", i, " connect block ", hh.height, " ", hh.hash, " error ", err, ". Retrying...") - } else { - glog.Error("getBlockWorker ", i, " connect block error ", err, ". Retrying...") - } - w.metrics.IndexResyncErrors.With(common.Labels{"error": "failure"}).Inc() - time.Sleep(time.Millisecond * 500) - } else { - break - } - } - if w.dryRun { - continue - } - select { - case bch[hh.height%uint32(w.syncWorkers)] <- block: - case <-terminating: - break GetBlockLoop - } - } - glog.Info("getBlockWorker ", i, " exiting...") - } for i := 0; i < w.syncWorkers; i++ { wg.Add(1) - go getBlockWorker(i) + go w.getBlockWorker(i, uint32(w.syncWorkers), &wg, hch, bch, &hchClosed, terminating) } go writeBlockWorker() var hash string @@ -422,7 +530,6 @@ func (w *SyncWorker) getBlockChain(out chan blockResult, done chan struct{}) { hash := w.startHash height := w.startHeight prevHash := "" - // loop until error ErrBlockNotFound for { select { From 3bd93cd5f26a049fdd66c8ccf2eb5de7a58d4f41 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 17 Mar 2025 16:16:37 +0100 Subject: [PATCH 454/530] Bump Blockbook to version to 0.5.0 --- configs/environ.json | 10 +++++----- docs/api.md | 4 ++-- docs/rocksdb.md | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/configs/environ.json b/configs/environ.json index 529ac6404f..93c92a12f7 100644 --- a/configs/environ.json +++ b/configs/environ.json @@ -1,7 +1,7 @@ { - "version": "0.4.0", - "backend_install_path": "/opt/coins/nodes", - "backend_data_path": "/opt/coins/data", - "blockbook_install_path": "/opt/coins/blockbook", - "blockbook_data_path": "/opt/coins/data" + "version": "0.5.0", + "backend_install_path": "/opt/coins/nodes", + "backend_data_path": "/opt/coins/data", + "blockbook_install_path": "/opt/coins/blockbook", + "blockbook_data_path": "/opt/coins/data" } diff --git a/docs/api.md b/docs/api.md index 811a5c4081..2d42b940a9 100644 --- a/docs/api.md +++ b/docs/api.md @@ -48,7 +48,7 @@ Response (`SystemInfo` type): "coin": "Bitcoin", "network": "BTC", "host": "backend5", - "version": "0.4.0", + "version": "0.5.0", "gitCommit": "a0960c8e", "buildTime": "2024-08-08T12:32:50+00:00", "syncMode": true, @@ -1055,4 +1055,4 @@ Socket.io interface is provided at `/socket.io/`. The interface also can be expl The legacy API is provided as is and will not be further developed. -The legacy API is currently (as of Blockbook v0.4.0) also accessible without the _/v1/_ prefix, however in the future versions the version-less access will be removed. +The legacy API is currently (as of Blockbook v0.5.0) also accessible without the _/v1/_ prefix, however in the future versions the version-less access will be removed. diff --git a/docs/rocksdb.md b/docs/rocksdb.md index ddc2356f93..3a230085e9 100644 --- a/docs/rocksdb.md +++ b/docs/rocksdb.md @@ -25,7 +25,7 @@ **Database structure:** -The database structure described here is of Blockbook version **0.4.0** (internal data format version 6). +The database structure described here is of Blockbook version **0.5.0** (internal data format version 7). The database structure for **Bitcoin type** and **Ethereum type** coins is different. Column families used for both types: @@ -100,7 +100,7 @@ Column families used only by **Ethereum type** coins: and array of _contracts_ with _number of transfers_ of given address. ``` - (addrDesc []byte) -> (total_txs vuint)+(non-contract_txs vuint)+(internal_txs vuint)+ + (addrDesc []byte) -> (total_txs vuint)+(non-contract_txs vuint)+(internal_txs vuint)+(contracts vuint)+ []((contractAddrDesc []byte)+(type+4*nr_transfers vuint))+ <(value bigInt) if ERC20> or <(nr_values vuint)+[](id bigInt) if ERC721> or From e8cda83163f36927040340fffd0b586cd76f0a49 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 20 Mar 2025 16:02:41 +0100 Subject: [PATCH 455/530] Support priority fees for mined transactions --- api/types.go | 33 +-- api/worker.go | 40 +-- bchain/coins/eth/ethparser.go | 50 +++- bchain/coins/eth/ethparser_test.go | 46 ++-- bchain/coins/eth/ethrpc.go | 4 +- bchain/coins/eth/ethtx.pb.go | 280 +++++++++----------- bchain/coins/eth/ethtx.proto | 3 + bchain/types_ethereum_type.go | 25 +- blockbook-api.ts | 3 + db/rocksdb.go | 2 + docs/api.md | 3 + server/public_ethereumtype_test.go | 4 +- static/templates/tx.html | 18 ++ tests/dbtestdata/dbtestdata_ethereumtype.go | 4 +- 14 files changed, 285 insertions(+), 230 deletions(-) diff --git a/api/types.go b/api/types.go index 7d47ba305f..63c03cbc4b 100644 --- a/api/types.go +++ b/api/types.go @@ -245,21 +245,24 @@ type EthereumInternalTransfer struct { // EthereumSpecific contains ethereum specific transaction data type EthereumSpecific struct { - Type bchain.EthereumInternalTransactionType `json:"type,omitempty"` - CreatedContract string `json:"createdContract,omitempty"` - Status eth.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending - Error string `json:"error,omitempty"` - Nonce uint64 `json:"nonce"` - GasLimit *big.Int `json:"gasLimit"` - GasUsed *big.Int `json:"gasUsed,omitempty"` - GasPrice *Amount `json:"gasPrice,omitempty"` - L1Fee *big.Int `json:"l1Fee,omitempty"` - L1FeeScalar string `json:"l1FeeScalar,omitempty"` - L1GasPrice *Amount `json:"l1GasPrice,omitempty"` - L1GasUsed *big.Int `json:"l1GasUsed,omitempty"` - Data string `json:"data,omitempty"` - ParsedData *bchain.EthereumParsedInputData `json:"parsedData,omitempty"` - InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"` + Type bchain.EthereumInternalTransactionType `json:"type,omitempty"` + CreatedContract string `json:"createdContract,omitempty"` + Status eth.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending + Error string `json:"error,omitempty"` + Nonce uint64 `json:"nonce"` + GasLimit *big.Int `json:"gasLimit"` + GasUsed *big.Int `json:"gasUsed,omitempty"` + GasPrice *Amount `json:"gasPrice,omitempty"` + MaxPriorityFeePerGas *Amount `json:"maxPriorityFeePerGas,omitempty"` + MaxFeePerGas *Amount `json:"maxFeePerGas,omitempty"` + BaseFeePerGas *Amount `json:"baseFeePerGas,omitempty"` + L1Fee *big.Int `json:"l1Fee,omitempty"` + L1FeeScalar string `json:"l1FeeScalar,omitempty"` + L1GasPrice *Amount `json:"l1GasPrice,omitempty"` + L1GasUsed *big.Int `json:"l1GasUsed,omitempty"` + Data string `json:"data,omitempty"` + ParsedData *bchain.EthereumParsedInputData `json:"parsedData,omitempty"` + InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"` } type AddressAlias struct { diff --git a/api/worker.go b/api/worker.go index 42909198f0..17c2f20d24 100644 --- a/api/worker.go +++ b/api/worker.go @@ -451,17 +451,20 @@ func (w *Worker) getTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe valOutSat = bchainTx.Vout[0].ValueSat } ethSpecific = &EthereumSpecific{ - GasLimit: ethTxData.GasLimit, - GasPrice: (*Amount)(ethTxData.GasPrice), - GasUsed: ethTxData.GasUsed, - L1Fee: ethTxData.L1Fee, - L1FeeScalar: ethTxData.L1FeeScalar, - L1GasPrice: (*Amount)(ethTxData.L1GasPrice), - L1GasUsed: ethTxData.L1GasUsed, - Nonce: ethTxData.Nonce, - Status: ethTxData.Status, - Data: ethTxData.Data, - ParsedData: parsedInputData, + GasLimit: ethTxData.GasLimit, + GasPrice: (*Amount)(ethTxData.GasPrice), + MaxPriorityFeePerGas: (*Amount)(ethTxData.MaxPriorityFeePerGas), + MaxFeePerGas: (*Amount)(ethTxData.MaxFeePerGas), + BaseFeePerGas: (*Amount)(ethTxData.BaseFeePerGas), + GasUsed: ethTxData.GasUsed, + L1Fee: ethTxData.L1Fee, + L1FeeScalar: ethTxData.L1FeeScalar, + L1GasPrice: (*Amount)(ethTxData.L1GasPrice), + L1GasUsed: ethTxData.L1GasUsed, + Nonce: ethTxData.Nonce, + Status: ethTxData.Status, + Data: ethTxData.Data, + ParsedData: parsedInputData, } if internalData != nil { ethSpecific.Type = internalData.Type @@ -592,12 +595,15 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, tokens = w.getEthereumTokensTransfers(mempoolTx.TokenTransfers, addresses) ethTxData := eth.GetEthereumTxDataFromSpecificData(mempoolTx.CoinSpecificData) ethSpecific = &EthereumSpecific{ - GasLimit: ethTxData.GasLimit, - GasPrice: (*Amount)(ethTxData.GasPrice), - GasUsed: ethTxData.GasUsed, - Nonce: ethTxData.Nonce, - Status: ethTxData.Status, - Data: ethTxData.Data, + GasLimit: ethTxData.GasLimit, + GasPrice: (*Amount)(ethTxData.GasPrice), + MaxPriorityFeePerGas: (*Amount)(ethTxData.MaxPriorityFeePerGas), + MaxFeePerGas: (*Amount)(ethTxData.MaxFeePerGas), + BaseFeePerGas: (*Amount)(ethTxData.BaseFeePerGas), + GasUsed: ethTxData.GasUsed, + Nonce: ethTxData.Nonce, + Status: ethTxData.Status, + Data: ethTxData.Data, } } r := &Tx{ diff --git a/bchain/coins/eth/ethparser.go b/bchain/coins/eth/ethparser.go index 73f58b621a..e3d310cc73 100644 --- a/bchain/coins/eth/ethparser.go +++ b/bchain/coins/eth/ethparser.go @@ -277,6 +277,21 @@ func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ( if pt.Tx.GasPrice, err = hexDecodeBig(r.Tx.GasPrice); err != nil { return nil, errors.Annotatef(err, "Price %v", r.Tx.GasPrice) } + if len(r.Tx.MaxPriorityFeePerGas) > 0 { + if pt.Tx.MaxPriorityFeePerGas, err = hexDecodeBig(r.Tx.MaxPriorityFeePerGas); err != nil { + return nil, errors.Annotatef(err, "MaxPriorityFeePerGas %v", r.Tx.MaxPriorityFeePerGas) + } + } + if len(r.Tx.MaxFeePerGas) > 0 { + if pt.Tx.MaxFeePerGas, err = hexDecodeBig(r.Tx.MaxFeePerGas); err != nil { + return nil, errors.Annotatef(err, "MaxFeePerGas %v", r.Tx.MaxFeePerGas) + } + } + if len(r.Tx.BaseFeePerGas) > 0 { + if pt.Tx.BaseFeePerGas, err = hexDecodeBig(r.Tx.BaseFeePerGas); err != nil { + return nil, errors.Annotatef(err, "BaseFeePerGas %v", r.Tx.BaseFeePerGas) + } + } // if pt.R, err = hexDecodeBig(r.R); err != nil { // return nil, errors.Annotatef(err, "R %v", r.R) // } @@ -379,6 +394,15 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)), Value: hexEncodeBig(pt.Tx.Value), } + if len(pt.Tx.MaxPriorityFeePerGas) > 0 { + rt.MaxPriorityFeePerGas = hexEncodeBig(pt.Tx.MaxPriorityFeePerGas) + } + if len(pt.Tx.MaxFeePerGas) > 0 { + rt.MaxFeePerGas = hexEncodeBig(pt.Tx.MaxFeePerGas) + } + if len(pt.Tx.BaseFeePerGas) > 0 { + rt.BaseFeePerGas = hexEncodeBig(pt.Tx.BaseFeePerGas) + } var rr *bchain.RpcReceipt if pt.Receipt != nil { rr = &bchain.RpcReceipt{ @@ -509,16 +533,19 @@ const ( // EthereumTxData contains ethereum specific transaction data type EthereumTxData struct { - Status TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown - Nonce uint64 `json:"nonce"` - GasLimit *big.Int `json:"gaslimit"` - GasUsed *big.Int `json:"gasused"` - GasPrice *big.Int `json:"gasprice"` - L1Fee *big.Int `json:"l1Fee,omitempty"` - L1FeeScalar string `json:"l1FeeScalar,omitempty"` - L1GasPrice *big.Int `json:"l1GasPrice,omitempty"` - L1GasUsed *big.Int `json:"L1GasUsed,omitempty"` - Data string `json:"data"` + Status TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown + Nonce uint64 `json:"nonce"` + GasLimit *big.Int `json:"gaslimit"` + GasUsed *big.Int `json:"gasused"` + GasPrice *big.Int `json:"gasprice"` + MaxPriorityFeePerGas *big.Int `json:"maxPriorityFeePerGas,omitempty"` + MaxFeePerGas *big.Int `json:"maxFeePerGas,omitempty"` + BaseFeePerGas *big.Int `json:"baseFeePerGas,omitempty"` + L1Fee *big.Int `json:"l1Fee,omitempty"` + L1FeeScalar string `json:"l1FeeScalar,omitempty"` + L1GasPrice *big.Int `json:"l1GasPrice,omitempty"` + L1GasUsed *big.Int `json:"L1GasUsed,omitempty"` + Data string `json:"data"` } // GetEthereumTxData returns EthereumTxData from bchain.Tx @@ -535,6 +562,9 @@ func GetEthereumTxDataFromSpecificData(coinSpecificData interface{}) *EthereumTx etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce) etd.GasLimit, _ = hexutil.DecodeBig(csd.Tx.GasLimit) etd.GasPrice, _ = hexutil.DecodeBig(csd.Tx.GasPrice) + etd.MaxPriorityFeePerGas, _ = hexutil.DecodeBig(csd.Tx.MaxPriorityFeePerGas) + etd.MaxFeePerGas, _ = hexutil.DecodeBig(csd.Tx.MaxFeePerGas) + etd.BaseFeePerGas, _ = hexutil.DecodeBig(csd.Tx.BaseFeePerGas) etd.Data = csd.Tx.Payload } if csd.Receipt != nil { diff --git a/bchain/coins/eth/ethparser_test.go b/bchain/coins/eth/ethparser_test.go index aaee177ae6..65b46c7d1a 100644 --- a/bchain/coins/eth/ethparser_test.go +++ b/bchain/coins/eth/ethparser_test.go @@ -91,16 +91,19 @@ func init() { }, CoinSpecificData: bchain.EthereumSpecificData{ Tx: &bchain.RpcTransaction{ - AccountNonce: "0xb26c", - GasPrice: "0x430e23400", - GasLimit: "0x5208", - To: "0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f", - Value: "0x1bc0159d530e6000", - Payload: "0x", - Hash: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b", - BlockNumber: "0x41eee8", - From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97", - TransactionIndex: "0xa", + AccountNonce: "0xb26c", + GasPrice: "0x430e23400", + MaxPriorityFeePerGas: "0x430e23401", + MaxFeePerGas: "0x430e23402", + BaseFeePerGas: "0x430e23403", + GasLimit: "0x5208", + To: "0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f", + Value: "0x1bc0159d530e6000", + Payload: "0x", + Hash: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b", + BlockNumber: "0x41eee8", + From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97", + TransactionIndex: "0xa", }, Receipt: &bchain.RpcReceipt{ GasUsed: "0x5208", @@ -129,16 +132,19 @@ func init() { }, CoinSpecificData: bchain.EthereumSpecificData{ Tx: &bchain.RpcTransaction{ - AccountNonce: "0xd0", - GasPrice: "0x9502f9000", - GasLimit: "0x130d5", - To: "0x4af4114F73d1c1C903aC9E0361b379D1291808A2", - Value: "0x0", - Payload: "0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000", - Hash: "0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101", - BlockNumber: "0x41eee8", - From: "0x20cD153de35D469BA46127A0C8F18626b59a256A", - TransactionIndex: "0x0"}, + AccountNonce: "0xd0", + GasPrice: "0x9502f9000", + MaxPriorityFeePerGas: "0x9502f9001", + MaxFeePerGas: "0x9502f9002", + BaseFeePerGas: "0x9502f9003", + GasLimit: "0x130d5", + To: "0x4af4114F73d1c1C903aC9E0361b379D1291808A2", + Value: "0x0", + Payload: "0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000", + Hash: "0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101", + BlockNumber: "0x41eee8", + From: "0x20cD153de35D469BA46127A0C8F18626b59a256A", + TransactionIndex: "0x0"}, Receipt: &bchain.RpcReceipt{ GasUsed: "0xcb39", Status: "0x1", diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 7d3e35a6b7..0f8bce2660 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -891,7 +891,8 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { return nil, err } var ht struct { - Time string `json:"timestamp"` + Time string `json:"timestamp"` + BaseFeePerGas string `json:"baseFeePerGas"` } if err := json.Unmarshal(raw, &ht); err != nil { return nil, errors.Annotatef(err, "hash %v", hash) @@ -900,6 +901,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { if time, err = ethNumber(ht.Time); err != nil { return nil, errors.Annotatef(err, "txid %v", txid) } + tx.BaseFeePerGas = ht.BaseFeePerGas var receipt bchain.RpcReceipt err = b.RPC.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash) if err != nil { diff --git a/bchain/coins/eth/ethtx.pb.go b/bchain/coins/eth/ethtx.pb.go index 0174ab9b0a..500d5a16ba 100644 --- a/bchain/coins/eth/ethtx.pb.go +++ b/bchain/coins/eth/ethtx.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.12 +// protoc-gen-go v1.36.5 +// protoc v3.21.5 // source: bchain/coins/eth/ethtx.proto package eth @@ -11,6 +11,7 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -21,23 +22,20 @@ const ( ) type ProtoCompleteTransaction struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + BlockNumber uint32 `protobuf:"varint,1,opt,name=BlockNumber,proto3" json:"BlockNumber,omitempty"` + BlockTime uint64 `protobuf:"varint,2,opt,name=BlockTime,proto3" json:"BlockTime,omitempty"` + Tx *ProtoCompleteTransaction_TxType `protobuf:"bytes,3,opt,name=Tx,proto3" json:"Tx,omitempty"` + Receipt *ProtoCompleteTransaction_ReceiptType `protobuf:"bytes,4,opt,name=Receipt,proto3" json:"Receipt,omitempty"` unknownFields protoimpl.UnknownFields - - BlockNumber uint32 `protobuf:"varint,1,opt,name=BlockNumber,proto3" json:"BlockNumber,omitempty"` - BlockTime uint64 `protobuf:"varint,2,opt,name=BlockTime,proto3" json:"BlockTime,omitempty"` - Tx *ProtoCompleteTransaction_TxType `protobuf:"bytes,3,opt,name=Tx,proto3" json:"Tx,omitempty"` - Receipt *ProtoCompleteTransaction_ReceiptType `protobuf:"bytes,4,opt,name=Receipt,proto3" json:"Receipt,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ProtoCompleteTransaction) Reset() { *x = ProtoCompleteTransaction{} - if protoimpl.UnsafeEnabled { - mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ProtoCompleteTransaction) String() string { @@ -48,7 +46,7 @@ func (*ProtoCompleteTransaction) ProtoMessage() {} func (x *ProtoCompleteTransaction) ProtoReflect() protoreflect.Message { mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -92,28 +90,28 @@ func (x *ProtoCompleteTransaction) GetReceipt() *ProtoCompleteTransaction_Receip } type ProtoCompleteTransaction_TxType struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - AccountNonce uint64 `protobuf:"varint,1,opt,name=AccountNonce,proto3" json:"AccountNonce,omitempty"` - GasPrice []byte `protobuf:"bytes,2,opt,name=GasPrice,proto3" json:"GasPrice,omitempty"` - GasLimit uint64 `protobuf:"varint,3,opt,name=GasLimit,proto3" json:"GasLimit,omitempty"` - Value []byte `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"` - Payload []byte `protobuf:"bytes,5,opt,name=Payload,proto3" json:"Payload,omitempty"` - Hash []byte `protobuf:"bytes,6,opt,name=Hash,proto3" json:"Hash,omitempty"` - To []byte `protobuf:"bytes,7,opt,name=To,proto3" json:"To,omitempty"` - From []byte `protobuf:"bytes,8,opt,name=From,proto3" json:"From,omitempty"` - TransactionIndex uint32 `protobuf:"varint,9,opt,name=TransactionIndex,proto3" json:"TransactionIndex,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + AccountNonce uint64 `protobuf:"varint,1,opt,name=AccountNonce,proto3" json:"AccountNonce,omitempty"` + GasPrice []byte `protobuf:"bytes,2,opt,name=GasPrice,proto3" json:"GasPrice,omitempty"` + GasLimit uint64 `protobuf:"varint,3,opt,name=GasLimit,proto3" json:"GasLimit,omitempty"` + Value []byte `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"` + Payload []byte `protobuf:"bytes,5,opt,name=Payload,proto3" json:"Payload,omitempty"` + Hash []byte `protobuf:"bytes,6,opt,name=Hash,proto3" json:"Hash,omitempty"` + To []byte `protobuf:"bytes,7,opt,name=To,proto3" json:"To,omitempty"` + From []byte `protobuf:"bytes,8,opt,name=From,proto3" json:"From,omitempty"` + TransactionIndex uint32 `protobuf:"varint,9,opt,name=TransactionIndex,proto3" json:"TransactionIndex,omitempty"` + MaxPriorityFeePerGas []byte `protobuf:"bytes,10,opt,name=MaxPriorityFeePerGas,proto3,oneof" json:"MaxPriorityFeePerGas,omitempty"` + MaxFeePerGas []byte `protobuf:"bytes,11,opt,name=MaxFeePerGas,proto3,oneof" json:"MaxFeePerGas,omitempty"` + BaseFeePerGas []byte `protobuf:"bytes,12,opt,name=BaseFeePerGas,proto3,oneof" json:"BaseFeePerGas,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ProtoCompleteTransaction_TxType) Reset() { *x = ProtoCompleteTransaction_TxType{} - if protoimpl.UnsafeEnabled { - mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ProtoCompleteTransaction_TxType) String() string { @@ -124,7 +122,7 @@ func (*ProtoCompleteTransaction_TxType) ProtoMessage() {} func (x *ProtoCompleteTransaction_TxType) ProtoReflect() protoreflect.Message { mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -202,27 +200,45 @@ func (x *ProtoCompleteTransaction_TxType) GetTransactionIndex() uint32 { return 0 } +func (x *ProtoCompleteTransaction_TxType) GetMaxPriorityFeePerGas() []byte { + if x != nil { + return x.MaxPriorityFeePerGas + } + return nil +} + +func (x *ProtoCompleteTransaction_TxType) GetMaxFeePerGas() []byte { + if x != nil { + return x.MaxFeePerGas + } + return nil +} + +func (x *ProtoCompleteTransaction_TxType) GetBaseFeePerGas() []byte { + if x != nil { + return x.BaseFeePerGas + } + return nil +} + type ProtoCompleteTransaction_ReceiptType struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + GasUsed []byte `protobuf:"bytes,1,opt,name=GasUsed,proto3" json:"GasUsed,omitempty"` + Status []byte `protobuf:"bytes,2,opt,name=Status,proto3" json:"Status,omitempty"` + Log []*ProtoCompleteTransaction_ReceiptType_LogType `protobuf:"bytes,3,rep,name=Log,proto3" json:"Log,omitempty"` + L1Fee []byte `protobuf:"bytes,4,opt,name=L1Fee,proto3,oneof" json:"L1Fee,omitempty"` + L1FeeScalar []byte `protobuf:"bytes,5,opt,name=L1FeeScalar,proto3,oneof" json:"L1FeeScalar,omitempty"` + L1GasPrice []byte `protobuf:"bytes,6,opt,name=L1GasPrice,proto3,oneof" json:"L1GasPrice,omitempty"` + L1GasUsed []byte `protobuf:"bytes,7,opt,name=L1GasUsed,proto3,oneof" json:"L1GasUsed,omitempty"` unknownFields protoimpl.UnknownFields - - GasUsed []byte `protobuf:"bytes,1,opt,name=GasUsed,proto3" json:"GasUsed,omitempty"` - Status []byte `protobuf:"bytes,2,opt,name=Status,proto3" json:"Status,omitempty"` - Log []*ProtoCompleteTransaction_ReceiptType_LogType `protobuf:"bytes,3,rep,name=Log,proto3" json:"Log,omitempty"` - L1Fee []byte `protobuf:"bytes,4,opt,name=L1Fee,proto3,oneof" json:"L1Fee,omitempty"` - L1FeeScalar []byte `protobuf:"bytes,5,opt,name=L1FeeScalar,proto3,oneof" json:"L1FeeScalar,omitempty"` - L1GasPrice []byte `protobuf:"bytes,6,opt,name=L1GasPrice,proto3,oneof" json:"L1GasPrice,omitempty"` - L1GasUsed []byte `protobuf:"bytes,7,opt,name=L1GasUsed,proto3,oneof" json:"L1GasUsed,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ProtoCompleteTransaction_ReceiptType) Reset() { *x = ProtoCompleteTransaction_ReceiptType{} - if protoimpl.UnsafeEnabled { - mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ProtoCompleteTransaction_ReceiptType) String() string { @@ -233,7 +249,7 @@ func (*ProtoCompleteTransaction_ReceiptType) ProtoMessage() {} func (x *ProtoCompleteTransaction_ReceiptType) ProtoReflect() protoreflect.Message { mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -298,22 +314,19 @@ func (x *ProtoCompleteTransaction_ReceiptType) GetL1GasUsed() []byte { } type ProtoCompleteTransaction_ReceiptType_LogType struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Address []byte `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=Data,proto3" json:"Data,omitempty"` + Topics [][]byte `protobuf:"bytes,3,rep,name=Topics,proto3" json:"Topics,omitempty"` unknownFields protoimpl.UnknownFields - - Address []byte `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"` - Data []byte `protobuf:"bytes,2,opt,name=Data,proto3" json:"Data,omitempty"` - Topics [][]byte `protobuf:"bytes,3,rep,name=Topics,proto3" json:"Topics,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ProtoCompleteTransaction_ReceiptType_LogType) Reset() { *x = ProtoCompleteTransaction_ReceiptType_LogType{} - if protoimpl.UnsafeEnabled { - mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ProtoCompleteTransaction_ReceiptType_LogType) String() string { @@ -324,7 +337,7 @@ func (*ProtoCompleteTransaction_ReceiptType_LogType) ProtoMessage() {} func (x *ProtoCompleteTransaction_ReceiptType_LogType) ProtoReflect() protoreflect.Message { mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -362,10 +375,10 @@ func (x *ProtoCompleteTransaction_ReceiptType_LogType) GetTopics() [][]byte { var File_bchain_coins_eth_ethtx_proto protoreflect.FileDescriptor -var file_bchain_coins_eth_ethtx_proto_rawDesc = []byte{ +var file_bchain_coins_eth_ethtx_proto_rawDesc = string([]byte{ 0x0a, 0x1c, 0x62, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x2f, 0x65, - 0x74, 0x68, 0x2f, 0x65, 0x74, 0x68, 0x74, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdd, - 0x06, 0x0a, 0x18, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x74, 0x68, 0x2f, 0x65, 0x74, 0x68, 0x74, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa6, + 0x08, 0x0a, 0x18, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1c, 0x0a, @@ -377,8 +390,8 @@ var file_bchain_coins_eth_ethtx_proto_rawDesc = []byte{ 0x07, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x1a, 0xf8, - 0x01, 0x0a, 0x06, 0x54, 0x78, 0x54, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x41, 0x63, 0x63, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x1a, 0xc1, + 0x03, 0x0a, 0x06, 0x54, 0x78, 0x54, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, @@ -393,50 +406,63 @@ var file_bchain_coins_eth_ethtx_proto_rawDesc = []byte{ 0x6d, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x2a, 0x0a, 0x10, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x1a, 0x92, 0x03, 0x0a, 0x0b, 0x52, 0x65, - 0x63, 0x65, 0x69, 0x70, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x47, 0x61, 0x73, - 0x55, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x47, 0x61, 0x73, 0x55, - 0x73, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3f, 0x0a, 0x03, 0x4c, - 0x6f, 0x67, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, - 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x19, 0x0a, 0x05, - 0x4c, 0x31, 0x46, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x05, 0x4c, - 0x31, 0x46, 0x65, 0x65, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x4c, 0x31, 0x46, 0x65, 0x65, - 0x53, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x0b, - 0x4c, 0x31, 0x46, 0x65, 0x65, 0x53, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x88, 0x01, 0x01, 0x12, 0x23, - 0x0a, 0x0a, 0x4c, 0x31, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0c, 0x48, 0x02, 0x52, 0x0a, 0x4c, 0x31, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, - 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x4c, 0x31, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x03, 0x52, 0x09, 0x4c, 0x31, 0x47, 0x61, 0x73, 0x55, - 0x73, 0x65, 0x64, 0x88, 0x01, 0x01, 0x1a, 0x4f, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x44, - 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x16, 0x0a, 0x06, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, - 0x06, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x4c, 0x31, 0x46, 0x65, - 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x4c, 0x31, 0x46, 0x65, 0x65, 0x53, 0x63, 0x61, 0x6c, 0x61, - 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x4c, 0x31, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, - 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x4c, 0x31, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x42, 0x12, - 0x5a, 0x10, 0x62, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x2f, 0x65, - 0x74, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x37, 0x0a, 0x14, 0x4d, 0x61, 0x78, + 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, + 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x14, 0x4d, 0x61, 0x78, 0x50, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, 0x88, + 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x4d, 0x61, 0x78, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, + 0x61, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x0c, 0x4d, 0x61, 0x78, 0x46, + 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0d, 0x42, + 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x0c, 0x48, 0x02, 0x52, 0x0d, 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, + 0x47, 0x61, 0x73, 0x88, 0x01, 0x01, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x4d, 0x61, 0x78, 0x50, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, 0x42, + 0x0f, 0x0a, 0x0d, 0x5f, 0x4d, 0x61, 0x78, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, + 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, + 0x61, 0x73, 0x1a, 0x92, 0x03, 0x0a, 0x0b, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x3f, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2d, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x70, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x19, 0x0a, 0x05, 0x4c, 0x31, 0x46, 0x65, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x05, 0x4c, 0x31, 0x46, 0x65, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x25, 0x0a, 0x0b, 0x4c, 0x31, 0x46, 0x65, 0x65, 0x53, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x0b, 0x4c, 0x31, 0x46, 0x65, 0x65, 0x53, 0x63, + 0x61, 0x6c, 0x61, 0x72, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x4c, 0x31, 0x47, 0x61, 0x73, + 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x02, 0x52, 0x0a, 0x4c, + 0x31, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, + 0x4c, 0x31, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, + 0x03, 0x52, 0x09, 0x4c, 0x31, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x88, 0x01, 0x01, 0x1a, + 0x4f, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x54, 0x6f, 0x70, 0x69, + 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x73, + 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x4c, 0x31, 0x46, 0x65, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x4c, + 0x31, 0x46, 0x65, 0x65, 0x53, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x4c, + 0x31, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x4c, 0x31, + 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x42, 0x12, 0x5a, 0x10, 0x62, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x2f, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +}) var ( file_bchain_coins_eth_ethtx_proto_rawDescOnce sync.Once - file_bchain_coins_eth_ethtx_proto_rawDescData = file_bchain_coins_eth_ethtx_proto_rawDesc + file_bchain_coins_eth_ethtx_proto_rawDescData []byte ) func file_bchain_coins_eth_ethtx_proto_rawDescGZIP() []byte { file_bchain_coins_eth_ethtx_proto_rawDescOnce.Do(func() { - file_bchain_coins_eth_ethtx_proto_rawDescData = protoimpl.X.CompressGZIP(file_bchain_coins_eth_ethtx_proto_rawDescData) + file_bchain_coins_eth_ethtx_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_bchain_coins_eth_ethtx_proto_rawDesc), len(file_bchain_coins_eth_ethtx_proto_rawDesc))) }) return file_bchain_coins_eth_ethtx_proto_rawDescData } var file_bchain_coins_eth_ethtx_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_bchain_coins_eth_ethtx_proto_goTypes = []interface{}{ +var file_bchain_coins_eth_ethtx_proto_goTypes = []any{ (*ProtoCompleteTransaction)(nil), // 0: ProtoCompleteTransaction (*ProtoCompleteTransaction_TxType)(nil), // 1: ProtoCompleteTransaction.TxType (*ProtoCompleteTransaction_ReceiptType)(nil), // 2: ProtoCompleteTransaction.ReceiptType @@ -458,62 +484,13 @@ func file_bchain_coins_eth_ethtx_proto_init() { if File_bchain_coins_eth_ethtx_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_bchain_coins_eth_ethtx_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProtoCompleteTransaction); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_bchain_coins_eth_ethtx_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProtoCompleteTransaction_TxType); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_bchain_coins_eth_ethtx_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProtoCompleteTransaction_ReceiptType); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_bchain_coins_eth_ethtx_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProtoCompleteTransaction_ReceiptType_LogType); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_bchain_coins_eth_ethtx_proto_msgTypes[2].OneofWrappers = []interface{}{} + file_bchain_coins_eth_ethtx_proto_msgTypes[1].OneofWrappers = []any{} + file_bchain_coins_eth_ethtx_proto_msgTypes[2].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_bchain_coins_eth_ethtx_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_bchain_coins_eth_ethtx_proto_rawDesc), len(file_bchain_coins_eth_ethtx_proto_rawDesc)), NumEnums: 0, NumMessages: 4, NumExtensions: 0, @@ -524,7 +501,6 @@ func file_bchain_coins_eth_ethtx_proto_init() { MessageInfos: file_bchain_coins_eth_ethtx_proto_msgTypes, }.Build() File_bchain_coins_eth_ethtx_proto = out.File - file_bchain_coins_eth_ethtx_proto_rawDesc = nil file_bchain_coins_eth_ethtx_proto_goTypes = nil file_bchain_coins_eth_ethtx_proto_depIdxs = nil } diff --git a/bchain/coins/eth/ethtx.proto b/bchain/coins/eth/ethtx.proto index f53e0e39cf..3a3cbfe2ce 100644 --- a/bchain/coins/eth/ethtx.proto +++ b/bchain/coins/eth/ethtx.proto @@ -12,6 +12,9 @@ message ProtoCompleteTransaction { bytes To = 7; bytes From = 8; uint32 TransactionIndex = 9; + optional bytes MaxPriorityFeePerGas = 10; + optional bytes MaxFeePerGas = 11; + optional bytes BaseFeePerGas = 12; } message ReceiptType { message LogType { diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index 9713b7682d..f29602ebe8 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -98,17 +98,20 @@ type TokenTransfer struct { // RpcTransaction is returned by eth_getTransactionByHash type RpcTransaction struct { - AccountNonce string `json:"nonce"` - GasPrice string `json:"gasPrice"` - GasLimit string `json:"gas"` - To string `json:"to"` // nil means contract creation - Value string `json:"value"` - Payload string `json:"input"` - Hash string `json:"hash"` - BlockNumber string `json:"blockNumber"` - BlockHash string `json:"blockHash,omitempty"` - From string `json:"from"` - TransactionIndex string `json:"transactionIndex"` + AccountNonce string `json:"nonce"` + GasPrice string `json:"gasPrice"` + MaxPriorityFeePerGas string `json:"maxPriorityFeePerGas,omitempty"` + MaxFeePerGas string `json:"maxFeePerGas,omitempty"` + BaseFeePerGas string `json:"baseFeePerGas,omitempty"` + GasLimit string `json:"gas"` + To string `json:"to"` // nil means contract creation + Value string `json:"value"` + Payload string `json:"input"` + Hash string `json:"hash"` + BlockNumber string `json:"blockNumber"` + BlockHash string `json:"blockHash,omitempty"` + From string `json:"from"` + TransactionIndex string `json:"transactionIndex"` // Signature values - ignored // V string `json:"v"` // R string `json:"r"` diff --git a/blockbook-api.ts b/blockbook-api.ts index d66954ff58..bc6b6e43d3 100644 --- a/blockbook-api.ts +++ b/blockbook-api.ts @@ -33,6 +33,9 @@ export interface EthereumSpecific { gasLimit: number; gasUsed?: number; gasPrice?: string; + maxPriorityFeePerGas?: string; + maxFeePerGas?: string; + baseFeePerGas?: string; l1Fee?: number; l1FeeScalar?: string; l1GasPrice?: string; diff --git a/db/rocksdb.go b/db/rocksdb.go index 7739de3eb8..f13bf29cb5 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -1939,6 +1939,8 @@ func (d *RocksDB) migrateVersion6To7(sc, nc *common.InternalStateColumn) error { if err != nil { return err } + } else if nc.Name == "transactions" { + d.db.DeleteRangeCF(d.wo, d.cfh[cfTransactions], []byte{0}, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) } glog.Infof("Column %s migrated from v%d to v%d", nc.Name, sc.Version, dbVersion) } diff --git a/docs/api.md b/docs/api.md index 2d42b940a9..d0fb05fab3 100644 --- a/docs/api.md +++ b/docs/api.md @@ -346,6 +346,9 @@ Response for Ethereum-type coins. Data of the transaction consist of: "gasLimit": 550941, "gasUsed": 434686, "gasPrice": "44035608242", + "maxPriorityFeePerGas": "44035608243", + "maxFeePerGas": "44035608244", + "baseFeePerGas": "2035608244", "data": "0xac9650d800000000000000000000", "parsedData": { "methodId": "0xfa2b068f", diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 89ca59e6db..f16658a6af 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -44,7 +44,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE0.00 USD0.00 USD
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE0.00 USD0.00 USD (40 Gwei)
Fees0.002081 FAKE4.16 USD18.55 USD
RBFON
Nonce208
 
0 FAKE0.00 USD0.00 USD
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
`, + `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE0.00 USD0.00 USD
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE0.00 USD0.00 USD (40 Gwei)
Max Priority Fee Per Gas0.000000040000000001 FAKE0.00 USD0.00 USD (40.000000001 Gwei)
Max Fee Per Gas0.000000040000000002 FAKE0.00 USD0.00 USD (40.000000002 Gwei)
Base Fee Per Gas0.000000040000000003 FAKE0.00 USD0.00 USD (40.000000003 Gwei)
Fees0.002081 FAKE4.16 USD18.55 USD
RBFON
Nonce208
 
0 FAKE0.00 USD0.00 USD
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
`, }, }, { name: "explorerTokenDetail " + dbtestdata.EthAddr7b, @@ -90,7 +90,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","standard":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}},"addressAliases":{"0x20cD153de35D469BA46127A0C8F18626b59a256A":{"Type":"ENS","Alias":"address20.eth"},"0x4af4114F73d1c1C903aC9E0361b379D1291808A2":{"Type":"Contract","Alias":"Contract 74"}}}`, + `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","maxPriorityFeePerGas":"0x9502f9001","maxFeePerGas":"0x9502f9002","baseFeePerGas":"0x9502f9003","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","standard":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","maxPriorityFeePerGas":"40000000001","maxFeePerGas":"40000000002","baseFeePerGas":"40000000003","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}},"addressAliases":{"0x20cD153de35D469BA46127A0C8F18626b59a256A":{"Type":"ENS","Alias":"address20.eth"},"0x4af4114F73d1c1C903aC9E0361b379D1291808A2":{"Type":"Contract","Alias":"Contract 74"}}}`, }, }, { diff --git a/static/templates/tx.html b/static/templates/tx.html index 5d518d7093..4ca57b8013 100644 --- a/static/templates/tx.html +++ b/static/templates/tx.html @@ -51,6 +51,24 @@
{{$tx.Txid}}Gas Price {{amountSpan $tx.EthereumSpecific.GasPrice $data "copyable"}} ({{amountSatsSpan $tx.EthereumSpecific.GasPrice $data "copyable"}} Gwei) + {{if $tx.EthereumSpecific.MaxPriorityFeePerGas}} + + Max Priority Fee Per Gas + {{amountSpan $tx.EthereumSpecific.MaxPriorityFeePerGas $data "copyable"}} ({{amountSatsSpan $tx.EthereumSpecific.MaxPriorityFeePerGas $data "copyable"}} Gwei) + + {{end}} + {{if $tx.EthereumSpecific.MaxFeePerGas}} + + Max Fee Per Gas + {{amountSpan $tx.EthereumSpecific.MaxFeePerGas $data "copyable"}} ({{amountSatsSpan $tx.EthereumSpecific.MaxFeePerGas $data "copyable"}} Gwei) + + {{end}} + {{if $tx.EthereumSpecific.BaseFeePerGas}} + + Base Fee Per Gas + {{amountSpan $tx.EthereumSpecific.BaseFeePerGas $data "copyable"}} ({{amountSatsSpan $tx.EthereumSpecific.BaseFeePerGas $data "copyable"}} Gwei) + + {{end}} {{if $tx.EthereumSpecific.L1GasUsed}} L1 Gas Used diff --git a/tests/dbtestdata/dbtestdata_ethereumtype.go b/tests/dbtestdata/dbtestdata_ethereumtype.go index b16b60e34b..9a5eb34070 100644 --- a/tests/dbtestdata/dbtestdata_ethereumtype.go +++ b/tests/dbtestdata/dbtestdata_ethereumtype.go @@ -31,7 +31,7 @@ const ( // non contract // EthAddr3e -> EthAddr55, value 1999622000000000000 EthTxidB1T1 = "cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b" - EthTx1Packed = "08e8dd870210a6a6f0db051a6908ece40212050430e234001888a40122081bc0159d530e60003220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b3a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f42143e3a3d69dc66ba10737f531ed088954a9ec89d97480a22070a025208120101" + EthTx1Packed = "08e8dd870210a6a6f0db051a7e08ece40212050430e234001888a40122081bc0159d530e60003220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b3a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f42143e3a3d69dc66ba10737f531ed088954a9ec89d97480a52050430e234015a050430e2340262050430e2340322070a025208120101" EthTx1FailedPacked = "08e8dd870210a6a6f0db051a6908ece40212050430e234001888a40122081bc0159d530e60003220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b3a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f42143e3a3d69dc66ba10737f531ed088954a9ec89d97480a22040a025208" EthTx1NoStatusPacked = "08e8dd870210a6a6f0db051a6908ece40212050430e234001888a40122081bc0159d530e60003220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b3a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f42143e3a3d69dc66ba10737f531ed088954a9ec89d97480a22070a025208120155" @@ -39,7 +39,7 @@ const ( // EthAddr20 -> EthAddrContract4a, value 0 // ERC20 EthAddrContract4a: EthAddr20 -> EthAddr55, value 10000000000000000000000 EthTxidB1T2 = "a9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101" - EthTx2Packed = "08e8dd870210a6a6f0db051aa20108d001120509502f900018d5e1042a44a9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab24000003220a9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b1013a144af4114f73d1c1c903ac9e0361b379d1291808a2421420cd153de35d469ba46127a0c8f18626b59a256a22a8010a02cb391201011a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a2122000000000000000000000000000000000000000000000021e19e0c9bab24000001a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a2000000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f" + EthTx2Packed = "08e8dd870210a6a6f0db051ab70108d001120509502f900018d5e1042a44a9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab24000003220a9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b1013a144af4114f73d1c1c903ac9e0361b379d1291808a2421420cd153de35d469ba46127a0c8f18626b59a256a520509502f90015a0509502f9002620509502f900322a8010a02cb391201011a9e010a144af4114f73d1c1c903ac9e0361b379d1291808a2122000000000000000000000000000000000000000000000021e19e0c9bab24000001a20ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a2000000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a1a20000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f" // non contract // EthAddr55 -> EthAddr9f, value 4710537472325592 From c5affa167a86cb388e27b86aa6077f49da70d1b4 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 24 Mar 2025 23:12:35 +0100 Subject: [PATCH 456/530] Set up infura eip1559 refresh periods --- configs/coins/arbitrum_archive.json | 2 +- configs/coins/base_archive.json | 5 ++++- configs/coins/bsc_archive.json | 2 +- configs/coins/ethereum_archive.json | 2 +- configs/coins/optimism_archive.json | 2 +- configs/coins/polygon_archive.json | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/configs/coins/arbitrum_archive.json b/configs/coins/arbitrum_archive.json index d0add43185..e34daf1a21 100644 --- a/configs/coins/arbitrum_archive.json +++ b/configs/coins/arbitrum_archive.json @@ -53,7 +53,7 @@ "address_aliases": true, "eip1559Fees": true, "alternative_estimate_fee": "infura", - "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/42161/suggestedGasFees\", \"periodSeconds\": 60}", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/42161/suggestedGasFees\", \"periodSeconds\": 8}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, diff --git a/configs/coins/base_archive.json b/configs/coins/base_archive.json index 877225787c..f4f6005340 100644 --- a/configs/coins/base_archive.json +++ b/configs/coins/base_archive.json @@ -53,6 +53,9 @@ "block_addresses_to_keep": 600, "additional_params": { "address_aliases": true, + "eip1559Fees": true, + "alternative_estimate_fee": "infura", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/8453/suggestedGasFees\", \"periodSeconds\": 4}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, @@ -67,4 +70,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/bsc_archive.json b/configs/coins/bsc_archive.json index 0971b7e09a..7fbc2ae610 100644 --- a/configs/coins/bsc_archive.json +++ b/configs/coins/bsc_archive.json @@ -59,7 +59,7 @@ "additional_params": { "address_aliases": true, "eip1559Fees": true, - "alternative_estimate_fee": "infura", + "alternative_estimate_fee": "infura-disabled", "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/56/suggestedGasFees\", \"periodSeconds\": 60}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 5310556a22..240cdbadbe 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -61,7 +61,7 @@ "address_aliases": true, "eip1559Fees": true, "alternative_estimate_fee": "infura", - "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/1/suggestedGasFees\", \"periodSeconds\": 20}", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/1/suggestedGasFees\", \"periodSeconds\": 4}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, diff --git a/configs/coins/optimism_archive.json b/configs/coins/optimism_archive.json index ab7f2fcd1f..9719efcca5 100644 --- a/configs/coins/optimism_archive.json +++ b/configs/coins/optimism_archive.json @@ -55,7 +55,7 @@ "address_aliases": true, "eip1559Fees": true, "alternative_estimate_fee": "infura", - "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/10/suggestedGasFees\", \"periodSeconds\": 60}", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/10/suggestedGasFees\", \"periodSeconds\": 10}", "processInternalTransactions": true, "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index 4621e39f68..7aa906a7d3 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -60,7 +60,7 @@ "address_aliases": true, "eip1559Fees": true, "alternative_estimate_fee": "infura", - "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/137/suggestedGasFees\", \"periodSeconds\": 60}", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/137/suggestedGasFees\", \"periodSeconds\": 4}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, From 4aeeccf82ee19959b1669da83db8743ba641aee3 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 24 Mar 2025 23:32:03 +0100 Subject: [PATCH 457/530] Stop returning stale alternative fee data --- bchain/coins/eth/alternativefeeprovider.go | 12 ++++++++---- bchain/coins/eth/infurafees.go | 4 +++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/bchain/coins/eth/alternativefeeprovider.go b/bchain/coins/eth/alternativefeeprovider.go index fab48b33b6..d361df8fbf 100644 --- a/bchain/coins/eth/alternativefeeprovider.go +++ b/bchain/coins/eth/alternativefeeprovider.go @@ -8,10 +8,11 @@ import ( ) type alternativeFeeProvider struct { - eip1559Fees *bchain.Eip1559Fees - lastSync time.Time - chain bchain.BlockChain - mux sync.Mutex + eip1559Fees *bchain.Eip1559Fees + lastSync time.Time + staleSyncDuration time.Duration + chain bchain.BlockChain + mux sync.Mutex } type alternativeFeeProviderInterface interface { @@ -21,5 +22,8 @@ type alternativeFeeProviderInterface interface { func (p *alternativeFeeProvider) GetEip1559Fees() (*bchain.Eip1559Fees, error) { p.mux.Lock() defer p.mux.Unlock() + if p.lastSync.Add(p.staleSyncDuration).Before(time.Now()) { + return nil, nil + } return p.eip1559Fees, nil } diff --git a/bchain/coins/eth/infurafees.go b/bchain/coins/eth/infurafees.go index 448a815603..53443e6160 100644 --- a/bchain/coins/eth/infurafees.go +++ b/bchain/coins/eth/infurafees.go @@ -102,6 +102,8 @@ func NewInfuraFeesProvider(chain bchain.BlockChain, params string) (alternativeF } p.params.URL = strings.Replace(p.params.URL, "${api_key}", p.apiKey, -1) p.chain = chain + // if the data are not successfully downloaded 10 times, stop providing data + p.staleSyncDuration = time.Duration(p.params.PeriodSeconds*10) * time.Second go p.FeeDownloader() return p, nil } @@ -113,7 +115,7 @@ func (p *infuraFeeProvider) FeeDownloader() { var data infuraFeesResult err := p.getData(&data) if err != nil { - glog.Error("infuraFeeProvider.FeeDownloader", err) + glog.Error("infuraFeeProvider.FeeDownloader ", err) } else { p.processData(&data) } From 98f0df104ad7c55a370fc14721daefbbf6b6519a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 20 Apr 2025 16:36:30 +0200 Subject: [PATCH 458/530] =?UTF-8?q?avalanche=201.12.1=20=E2=86=92=201.13.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/avalanche.json | 10 +++++----- configs/coins/avalanche_archive.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/configs/coins/avalanche.json b/configs/coins/avalanche.json index 4514b030b0..97ce0d7c77 100644 --- a/configs/coins/avalanche.json +++ b/configs/coins/avalanche.json @@ -19,10 +19,10 @@ "package_name": "backend-avalanche", "package_revision": "satoshilabs-1", "system_user": "avalanche", - "version": "1.12.1", - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz", + "version": "1.13.0", + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-amd64-v1.13.0.tar.gz", "verification_type": "gpg", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz.sig", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-amd64-v1.13.0.tar.gz.sig", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMEtmUT09IgogIH0KfQ==", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz.sig" + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-arm64-v1.13.0.tar.gz", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-arm64-v1.13.0.tar.gz.sig" } } }, diff --git a/configs/coins/avalanche_archive.json b/configs/coins/avalanche_archive.json index 10ed9113f0..10ee49457d 100644 --- a/configs/coins/avalanche_archive.json +++ b/configs/coins/avalanche_archive.json @@ -19,10 +19,10 @@ "package_name": "backend-avalanche-archive", "package_revision": "satoshilabs-1", "system_user": "avalanche", - "version": "1.12.1", - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz", + "version": "1.13.0", + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-amd64-v1.13.0.tar.gz", "verification_type": "gpg", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz.sig", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-amd64-v1.13.0.tar.gz.sig", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMHNDaUFnSW5CeWRXNXBibWN0Wlc1aFlteGxaQ0k2Wm1Gc2MyVUtmUT09IgogIH0KfQ==", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz.sig" + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-arm64-v1.13.0.tar.gz", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-arm64-v1.13.0.tar.gz.sig" } } }, From 657cbcf4c8626515f86c90245667a0616cdf72fd Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 21 Apr 2025 00:05:25 +0200 Subject: [PATCH 459/530] Detect sync issues in EthereumType coins --- api/worker.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/api/worker.go b/api/worker.go index 17c2f20d24..2c527c663c 100644 --- a/api/worker.go +++ b/api/worker.go @@ -2418,6 +2418,7 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) { start := time.Now().UTC() vi := common.GetVersionInfo() inSync, bestHeight, lastBlockTime, startSync := w.is.GetSyncState() + blockPeriod := w.is.GetAvgBlockPeriod() if !inSync && !w.is.InitialSync { // if less than 5 seconds into syncing, return inSync=true to avoid short time not in sync reports that confuse monitoring if startSync.Add(5 * time.Second).After(start) { @@ -2435,6 +2436,13 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) { inSync = false inSyncMempool = false } + // for networks with stable block period, set not in sync if last sync more than 12 block periods ago + if inSync && blockPeriod > 0 && w.chainType == bchain.ChainEthereumType { + threshold := 12 * time.Duration(blockPeriod) * time.Second + if lastBlockTime.Add(threshold).Before(time.Now().UTC()) { + inSync = false + } + } var columnStats []common.InternalStateColumn var internalDBSize int64 if internal { From 4bdfac1fef1e43127757e95ee806fe55ed87aa94 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Mon, 28 Apr 2025 13:30:53 +0200 Subject: [PATCH 460/530] =?UTF-8?q?zcash=20(+testnet)=206.1.0=20=E2=86=92?= =?UTF-8?q?=206.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/zcash.json | 9 +++++---- configs/coins/zcash_testnet.json | 10 ++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index be6407832f..566c3066de 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,10 +22,10 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "6.1.0", - "binary_url": "https://download.z.cash/downloads/zcash-6.1.0-linux64-debian-bullseye.tar.gz", + "version": "6.2.0", + "binary_url": "https://download.z.cash/downloads/zcash-6.2.0-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "1d17ceacb265599bb4ee690baaf2b335cfe9825df5198359c771ee1834fd4358", + "verification_source": "71cf378c27582a4b9f9d57cafc2b5a57a46e9e52a5eda33be112dc9790c64c6f", "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", @@ -38,7 +38,8 @@ "server_config_file": "bitcoin_like.conf", "client_config_file": "bitcoin_like_client.conf", "additional_params": { - "addnode": ["mainnet.z.cash"] + "addnode": ["mainnet.z.cash"], + "i-am-aware-zcashd-will-be-replaced-by-zebrad-and-zallet-in-2025": 1 } }, "blockbook": { diff --git a/configs/coins/zcash_testnet.json b/configs/coins/zcash_testnet.json index f2a1c388b2..14d5332e78 100644 --- a/configs/coins/zcash_testnet.json +++ b/configs/coins/zcash_testnet.json @@ -21,10 +21,10 @@ "backend": { "package_name": "backend-zcash-testnet", "package_revision": "satoshilabs-1", - "version": "6.1.0", - "binary_url": "https://download.z.cash/downloads/zcash-6.1.0-linux64-debian-bullseye.tar.gz", + "version": "6.2.0", + "binary_url": "https://download.z.cash/downloads/zcash-6.2.0-linux64-debian-bullseye.tar.gz", "verification_type": "sha256", - "verification_source": "1d17ceacb265599bb4ee690baaf2b335cfe9825df5198359c771ee1834fd4358", + "verification_source": "71cf378c27582a4b9f9d57cafc2b5a57a46e9e52a5eda33be112dc9790c64c6f", "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", @@ -39,7 +39,9 @@ "additional_params": { "addnode": [ "testnet.z.cash" - ] + + ], + "i-am-aware-zcashd-will-be-replaced-by-zebrad-and-zallet-in-2025": 1 } }, "blockbook": { From 9dfbb10c47ea8fb01491a2c69b5a1e86fba185e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrej=20=C5=A0korupa?= Date: Wed, 23 Apr 2025 09:35:57 +0200 Subject: [PATCH 461/530] prysm (+testnet): 5.3.2 -> 6.0.0 --- configs/coins/ethereum_archive_consensus.json | 10 +++++----- configs/coins/ethereum_consensus.json | 10 +++++----- .../ethereum_testnet_holesky_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_consensus.json | 10 +++++----- .../ethereum_testnet_sepolia_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_consensus.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index b41f5ae557..1a5e564638 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", + "version": "6.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", + "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", - "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", + "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" } } }, diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index fc8ec2995d..12b71b1c2d 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", + "version": "6.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", + "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", - "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", + "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive_consensus.json b/configs/coins/ethereum_testnet_holesky_archive_consensus.json index 4c4d7c422d..378b7e27c1 100644 --- a/configs/coins/ethereum_testnet_holesky_archive_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", + "version": "6.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", + "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13636 --p2p-udp-port=12636 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", - "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", + "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_consensus.json b/configs/coins/ethereum_testnet_holesky_consensus.json index e72a474252..e64f0d0d6a 100644 --- a/configs/coins/ethereum_testnet_holesky_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", + "version": "6.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", + "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", - "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", + "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json index 198196e268..d0b7d3289e 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", + "version": "6.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", + "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", - "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", + "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json index 1cfd74fb35..22a4ad57ac 100644 --- a/configs/coins/ethereum_testnet_sepolia_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "5.3.2", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-amd64", + "version": "6.0.0", + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5218357057a88758ca3ff2359bd44956d010b56bc4852a66ddfe9560f1505110", + "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v5.3.2/beacon-chain-v5.3.2-linux-arm64", - "verification_source": "5393746e3fdf71521967ff937e8aad789dd238ab526d5eddc802e617a1fd551c" + "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", + "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" } } }, From f18fedac58421508c999a84a4d7b4dad41f42faa Mon Sep 17 00:00:00 2001 From: JoHnY Date: Sat, 3 May 2025 17:24:19 +0200 Subject: [PATCH 462/530] =?UTF-8?q?prysm=206.0.0=20=E2=86=92=206.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum_archive_consensus.json | 10 +++++----- configs/coins/ethereum_consensus.json | 10 +++++----- .../ethereum_testnet_holesky_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_consensus.json | 10 +++++----- .../ethereum_testnet_sepolia_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_consensus.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 1a5e564638..387b5b967b 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", + "version": "6.0.1", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", "verification_type": "sha256", - "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", + "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", - "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", + "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" } } }, diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 12b71b1c2d..5a5cbfa312 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", + "version": "6.0.1", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", "verification_type": "sha256", - "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", + "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", - "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", + "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive_consensus.json b/configs/coins/ethereum_testnet_holesky_archive_consensus.json index 378b7e27c1..73ebec4f5f 100644 --- a/configs/coins/ethereum_testnet_holesky_archive_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", + "version": "6.0.1", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", "verification_type": "sha256", - "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", + "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13636 --p2p-udp-port=12636 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", - "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", + "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_consensus.json b/configs/coins/ethereum_testnet_holesky_consensus.json index e64f0d0d6a..4197c38d33 100644 --- a/configs/coins/ethereum_testnet_holesky_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", + "version": "6.0.1", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", "verification_type": "sha256", - "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", + "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", - "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", + "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json index d0b7d3289e..33653fb74d 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", + "version": "6.0.1", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", "verification_type": "sha256", - "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", + "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", - "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", + "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json index 22a4ad57ac..2091b6b690 100644 --- a/configs/coins/ethereum_testnet_sepolia_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.0", - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-amd64", + "version": "6.0.1", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", "verification_type": "sha256", - "verification_source": "7d1129c23bccdc9dcd901d4a98d1b4cfac50e129a25908032af1ec3c6521fada", + "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/prysmaticlabs/prysm/releases/download/v6.0.0/beacon-chain-v6.0.0-linux-arm64", - "verification_source": "e73998ff5a0646be756e83ecaebaa5c69dcfaeac45acca653f36dd23faef18b4" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", + "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" } } }, From a2aab473b3346f4244dcc5479efa650265268e5d Mon Sep 17 00:00:00 2001 From: AlexanderPavlenko Date: Thu, 1 May 2025 15:27:41 +0400 Subject: [PATCH 463/530] =?UTF-8?q?eth=20(+testnets)=203.0.1=20=E2=86=92?= =?UTF-8?q?=203.0.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum.json | 12 ++++++------ configs/coins/ethereum_archive.json | 12 ++++++------ configs/coins/ethereum_testnet_holesky.json | 12 ++++++------ configs/coins/ethereum_testnet_holesky_archive.json | 12 ++++++------ configs/coins/ethereum_testnet_sepolia.json | 12 ++++++------ configs/coins/ethereum_testnet_sepolia_archive.json | 12 ++++++------ 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 0749118bf1..75c8b929d9 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.1", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", + "version": "3.0.2", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", + "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", - "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", + "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" } } }, @@ -73,4 +73,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 240cdbadbe..a7267975a4 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.1", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", + "version": "3.0.2", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", + "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", - "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", + "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" } } }, @@ -76,4 +76,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index 3540affc47..5c8c6be525 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.1", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", + "version": "3.0.2", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", + "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", - "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", + "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" } } }, @@ -68,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index a54111fa8c..0a37fa43da 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.1", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", + "version": "3.0.2", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", + "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", - "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", + "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" } } }, @@ -76,4 +76,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 7d7924f612..4cb4b653d3 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.1", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", + "version": "3.0.2", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", + "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", - "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", + "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" } } }, @@ -68,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index fbf4db9b5a..b3d0a18eea 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.1", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_amd64.tar.gz", + "version": "3.0.2", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "9f2e222e36f8d2c790f2e06dac57f465e6ee01b296950e6b6fdb13939f434033", + "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.1/erigon_v3.0.1_linux_arm64.tar.gz", - "verification_source": "81087d5fceff3821420f2b3cae48b55c3e884a64f1b8e7fdd4b1420fa664cb3d" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", + "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" } } }, @@ -74,4 +74,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} From 4fafbd60f8953680f34dfd7c92403634c68fd722 Mon Sep 17 00:00:00 2001 From: f7b Date: Mon, 5 May 2025 12:41:10 +0200 Subject: [PATCH 464/530] eth (+testnet) 3.0.2 -> 3.0.3 --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_holesky.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_archive.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 75c8b929d9..b4dc67a1b4 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.2", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", + "version": "3.0.3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", + "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", - "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", + "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index a7267975a4..ee9ff03950 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.2", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", + "version": "3.0.3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", + "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", - "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", + "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" } } }, diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index 5c8c6be525..c819b23750 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.2", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", + "version": "3.0.3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", + "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", - "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", + "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 0a37fa43da..794ef93753 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.2", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", + "version": "3.0.3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", + "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", - "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", + "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 4cb4b653d3..6803ba312a 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.2", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", + "version": "3.0.3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", + "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", - "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", + "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index b3d0a18eea..59926e9f0e 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.2", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_amd64.tar.gz", + "version": "3.0.3", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "db775b9c79b2e3248152dfd72b57f94ad98ac779abb7ae2569c2cb5450756890", + "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.2/erigon_v3.0.2_linux_arm64.tar.gz", - "verification_source": "9a38679827293d9b8e5e8810de7cb6b879011de49f5cea78ab8ebea517785970" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", + "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" } } }, From 386cccafb07ee383651c3233e424b2a94f19d631 Mon Sep 17 00:00:00 2001 From: gruve-p Date: Mon, 5 May 2025 10:49:04 +0200 Subject: [PATCH 465/530] Groestlcoin: Bump to 29.0 --- configs/coins/groestlcoin.json | 8 ++++---- configs/coins/groestlcoin_regtest.json | 8 ++++---- configs/coins/groestlcoin_signet.json | 8 ++++---- configs/coins/groestlcoin_testnet.json | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/configs/coins/groestlcoin.json b/configs/coins/groestlcoin.json index 18b8f5cc87..0434d5108a 100644 --- a/configs/coins/groestlcoin.json +++ b/configs/coins/groestlcoin.json @@ -23,9 +23,9 @@ "package_revision": "satoshilabs-1", "system_user": "groestlcoin", "version": "28.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-x86_64-linux-gnu.tar.gz", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "540d5d7c6bb0449763567ea7c2559e124d61b82a6b2798701d5759458d9c21d7", + "verification_source": "e0b3e3d96caf908060779c0d9964c777ccc4b7364af54404ff1768e018e56768", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-aarch64-linux-gnu.tar.gz", - "verification_source": "092c6ff333a3defe2603b91c55aea6415e554a2bbc6abb3ad43ac712fa9b63b1" + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-aarch64-linux-gnu.tar.gz", + "verification_source": "43b67b0945eb63c26bf0106ce3e302d4fe0720900cd8658e84f5d7954899a2a8" } } }, diff --git a/configs/coins/groestlcoin_regtest.json b/configs/coins/groestlcoin_regtest.json index aaa4ba27e3..ffa21199ea 100644 --- a/configs/coins/groestlcoin_regtest.json +++ b/configs/coins/groestlcoin_regtest.json @@ -23,9 +23,9 @@ "package_revision": "satoshilabs-1", "system_user": "groestlcoin", "version": "28.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-x86_64-linux-gnu.tar.gz", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "540d5d7c6bb0449763567ea7c2559e124d61b82a6b2798701d5759458d9c21d7", + "verification_source": "e0b3e3d96caf908060779c0d9964c777ccc4b7364af54404ff1768e018e56768", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-aarch64-linux-gnu.tar.gz", - "verification_source": "092c6ff333a3defe2603b91c55aea6415e554a2bbc6abb3ad43ac712fa9b63b1" + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-aarch64-linux-gnu.tar.gz", + "verification_source": "43b67b0945eb63c26bf0106ce3e302d4fe0720900cd8658e84f5d7954899a2a8" } } }, diff --git a/configs/coins/groestlcoin_signet.json b/configs/coins/groestlcoin_signet.json index 53df59f7eb..0ca0be12cf 100644 --- a/configs/coins/groestlcoin_signet.json +++ b/configs/coins/groestlcoin_signet.json @@ -23,9 +23,9 @@ "package_revision": "satoshilabs-1", "system_user": "groestlcoin", "version": "28.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-x86_64-linux-gnu.tar.gz", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "540d5d7c6bb0449763567ea7c2559e124d61b82a6b2798701d5759458d9c21d7", + "verification_source": "e0b3e3d96caf908060779c0d9964c777ccc4b7364af54404ff1768e018e56768", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-aarch64-linux-gnu.tar.gz", - "verification_source": "092c6ff333a3defe2603b91c55aea6415e554a2bbc6abb3ad43ac712fa9b63b1" + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-aarch64-linux-gnu.tar.gz", + "verification_source": "43b67b0945eb63c26bf0106ce3e302d4fe0720900cd8658e84f5d7954899a2a8" } } }, diff --git a/configs/coins/groestlcoin_testnet.json b/configs/coins/groestlcoin_testnet.json index 7106edef08..f6c5d7bcd2 100644 --- a/configs/coins/groestlcoin_testnet.json +++ b/configs/coins/groestlcoin_testnet.json @@ -23,9 +23,9 @@ "package_revision": "satoshilabs-1", "system_user": "groestlcoin", "version": "28.0", - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-x86_64-linux-gnu.tar.gz", + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "540d5d7c6bb0449763567ea7c2559e124d61b82a6b2798701d5759458d9c21d7", + "verification_source": "e0b3e3d96caf908060779c0d9964c777ccc4b7364af54404ff1768e018e56768", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/groestlcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/groestlcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v28.0/groestlcoin-28.0-aarch64-linux-gnu.tar.gz", - "verification_source": "092c6ff333a3defe2603b91c55aea6415e554a2bbc6abb3ad43ac712fa9b63b1" + "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-aarch64-linux-gnu.tar.gz", + "verification_source": "43b67b0945eb63c26bf0106ce3e302d4fe0720900cd8658e84f5d7954899a2a8" } } }, From b14641d979241b93108025f2a72b4517ab4db260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Musil?= Date: Wed, 7 May 2025 10:36:46 +0200 Subject: [PATCH 466/530] feat: support new alternative_estimate_fee option - mempoolspacemedian (#1233) * feat: support new alternative_estimate_fee option - mempoolspacemedian * chore: introduce a fallback median fee rate in case it comes zero from mempool space response * chore: add and test util function for rounding to significant digits * chore: use rounding of medianFee to 3 significant digits * chore: make the new alternative_estimate_fee be configurable, change its name from Median to Block * chore: return 1000 sats/kB fee for MempoolSpaceBlock estimation method when block not identified * chore: make the fallback fee rate configurable, improve tests, improve function names --- bchain/coins/btc/alternativefeeprovider.go | 15 +- bchain/coins/btc/bitcoinrpc.go | 13 ++ bchain/coins/btc/mempoolspaceblock.go | 201 +++++++++++++++++++++ bchain/coins/btc/mempoolspaceblock_test.go | 198 ++++++++++++++++++++ common/utils.go | 40 ++++ common/utils_test.go | 44 +++++ configs/coins/bitcoin_regtest.json | 2 + 7 files changed, 509 insertions(+), 4 deletions(-) create mode 100644 bchain/coins/btc/mempoolspaceblock.go create mode 100644 bchain/coins/btc/mempoolspaceblock_test.go create mode 100644 common/utils_test.go diff --git a/bchain/coins/btc/alternativefeeprovider.go b/bchain/coins/btc/alternativefeeprovider.go index 6bdf0e6a2b..7d72acdbdd 100644 --- a/bchain/coins/btc/alternativefeeprovider.go +++ b/bchain/coins/btc/alternativefeeprovider.go @@ -17,10 +17,11 @@ type alternativeFeeProviderFee struct { } type alternativeFeeProvider struct { - fees []alternativeFeeProviderFee - lastSync time.Time - chain bchain.BlockChain - mux sync.Mutex + fees []alternativeFeeProviderFee + lastSync time.Time + chain bchain.BlockChain + mux sync.Mutex + fallbackFeePerKBIfNotAvailable int } type alternativeFeeProviderInterface interface { @@ -62,6 +63,12 @@ func (p *alternativeFeeProvider) estimateFee(blocks int) (big.Int, error) { return r, nil } } + + if p.fallbackFeePerKBIfNotAvailable > 0 { + r = *big.NewInt(int64(p.fallbackFeePerKBIfNotAvailable)) + return r, nil + } + // use the last value as fallback r = *big.NewInt(int64(p.fees[len(p.fees)-1].feePerKB)) return r, nil diff --git a/bchain/coins/btc/bitcoinrpc.go b/bchain/coins/btc/bitcoinrpc.go index e378d417bd..00b3f468ea 100644 --- a/bchain/coins/btc/bitcoinrpc.go +++ b/bchain/coins/btc/bitcoinrpc.go @@ -144,17 +144,30 @@ func (b *BitcoinRPC) Initialize() error { glog.Info("rpc: block chain ", params.Name) if b.ChainConfig.AlternativeEstimateFee == "whatthefee" { + glog.Info("Using WhatTheFee") if b.alternativeFeeProvider, err = NewWhatTheFee(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil { glog.Error("NewWhatTheFee error ", err, " Reverting to default estimateFee functionality") // disable AlternativeEstimateFee logic b.alternativeFeeProvider = nil } } else if b.ChainConfig.AlternativeEstimateFee == "mempoolspace" { + glog.Info("Using MempoolSpaceFee") if b.alternativeFeeProvider, err = NewMempoolSpaceFee(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil { glog.Error("MempoolSpaceFee error ", err, " Reverting to default estimateFee functionality") // disable AlternativeEstimateFee logic b.alternativeFeeProvider = nil } + } else if b.ChainConfig.AlternativeEstimateFee == "mempoolspaceblock" { + glog.Info("Using MempoolSpaceBlockFee") + if b.alternativeFeeProvider, err = NewMempoolSpaceBlockFee(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil { + glog.Error("MempoolSpaceBlockFee error ", err, " Reverting to default estimateFee functionality") + // disable AlternativeEstimateFee logic + b.alternativeFeeProvider = nil + } + } else if len(b.ChainConfig.AlternativeEstimateFee) > 0 { + glog.Error("AlternativeEstimateFee ", b.ChainConfig.AlternativeEstimateFee, " not supported") + } else { + glog.Info("Using default estimateFee") } return nil diff --git a/bchain/coins/btc/mempoolspaceblock.go b/bchain/coins/btc/mempoolspaceblock.go new file mode 100644 index 0000000000..61a9dcada9 --- /dev/null +++ b/bchain/coins/btc/mempoolspaceblock.go @@ -0,0 +1,201 @@ +package btc + +import ( + "encoding/json" + "math" + "net/http" + "strconv" + "time" + + "github.com/golang/glog" + "github.com/juju/errors" + "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/common" +) + +// https://mempool.space/api/v1/fees/mempool-blocks returns a list of upcoming blocks and their medianFee. +// Example response: +// [ +// { +// "blockSize": 1604493, +// "blockVSize": 997944.75, +// "nTx": 3350, +// "totalFees": 8333539, +// "medianFee": 3.0073509137538332, +// "feeRange": [ +// 2.0444444444444443, +// 2.2135922330097086, +// 2.608695652173913, +// 3.016042780748663, +// 4.0048289738430585, +// 9.27631139325092, +// 201.06951871657753 +// ] +// }, +// ... +// ] + +type mempoolSpaceBlockFeeResult struct { + BlockSize float64 `json:"blockSize"` + BlockVSize float64 `json:"blockVSize"` + NTx int `json:"nTx"` + TotalFees int `json:"totalFees"` + MedianFee float64 `json:"medianFee"` + // 2nd, 10th, 25th, 50th, 75th, 90th, 98th percentiles + FeeRange []float64 `json:"feeRange"` +} + +type mempoolSpaceBlockFeeParams struct { + URL string `json:"url"` + PeriodSeconds int `json:"periodSeconds"` + // Either number, then take the specified index. If null or missing, take the medianFee + FeeRangeIndex *int `json:"feeRangeIndex,omitempty"` + FallbackFeePerKB int `json:"fallbackFeePerKB,omitempty"` +} + +type mempoolSpaceBlockFeeProvider struct { + *alternativeFeeProvider + params mempoolSpaceBlockFeeParams +} + +// NewMempoolSpaceBlockFee initializes the provider completely. +func NewMempoolSpaceBlockFee(chain bchain.BlockChain, params string) (alternativeFeeProviderInterface, error) { + var paramsParsed mempoolSpaceBlockFeeParams + err := json.Unmarshal([]byte(params), ¶msParsed) + if err != nil { + return nil, err + } + + p, err := NewMempoolSpaceBlockFeeProviderFromParamsWithoutChain(paramsParsed) + if err != nil { + return nil, err + } + + p.chain = chain + go p.downloader() + return p, nil +} + +// NewMempoolSpaceBlockFeeProviderFromParamsWithoutChain initializes the provider from already parsed parameters and without chain. +// Refactored like this for better testability. +func NewMempoolSpaceBlockFeeProviderFromParamsWithoutChain(params mempoolSpaceBlockFeeParams) (*mempoolSpaceBlockFeeProvider, error) { + // Check mandatory parameters + if params.URL == "" { + return nil, errors.New("NewMempoolSpaceBlockFee: Missing url") + } + if params.PeriodSeconds == 0 { + return nil, errors.New("NewMempoolSpaceBlockFee: Missing periodSeconds") + } + + // Report on what is used + if params.FeeRangeIndex == nil { + glog.Info("NewMempoolSpaceBlockFee: Using median fee") + } else { + index := *params.FeeRangeIndex + if index < 0 || index > 6 { + return nil, errors.New("NewMempoolSpaceBlockFee: feeRangeIndex must be between 0 and 6") + } + glog.Infof("NewMempoolSpaceBlockFee: Using feeRangeIndex %d", index) + } + + p := &mempoolSpaceBlockFeeProvider{ + alternativeFeeProvider: &alternativeFeeProvider{}, + params: params, + } + + if params.FallbackFeePerKB > 0 { + p.fallbackFeePerKBIfNotAvailable = params.FallbackFeePerKB + } + + return p, nil +} + +func (p *mempoolSpaceBlockFeeProvider) downloader() { + period := time.Duration(p.params.PeriodSeconds) * time.Second + timer := time.NewTimer(period) + counter := 0 + for { + var data []mempoolSpaceBlockFeeResult + err := p.getData(&data) + if err != nil { + glog.Error("getData ", err) + } else { + if p.processData(&data) { + if counter%60 == 0 { + p.compareToDefault() + } + counter++ + } + } + <-timer.C + timer.Reset(period) + } +} + +func (p *mempoolSpaceBlockFeeProvider) processData(data *[]mempoolSpaceBlockFeeResult) bool { + if len(*data) == 0 { + glog.Error("processData: empty data") + return false + } + + p.mux.Lock() + defer p.mux.Unlock() + + p.fees = make([]alternativeFeeProviderFee, 0, len(*data)) + + for i, block := range *data { + var fee float64 + + if p.params.FeeRangeIndex == nil { + fee = block.MedianFee + } else { + feeRange := block.FeeRange + index := *p.params.FeeRangeIndex + if len(feeRange) > index { + fee = feeRange[index] + } else { + glog.Warningf("Block %d has too short feeRange (len=%d, required=%d). Replacing by medianFee", i, len(feeRange), index) + fee = block.MedianFee + } + } + + if fee < 1 { + glog.Warningf("Skipping block at index %d due to invalid fee: %f", i, fee) + continue + } + + // TODO: it might make sense to not include _every_ block, but only e.g. first 20 and then some hardcoded ones like 50, 100, 200, etc. + // But even storing thousands of elements in []alternativeFeeProviderFee should not make a big performance overhead + // Depends on Suite requirements + + // We want to convert the fee to 3 significant digits + feeRounded := common.RoundToSignificantDigits(fee, 3) + feePerKB := int(math.Round(feeRounded * 1000)) + + p.fees = append(p.fees, alternativeFeeProviderFee{ + blocks: i + 1, + feePerKB: feePerKB, + }) + } + + p.lastSync = time.Now() + return true +} + +func (p *mempoolSpaceBlockFeeProvider) getData(res interface{}) error { + httpReq, err := http.NewRequest("GET", p.params.URL, nil) + if err != nil { + return err + } + httpRes, err := http.DefaultClient.Do(httpReq) + if httpRes != nil { + defer httpRes.Body.Close() + } + if err != nil { + return err + } + if httpRes.StatusCode != http.StatusOK { + return errors.New(p.params.URL + " returned status " + strconv.Itoa(httpRes.StatusCode)) + } + return common.SafeDecodeResponseFromReader(httpRes.Body, res) +} diff --git a/bchain/coins/btc/mempoolspaceblock_test.go b/bchain/coins/btc/mempoolspaceblock_test.go new file mode 100644 index 0000000000..09e2bcfede --- /dev/null +++ b/bchain/coins/btc/mempoolspaceblock_test.go @@ -0,0 +1,198 @@ +//go:build unittest + +package btc + +import ( + "math/big" + "strconv" + "strings" + "testing" +) + +var testBlocks = []mempoolSpaceBlockFeeResult{ + { + BlockSize: 1800000, + BlockVSize: 997931, + NTx: 2500, + TotalFees: 6000000, + MedianFee: 25.1, + FeeRange: []float64{1, 5, 10, 20, 30, 50, 300}, + }, + { + BlockSize: 1750000, + BlockVSize: 997930, + NTx: 2200, + TotalFees: 4500000, + MedianFee: 7.31, + FeeRange: []float64{1, 2, 5, 10, 15, 20, 150}, + }, + { + BlockSize: 1700000, + BlockVSize: 997929, + NTx: 2000, + TotalFees: 3000000, + MedianFee: 3.14, + FeeRange: []float64{1, 1.5, 2, 5, 7, 10, 100}, + }, + { + BlockSize: 1650000, + BlockVSize: 997928, + NTx: 1800, + TotalFees: 2000000, + MedianFee: 1.34, + FeeRange: []float64{1, 1.2, 1.5, 3, 4, 5, 50}, + }, + { + BlockSize: 1600000, + BlockVSize: 997927, + NTx: 1500, + TotalFees: 1500000, + MedianFee: 1.11, + FeeRange: []float64{1, 1.05, 1.1, 1.5, 1.8, 2, 20}, + }, +} + +var estimateFeeTestCasesMedian = []struct { + blocks int + want big.Int +}{ + {0, *big.NewInt(25100)}, + {1, *big.NewInt(25100)}, + {2, *big.NewInt(7310)}, + {3, *big.NewInt(3140)}, + {4, *big.NewInt(1340)}, + {5, *big.NewInt(1110)}, + {6, *big.NewInt(1110)}, + {7, *big.NewInt(1110)}, + {10, *big.NewInt(1110)}, + {36, *big.NewInt(1110)}, + {100, *big.NewInt(1110)}, + {201, *big.NewInt(1110)}, + {501, *big.NewInt(1110)}, + {5000000, *big.NewInt(1110)}, +} + +var estimateFeeTestCasesFeeRangeIndex5FallbackSet = []struct { + blocks int + want big.Int +}{ + {0, *big.NewInt(50000)}, + {1, *big.NewInt(50000)}, + {2, *big.NewInt(20000)}, + {3, *big.NewInt(10000)}, + {4, *big.NewInt(5000)}, + {5, *big.NewInt(2000)}, + {6, *big.NewInt(1000)}, + {7, *big.NewInt(1000)}, + {10, *big.NewInt(1000)}, + {36, *big.NewInt(1000)}, + {100, *big.NewInt(1000)}, + {201, *big.NewInt(1000)}, + {501, *big.NewInt(1000)}, + {5000000, *big.NewInt(1000)}, +} + +func runEstimateFeeTest(t *testing.T, testName string, m *mempoolSpaceBlockFeeProvider, expected []struct { + blocks int + want big.Int +}) { + success := m.processData(&testBlocks) + if !success { + t.Fatalf("[%s] Expected data to be processed successfully", testName) + } + + for _, tt := range expected { + t.Run(testName+"_"+strconv.Itoa(tt.blocks), func(t *testing.T) { + got, err := m.estimateFee(tt.blocks) + if err != nil { + t.Errorf("[%s] estimateFee returned error: %v", testName, err) + } + if got.Cmp(&tt.want) != 0 { + t.Errorf("[%s] estimateFee(%d) = %v, want %v", testName, tt.blocks, got, tt.want) + } + }) + } +} + +func Test_mempoolSpaceBlockFeeProviderMedian(t *testing.T) { + // Taking the median explicitly + m, err := + NewMempoolSpaceBlockFeeProviderFromParamsWithoutChain(mempoolSpaceBlockFeeParams{ + URL: "https://mempool.space/api/v1/fees/mempool-blocks", + PeriodSeconds: 20, + FeeRangeIndex: nil, + }) + if err != nil { + t.Fatalf("NewMempoolSpaceBlockFeeProviderFromParamsWithoutChain returned error: %v", err) + } + runEstimateFeeTest(t, "median", m, estimateFeeTestCasesMedian) +} + +func Test_mempoolSpaceBlockFeeProviderSecondLargestIndex(t *testing.T) { + // Taking the valid index + index := 5 + m, err := + NewMempoolSpaceBlockFeeProviderFromParamsWithoutChain(mempoolSpaceBlockFeeParams{ + URL: "https://mempool.space/api/v1/fees/mempool-blocks", + PeriodSeconds: 20, + FeeRangeIndex: &index, + FallbackFeePerKB: 1000, + }) + if err != nil { + t.Fatalf("NewMempoolSpaceBlockFeeProviderFromParamsWithoutChain returned error: %v", err) + } + runEstimateFeeTest(t, "feeRangeIndex_5", m, estimateFeeTestCasesFeeRangeIndex5FallbackSet) +} + +func Test_mempoolSpaceBlockFeeProviderInvalidIndexTooHigh(t *testing.T) { + index := 555 + _, err := + NewMempoolSpaceBlockFeeProviderFromParamsWithoutChain(mempoolSpaceBlockFeeParams{ + URL: "https://mempool.space/api/v1/fees/mempool-blocks", + PeriodSeconds: 20, + FeeRangeIndex: &index, + }) + + if err == nil { + t.Fatalf("expected error, got nil") + } + + expectedSubstring := "feeRangeIndex must be between 0 and 6" + if !strings.Contains(err.Error(), expectedSubstring) { + t.Errorf("expected error message to contain %q, got: %v", expectedSubstring, err) + } +} + +func Test_mempoolSpaceBlockFeeProviderMissingUrl(t *testing.T) { + _, err := + NewMempoolSpaceBlockFeeProviderFromParamsWithoutChain(mempoolSpaceBlockFeeParams{ + PeriodSeconds: 20, + FeeRangeIndex: nil, + }) + + if err == nil { + t.Fatalf("expected error, got nil") + } + + expectedSubstring := "Missing url" + if !strings.Contains(err.Error(), expectedSubstring) { + t.Errorf("expected error message to contain %q, got: %v", expectedSubstring, err) + } +} + +func Test_mempoolSpaceBlockFeeProviderMissingPeriodSeconds(t *testing.T) { + _, err := + NewMempoolSpaceBlockFeeProviderFromParamsWithoutChain(mempoolSpaceBlockFeeParams{ + URL: "https://mempool.space/api/v1/fees/mempool-blocks", + FeeRangeIndex: nil, + }) + + if err == nil { + t.Fatalf("expected error, got nil") + } + + expectedSubstring := "Missing periodSeconds" + if !strings.Contains(err.Error(), expectedSubstring) { + t.Errorf("expected error message to contain %q, got: %v", expectedSubstring, err) + } +} diff --git a/common/utils.go b/common/utils.go index e90e116639..4dee4686e0 100644 --- a/common/utils.go +++ b/common/utils.go @@ -3,6 +3,7 @@ package common import ( "encoding/json" "io" + "math" "runtime/debug" "time" @@ -66,3 +67,42 @@ func SafeDecodeResponseFromReader(body io.ReadCloser, res interface{}) (err erro } return json.Unmarshal(data, &res) } + +// RoundToSignificantDigits rounds a float64 number `n` to the specified number of significant figures `digits`. +// For example, RoundToSignificantDigits(1234, 3) returns 1230 +// +// This function works by shifting the number's decimal point to make the desired significant figures +// into whole numbers, rounding, and then shifting back. +// +// Example for n = 1234, digits = 3: +// +// log10(1234) ≈ 3.09 → ceil = 4 +// power = 3 - 4 = -1 +// magnitude = 10^-1 = 0.1 +// n * magnitude = 1234 * 0.1 = 123.4 +// round(123.4) = 123 +// 123 / 0.1 = 1230 +// +// Returns the number rounded to the desired number of significant figures. +func RoundToSignificantDigits(n float64, digits int) float64 { + if n == 0 { + return 0 + } + + // Step 1: Compute how many digits are before the decimal point. + // For 1234 → log10(1234) ≈ 3.09 → ceil = 4 + d := math.Ceil(math.Log10(math.Abs(n))) + + // Step 2: Calculate how much we need to shift the number to bring + // the significant digits into the integer part. + // For digits=3 and d=4 → power = -1 + power := digits - int(d) + + // Step 3: Compute 10^power to scale the number + // 10^-1 = 0.1 + magnitude := math.Pow(10, float64(power)) + + // Step 4: Scale, round, and scale back + // 1234 * 0.1 = 123.4 → round = 123 → 123 / 0.1 = 1230 + return math.Round(n*magnitude) / magnitude +} diff --git a/common/utils_test.go b/common/utils_test.go new file mode 100644 index 0000000000..6076742030 --- /dev/null +++ b/common/utils_test.go @@ -0,0 +1,44 @@ +//go:build unittest + +package common + +import ( + "math" + "strconv" + "testing" +) + +func Test_RoundToSignificantDigits(t *testing.T) { + type testCase struct { + input float64 + digits int + want float64 + } + + tests := []testCase{ + {input: 1234.5678, digits: 3, want: 1230}, + {input: 1234.5678, digits: 4, want: 1235}, + {input: 1234.5678, digits: 5, want: 1234.6}, + {input: 0.0123456, digits: 3, want: 0.0123}, + {input: 98765.4321, digits: 3, want: 98800}, + {input: 1.99999, digits: 3, want: 2.00}, + {input: 999.999, digits: 3, want: 1000}, + {input: 0.0006789, digits: 3, want: 0.000679}, + {input: 5.123456, digits: 3, want: 5.12}, + {input: 4.456789, digits: 3, want: 4.46}, + {input: 3.789012, digits: 3, want: 3.79}, + {input: 2.012345, digits: 3, want: 2.01}, + } + + for _, tt := range tests { + t.Run(strconv.FormatFloat(tt.input, 'f', -1, 64), func(t *testing.T) { + got := RoundToSignificantDigits(tt.input, tt.digits) + + // Use relative epsilon for float comparison + epsilon := 1e-9 + if math.Abs(got-tt.want) > epsilon { + t.Errorf("RoundToSignificantDigits(%v, %d) = %v, want %v", tt.input, tt.digits, got, tt.want) + } + }) + } +} diff --git a/configs/coins/bitcoin_regtest.json b/configs/coins/bitcoin_regtest.json index 680247808b..88ceed7e9a 100644 --- a/configs/coins/bitcoin_regtest.json +++ b/configs/coins/bitcoin_regtest.json @@ -64,6 +64,8 @@ "xpub_magic_segwit_native": 73342198, "slip44": 1, "additional_params": { + "alternative_estimate_fee": "mempoolspaceblock", + "alternative_estimate_fee_params": "{\"url\": \"https://mempool.space/api/v1/fees/mempool-blocks\", \"periodSeconds\": 20, \"feeRangeIndex\": 5, \"fallbackFeePerKB\": 1000}", "block_golomb_filter_p": 20, "block_filter_scripts": "taproot-noordinals", "block_filter_use_zeroed_key": true, From dcdb2bd25a36da6e5d90a148cc183692c4e14ef6 Mon Sep 17 00:00:00 2001 From: gruve-p Date: Wed, 7 May 2025 11:02:51 +0200 Subject: [PATCH 467/530] Bump version GRS --- configs/coins/groestlcoin.json | 2 +- configs/coins/groestlcoin_regtest.json | 2 +- configs/coins/groestlcoin_signet.json | 2 +- configs/coins/groestlcoin_testnet.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/coins/groestlcoin.json b/configs/coins/groestlcoin.json index 0434d5108a..367a2071c6 100644 --- a/configs/coins/groestlcoin.json +++ b/configs/coins/groestlcoin.json @@ -22,7 +22,7 @@ "package_name": "backend-groestlcoin", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "28.0", + "version": "29.0", "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", "verification_source": "e0b3e3d96caf908060779c0d9964c777ccc4b7364af54404ff1768e018e56768", diff --git a/configs/coins/groestlcoin_regtest.json b/configs/coins/groestlcoin_regtest.json index ffa21199ea..4d6ae18c80 100644 --- a/configs/coins/groestlcoin_regtest.json +++ b/configs/coins/groestlcoin_regtest.json @@ -22,7 +22,7 @@ "package_name": "backend-groestlcoin-regtest", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "28.0", + "version": "29.0", "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", "verification_source": "e0b3e3d96caf908060779c0d9964c777ccc4b7364af54404ff1768e018e56768", diff --git a/configs/coins/groestlcoin_signet.json b/configs/coins/groestlcoin_signet.json index 0ca0be12cf..2125aa4109 100644 --- a/configs/coins/groestlcoin_signet.json +++ b/configs/coins/groestlcoin_signet.json @@ -22,7 +22,7 @@ "package_name": "backend-groestlcoin-signet", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "28.0", + "version": "29.0", "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", "verification_source": "e0b3e3d96caf908060779c0d9964c777ccc4b7364af54404ff1768e018e56768", diff --git a/configs/coins/groestlcoin_testnet.json b/configs/coins/groestlcoin_testnet.json index f6c5d7bcd2..2b0e15aaec 100644 --- a/configs/coins/groestlcoin_testnet.json +++ b/configs/coins/groestlcoin_testnet.json @@ -22,7 +22,7 @@ "package_name": "backend-groestlcoin-testnet", "package_revision": "satoshilabs-1", "system_user": "groestlcoin", - "version": "28.0", + "version": "29.0", "binary_url": "https://github.com/Groestlcoin/groestlcoin/releases/download/v29.0/groestlcoin-29.0-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", "verification_source": "e0b3e3d96caf908060779c0d9964c777ccc4b7364af54404ff1768e018e56768", From e481c2cc2b21b2b0ead083c1181562ba7c658027 Mon Sep 17 00:00:00 2001 From: f7b Date: Mon, 12 May 2025 08:38:23 +0200 Subject: [PATCH 468/530] polygon-bor 2.0.1 -> 2.0.3 --- configs/coins/polygon.json | 14 +++++++------- configs/coins/polygon_archive.json | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/configs/coins/polygon.json b/configs/coins/polygon.json index 4f24eb0bd4..d900742ed1 100644 --- a/configs/coins/polygon.json +++ b/configs/coins/polygon.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "2.0.1", - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.1/bor-v2.0.1-amd64.deb", + "version": "2.0.3", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.3/bor-v2.0.3-amd64.deb", "verification_type": "sha256", - "verification_source": "879d72f58a1d779d00c27446b4e5652f8e22a123e8ea09f5b5757092920109fd", + "verification_source": "bf0a1316411dfa70decc7a12e1d46c0690e7f3a25d78d59a63721d0a271da183", "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.3/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.1/bor-v2.0.1-arm64.deb", - "verification_source": "9a77d0cdaa7d0c2d3152fd184a47905b24b6b05777d44f5cda2cb406fb82a3c5" + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.3/bor-v2.0.3-arm64.deb", + "verification_source": "0d8ed9eba551cd242ac2616c8c531969f58655e951403b374810a227d0c5b6d6" } } }, @@ -69,4 +69,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index 7aa906a7d3..0c8f4e4793 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-archive-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "2.0.1", - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.1/bor-v2.0.1-amd64.deb", + "version": "2.0.3", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.3/bor-v2.0.3-amd64.deb", "verification_type": "sha256", - "verification_source": "879d72f58a1d779d00c27446b4e5652f8e22a123e8ea09f5b5757092920109fd", + "verification_source": "bf0a1316411dfa70decc7a12e1d46c0690e7f3a25d78d59a63721d0a271da183", "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.3/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.1/bor-v2.0.1-arm64.deb", - "verification_source": "9a77d0cdaa7d0c2d3152fd184a47905b24b6b05777d44f5cda2cb406fb82a3c5" + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.3/bor-v2.0.3-arm64.deb", + "verification_source": "0d8ed9eba551cd242ac2616c8c531969f58655e951403b374810a227d0c5b6d6" } } }, @@ -75,4 +75,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file From 147f903342c1cdabfef656a0ea6038fc681793c8 Mon Sep 17 00:00:00 2001 From: f7b Date: Tue, 13 May 2025 07:44:42 +0200 Subject: [PATCH 469/530] =?UTF-8?q?prysm=206.0.1=20=E2=86=92=206.0.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/ethereum_archive_consensus.json | 10 +++++----- configs/coins/ethereum_consensus.json | 10 +++++----- .../ethereum_testnet_holesky_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_consensus.json | 10 +++++----- .../ethereum_testnet_sepolia_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_consensus.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 387b5b967b..c08434a6aa 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.1", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", + "version": "6.0.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", "verification_type": "sha256", - "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", + "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", - "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", + "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" } } }, diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 5a5cbfa312..bb5c5009ce 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.1", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", + "version": "6.0.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", "verification_type": "sha256", - "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", + "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", - "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", + "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive_consensus.json b/configs/coins/ethereum_testnet_holesky_archive_consensus.json index 73ebec4f5f..3870519530 100644 --- a/configs/coins/ethereum_testnet_holesky_archive_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.1", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", + "version": "6.0.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", "verification_type": "sha256", - "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", + "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13636 --p2p-udp-port=12636 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", - "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", + "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_consensus.json b/configs/coins/ethereum_testnet_holesky_consensus.json index 4197c38d33..0abeb4eb2d 100644 --- a/configs/coins/ethereum_testnet_holesky_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.1", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", + "version": "6.0.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", "verification_type": "sha256", - "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", + "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", - "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", + "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json index 33653fb74d..c874cdbaff 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.1", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", + "version": "6.0.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", "verification_type": "sha256", - "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", + "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", - "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", + "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json index 2091b6b690..692f9f2e2c 100644 --- a/configs/coins/ethereum_testnet_sepolia_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.1", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-amd64", + "version": "6.0.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", "verification_type": "sha256", - "verification_source": "74e3ac8aba4f56e44678b45fb7941b5e9f866758937d8f5e9cfa6d4bad46dee1", + "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.1/beacon-chain-v6.0.1-linux-arm64", - "verification_source": "256676cd8bc2bc8829d0e7841267ee7cfaf56e62206b48b0551de8eca3a2a75f" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", + "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" } } }, From 4516eebb92de8bf1214765b6b107dc9adae38a4c Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 8 May 2025 10:06:16 +0200 Subject: [PATCH 470/530] Use mempoolspaceblock fee estimation for BTC --- configs/coins/bitcoin.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index 79bd98266b..2665f6f627 100644 --- a/configs/coins/bitcoin.json +++ b/configs/coins/bitcoin.json @@ -64,8 +64,8 @@ "xpub_magic_segwit_p2sh": 77429938, "xpub_magic_segwit_native": 78792518, "additional_params": { - "alternative_estimate_fee": "mempoolspace", - "alternative_estimate_fee_params": "{\"url\": \"https://mempool.space/api/v1/fees/recommended\", \"periodSeconds\": 20}", + "alternative_estimate_fee": "mempoolspaceblock", + "alternative_estimate_fee_params": "{\"url\": \"https://mempool.space/api/v1/fees/mempool-blocks\", \"periodSeconds\": 20, \"feeRangeIndex\": 5, \"fallbackFeePerKB\": 1000}", "fiat_rates": "coingecko", "fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH", "fiat_rates_params": "{\"coin\": \"bitcoin\", \"periodSeconds\": 60}", From 91bdb8c9486f7b4b664b3a179ea868c9cb69fb89 Mon Sep 17 00:00:00 2001 From: grdddj Date: Tue, 15 Oct 2024 13:19:21 +0200 Subject: [PATCH 471/530] feat: differentiate between Sending and Receiving unconfirmed balance for Address --- api/types.go | 2 ++ api/worker.go | 19 ++++++++++++++++--- blockbook-api.ts | 2 ++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/api/types.go b/api/types.go index 63c03cbc4b..84c98ae3eb 100644 --- a/api/types.go +++ b/api/types.go @@ -366,6 +366,8 @@ type Address struct { TotalSentSat *Amount `json:"totalSent,omitempty"` UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance"` UnconfirmedTxs int `json:"unconfirmedTxs"` + UnconfirmedSending *Amount `json:"unconfirmedSending,omitempty"` + UnconfirmedReceiving *Amount `json:"unconfirmedReceiving,omitempty"` Txs int `json:"txs"` AddrTxCount int `json:"addrTxCount,omitempty"` NonTokenTxs int `json:"nonTokenTxs,omitempty"` diff --git a/api/worker.go b/api/worker.go index 2c527c663c..d261590e53 100644 --- a/api/worker.go +++ b/api/worker.go @@ -1331,6 +1331,8 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco txids []string pg Paging uBalSat big.Int + uBalSending big.Int + uBalReceiving big.Int totalReceived, totalSent *big.Int unconfirmedTxs int totalResults int @@ -1382,12 +1384,12 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco // skip already confirmed txs, mempool may be out of sync if tx.Confirmations == 0 { unconfirmedTxs++ - uBalSat.Add(&uBalSat, tx.getAddrVoutValue(addrDesc)) + uBalReceiving.Add(&uBalReceiving, tx.getAddrVoutValue(addrDesc)) // ethereum has a different logic - value not in input and add maximum possible fees if w.chainType == bchain.ChainEthereumType { - uBalSat.Sub(&uBalSat, tx.getAddrEthereumTypeMempoolInputValue(addrDesc)) + uBalSending.Add(&uBalSending, tx.getAddrEthereumTypeMempoolInputValue(addrDesc)) } else { - uBalSat.Sub(&uBalSat, tx.getAddrVinValue(addrDesc)) + uBalSending.Add(&uBalSending, tx.getAddrVinValue(addrDesc)) } if page == 0 { if option == AccountDetailsTxidHistory { @@ -1454,6 +1456,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco totalSecondaryValue = secondaryRate * totalBaseValue } } + uBalSat.Sub(&uBalReceiving, &uBalSending) r := &Address{ Paging: pg, AddrStr: address, @@ -1465,6 +1468,8 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco InternalTxs: ed.internalTxs, UnconfirmedBalanceSat: (*Amount)(&uBalSat), UnconfirmedTxs: unconfirmedTxs, + UnconfirmedSending: amountOrNil(&uBalSending), + UnconfirmedReceiving: amountOrNil(&uBalReceiving), Transactions: txs, Txids: txids, Tokens: ed.tokens, @@ -1486,6 +1491,14 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco return r, nil } +// Returns either the Amount or nil if the number is zero +func amountOrNil(num *big.Int) *Amount { + if num.Cmp(big.NewInt(0)) == 0 { + return nil + } + return (*Amount)(num) +} + func (w *Worker) balanceHistoryHeightsFromTo(fromTimestamp, toTimestamp int64) (uint32, uint32, uint32, uint32) { fromUnix := uint32(0) toUnix := maxUint32 diff --git a/blockbook-api.ts b/blockbook-api.ts index bc6b6e43d3..bf0f9ad4fc 100644 --- a/blockbook-api.ts +++ b/blockbook-api.ts @@ -168,6 +168,8 @@ export interface Address { totalSent?: string; unconfirmedBalance: string; unconfirmedTxs: number; + unconfirmedSending?: string; + unconfirmedReceiving?: string; txs: number; addrTxCount?: number; nonTokenTxs?: number; From d328ed9e00e1d74b02748ca6dfb7b6ddf0b5a5e8 Mon Sep 17 00:00:00 2001 From: grdddj Date: Mon, 10 Feb 2025 14:21:19 +0100 Subject: [PATCH 472/530] docs: document public API structs --- api/types.go | 452 +++++++++++++++++----------------- bchain/types.go | 190 +++++++------- bchain/types_ethereum_type.go | 151 ++++++------ common/internalstate.go | 108 ++++---- server/ws_types.go | 180 ++++++++------ 5 files changed, 556 insertions(+), 525 deletions(-) diff --git a/api/types.go b/api/types.go index 84c98ae3eb..a126eb6710 100644 --- a/api/types.go +++ b/api/types.go @@ -42,8 +42,8 @@ var ErrUnsupportedXpub = errors.New("XPUB not supported") // APIError extends error by information if the error details should be returned to the end user type APIError struct { - Text string - Public bool + Text string `ts_doc:"Human-readable error message describing the issue."` + Public bool `ts_doc:"Whether the error message can safely be shown to the end user."` } func (e *APIError) Error() string { @@ -58,16 +58,16 @@ func NewAPIError(s string, public bool) error { } } -// Amount is datatype holding amounts +// Amount is a datatype holding amounts type Amount big.Int -// IsZeroBigInt if big int has zero value +// IsZeroBigInt checks if big int has zero value func IsZeroBigInt(b *big.Int) bool { return len(b.Bits()) == 0 } // Compare returns an integer comparing two Amounts. The result will be 0 if a == b, -1 if a < b, and +1 if a > b. -// Nil Amount is always less then non nil amount, two nil Amounts are equal +// Nil Amount is always less then non-nil amount, two nil Amounts are equal func (a *Amount) Compare(b *Amount) int { if b == nil { if a == nil { @@ -136,41 +136,41 @@ func (a *Amount) AsInt64() int64 { // Vin contains information about single transaction input type Vin struct { - Txid string `json:"txid,omitempty"` - Vout uint32 `json:"vout,omitempty"` - Sequence int64 `json:"sequence,omitempty"` - N int `json:"n"` - AddrDesc bchain.AddressDescriptor `json:"-"` - Addresses []string `json:"addresses,omitempty"` - IsAddress bool `json:"isAddress"` - IsOwn bool `json:"isOwn,omitempty"` - ValueSat *Amount `json:"value,omitempty"` - Hex string `json:"hex,omitempty"` - Asm string `json:"asm,omitempty"` - Coinbase string `json:"coinbase,omitempty"` + Txid string `json:"txid,omitempty" ts_doc:"ID/hash of the originating transaction (where the UTXO comes from)."` + Vout uint32 `json:"vout,omitempty" ts_doc:"Index of the output in the referenced transaction."` + Sequence int64 `json:"sequence,omitempty" ts_doc:"Sequence number for this input (e.g. 4294967293)."` + N int `json:"n" ts_doc:"Relative index of this input within the transaction."` + AddrDesc bchain.AddressDescriptor `json:"-" ts_doc:"Internal address descriptor for backend usage (not exposed via JSON)."` + Addresses []string `json:"addresses,omitempty" ts_doc:"List of addresses associated with this input."` + IsAddress bool `json:"isAddress" ts_doc:"Indicates if this input is from a known address."` + IsOwn bool `json:"isOwn,omitempty" ts_doc:"Indicates if this input belongs to the wallet in context."` + ValueSat *Amount `json:"value,omitempty" ts_doc:"Amount (in satoshi or base units) of the input."` + Hex string `json:"hex,omitempty" ts_doc:"Raw script hex data for this input."` + Asm string `json:"asm,omitempty" ts_doc:"Disassembled script for this input."` + Coinbase string `json:"coinbase,omitempty" ts_doc:"Data for coinbase inputs (when mining)."` } // Vout contains information about single transaction output type Vout struct { - ValueSat *Amount `json:"value,omitempty"` - N int `json:"n"` - Spent bool `json:"spent,omitempty"` - SpentTxID string `json:"spentTxId,omitempty"` - SpentIndex int `json:"spentIndex,omitempty"` - SpentHeight int `json:"spentHeight,omitempty"` - Hex string `json:"hex,omitempty"` - Asm string `json:"asm,omitempty"` - AddrDesc bchain.AddressDescriptor `json:"-"` - Addresses []string `json:"addresses"` - IsAddress bool `json:"isAddress"` - IsOwn bool `json:"isOwn,omitempty"` - Type string `json:"type,omitempty"` -} - -// MultiTokenValue contains values for contract with id and value (like ERC1155) + ValueSat *Amount `json:"value,omitempty" ts_doc:"Amount (in satoshi or base units) of the output."` + N int `json:"n" ts_doc:"Relative index of this output within the transaction."` + Spent bool `json:"spent,omitempty" ts_doc:"Indicates whether this output has been spent."` + SpentTxID string `json:"spentTxId,omitempty" ts_doc:"Transaction ID in which this output was spent."` + SpentIndex int `json:"spentIndex,omitempty" ts_doc:"Index of the input that spent this output."` + SpentHeight int `json:"spentHeight,omitempty" ts_doc:"Block height at which this output was spent."` + Hex string `json:"hex,omitempty" ts_doc:"Raw script hex data for this output - aka ScriptPubKey."` + Asm string `json:"asm,omitempty" ts_doc:"Disassembled script for this output."` + AddrDesc bchain.AddressDescriptor `json:"-" ts_doc:"Internal address descriptor for backend usage (not exposed via JSON)."` + Addresses []string `json:"addresses" ts_doc:"List of addresses associated with this output."` + IsAddress bool `json:"isAddress" ts_doc:"Indicates whether this output is owned by valid address."` + IsOwn bool `json:"isOwn,omitempty" ts_doc:"Indicates if this output belongs to the wallet in context."` + Type string `json:"type,omitempty" ts_doc:"Output script type (e.g., 'P2PKH', 'P2SH')."` +} + +// MultiTokenValue contains values for contracts with multiple token IDs type MultiTokenValue struct { - Id *Amount `json:"id,omitempty"` - Value *Amount `json:"value,omitempty"` + Id *Amount `json:"id,omitempty" ts_doc:"Token ID (for ERC1155)."` + Value *Amount `json:"value,omitempty" ts_doc:"Amount of that specific token ID."` } // Token contains info about tokens held by an address @@ -178,19 +178,19 @@ type Token struct { // Deprecated: Use Standard instead. Type bchain.TokenStandardName `json:"type" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'" ts_doc:"@deprecated: Use standard instead."` Standard bchain.TokenStandardName `json:"standard" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'"` - Name string `json:"name"` - Path string `json:"path,omitempty"` - Contract string `json:"contract,omitempty"` - Transfers int `json:"transfers"` - Symbol string `json:"symbol,omitempty"` - Decimals int `json:"decimals"` - BalanceSat *Amount `json:"balance,omitempty"` - BaseValue float64 `json:"baseValue,omitempty"` // value in the base currency (ETH for Ethereum) - SecondaryValue float64 `json:"secondaryValue,omitempty"` // value in secondary (fiat) currency, if specified - Ids []Amount `json:"ids,omitempty"` // multiple ERC721 tokens - MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` // multiple ERC1155 tokens - TotalReceivedSat *Amount `json:"totalReceived,omitempty"` - TotalSentSat *Amount `json:"totalSent,omitempty"` + Name string `json:"name" ts_doc:"Readable name of the token."` + Path string `json:"path,omitempty" ts_doc:"Derivation path if this token is derived from an XPUB-based address."` + Contract string `json:"contract,omitempty" ts_doc:"Contract address on-chain."` + Transfers int `json:"transfers" ts_doc:"Total number of token transfers for this address."` + Symbol string `json:"symbol,omitempty" ts_doc:"Symbol for the token (e.g., 'ETH', 'USDT')."` + Decimals int `json:"decimals,omitempty" ts_doc:"Number of decimals for this token."` + BalanceSat *Amount `json:"balance,omitempty" ts_doc:"Current token balance (in minimal base units)."` + BaseValue float64 `json:"baseValue,omitempty" ts_doc:"Value in the base currency (e.g. ETH for ERC20 tokens)."` + SecondaryValue float64 `json:"secondaryValue,omitempty" ts_doc:"Value in a secondary currency (e.g. fiat), if available."` + Ids []Amount `json:"ids,omitempty" ts_doc:"List of token IDs (for ERC721, each ID is a unique collectible)."` + MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty" ts_doc:"Multiple ERC1155 token balances (id + value)."` + TotalReceivedSat *Amount `json:"totalReceived,omitempty" ts_doc:"Total amount of tokens received."` + TotalSentSat *Amount `json:"totalSent,omitempty" ts_doc:"Total amount of tokens sent."` ContractIndex string `json:"-"` } @@ -226,90 +226,94 @@ type TokenTransfer struct { // Deprecated: Use Standard instead. Type bchain.TokenStandardName `json:"type" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'" ts_doc:"@deprecated: Use standard instead."` Standard bchain.TokenStandardName `json:"standard" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'"` - From string `json:"from"` - To string `json:"to"` - Contract string `json:"contract"` - Name string `json:"name,omitempty"` - Symbol string `json:"symbol,omitempty"` - Decimals int `json:"decimals"` - Value *Amount `json:"value,omitempty"` - MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` + From string `json:"from" ts_doc:"Source address of the token transfer."` + To string `json:"to" ts_doc:"Destination address of the token transfer."` + Contract string `json:"contract" ts_doc:"Contract address of the token."` + Name string `json:"name,omitempty" ts_doc:"Token name."` + Symbol string `json:"symbol,omitempty" ts_doc:"Token symbol."` + Decimals int `json:"decimals,omitempty" ts_doc:"Number of decimals for this token (if applicable)."` + Value *Amount `json:"value,omitempty" ts_doc:"Amount (in base units) of tokens transferred."` + MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty" ts_doc:"List of multiple ID-value pairs for ERC1155 transfers."` } +// EthereumInternalTransfer represents internal transaction data in Ethereum-like blockchains type EthereumInternalTransfer struct { - Type bchain.EthereumInternalTransactionType `json:"type"` - From string `json:"from"` - To string `json:"to"` - Value *Amount `json:"value"` + Type bchain.EthereumInternalTransactionType `json:"type" ts_doc:"Type of internal transfer (CALL, CREATE, etc.)."` + From string `json:"from" ts_doc:"Address from which the transfer originated."` + To string `json:"to" ts_doc:"Address to which the transfer was sent."` + Value *Amount `json:"value" ts_doc:"Value transferred internally (in Wei or base units)."` } -// EthereumSpecific contains ethereum specific transaction data +// EthereumSpecific contains ethereum-specific transaction data type EthereumSpecific struct { - Type bchain.EthereumInternalTransactionType `json:"type,omitempty"` - CreatedContract string `json:"createdContract,omitempty"` - Status eth.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending - Error string `json:"error,omitempty"` - Nonce uint64 `json:"nonce"` - GasLimit *big.Int `json:"gasLimit"` - GasUsed *big.Int `json:"gasUsed,omitempty"` - GasPrice *Amount `json:"gasPrice,omitempty"` + Type bchain.EthereumInternalTransactionType `json:"type,omitempty" ts_doc:"High-level type of the Ethereum tx (e.g., 'call', 'create')."` + CreatedContract string `json:"createdContract,omitempty" ts_doc:"Address of contract created by this transaction, if any."` + Status eth.TxStatus `json:"status" ts_doc:"Execution status of the transaction (1: success, 0: fail, -1: pending)."` + Error string `json:"error,omitempty" ts_doc:"Error encountered during execution, if any."` + Nonce uint64 `json:"nonce" ts_doc:"Transaction nonce (sequential number from the sender)."` + GasLimit *big.Int `json:"gasLimit" ts_doc:"Maximum gas allowed by the sender for this transaction."` + GasUsed *big.Int `json:"gasUsed,omitempty" ts_doc:"Actual gas consumed by the transaction execution."` + GasPrice *Amount `json:"gasPrice,omitempty" ts_doc:"Price (in Wei or base units) per gas unit."` MaxPriorityFeePerGas *Amount `json:"maxPriorityFeePerGas,omitempty"` MaxFeePerGas *Amount `json:"maxFeePerGas,omitempty"` BaseFeePerGas *Amount `json:"baseFeePerGas,omitempty"` - L1Fee *big.Int `json:"l1Fee,omitempty"` - L1FeeScalar string `json:"l1FeeScalar,omitempty"` - L1GasPrice *Amount `json:"l1GasPrice,omitempty"` - L1GasUsed *big.Int `json:"l1GasUsed,omitempty"` - Data string `json:"data,omitempty"` - ParsedData *bchain.EthereumParsedInputData `json:"parsedData,omitempty"` - InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"` + L1Fee *big.Int `json:"l1Fee,omitempty" ts_doc:"Fee used for L1 part in rollups (e.g. Optimism)."` + L1FeeScalar string `json:"l1FeeScalar,omitempty" ts_doc:"Scaling factor for L1 fees in certain Layer 2 solutions."` + L1GasPrice *Amount `json:"l1GasPrice,omitempty" ts_doc:"Gas price for L1 component, if applicable."` + L1GasUsed *big.Int `json:"l1GasUsed,omitempty" ts_doc:"Amount of gas used in L1 for this tx, if applicable."` + Data string `json:"data,omitempty" ts_doc:"Hex-encoded input data for the transaction."` + ParsedData *bchain.EthereumParsedInputData `json:"parsedData,omitempty" ts_doc:"Decoded transaction data (function name, params, etc.)."` + InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty" ts_doc:"List of internal (sub-call) transfers."` } +// AddressAlias holds a specialized alias for an address type AddressAlias struct { - Type string - Alias string + Type string `ts_doc:"Type of alias, e.g., user-defined name or contract name."` + Alias string `ts_doc:"Alias string for the address."` } + +// AddressAliasesMap is a map of address strings to their alias definitions type AddressAliasesMap map[string]AddressAlias // Tx holds information about a transaction type Tx struct { - Txid string `json:"txid"` - Version int32 `json:"version,omitempty"` - Locktime uint32 `json:"lockTime,omitempty"` - Vin []Vin `json:"vin"` - Vout []Vout `json:"vout"` - Blockhash string `json:"blockHash,omitempty"` - Blockheight int `json:"blockHeight"` - Confirmations uint32 `json:"confirmations"` - ConfirmationETABlocks uint32 `json:"confirmationETABlocks,omitempty"` - ConfirmationETASeconds int64 `json:"confirmationETASeconds,omitempty"` - Blocktime int64 `json:"blockTime"` - Size int `json:"size,omitempty"` - VSize int `json:"vsize,omitempty"` - ValueOutSat *Amount `json:"value"` - ValueInSat *Amount `json:"valueIn,omitempty"` - FeesSat *Amount `json:"fees,omitempty"` - Hex string `json:"hex,omitempty"` - Rbf bool `json:"rbf,omitempty"` - CoinSpecificData json.RawMessage `json:"coinSpecificData,omitempty" ts_type:"any"` - TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty"` - EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty"` - AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` + Txid string `json:"txid" ts_doc:"Transaction ID (hash)."` + Version int32 `json:"version,omitempty" ts_doc:"Version of the transaction (if applicable)."` + Locktime uint32 `json:"lockTime,omitempty" ts_doc:"Locktime indicating earliest time/height transaction can be mined."` + Vin []Vin `json:"vin" ts_doc:"Array of inputs for this transaction."` + Vout []Vout `json:"vout" ts_doc:"Array of outputs for this transaction."` + Blockhash string `json:"blockHash,omitempty" ts_doc:"Hash of the block containing this transaction."` + Blockheight int `json:"blockHeight" ts_doc:"Block height in which this transaction was included."` + Confirmations uint32 `json:"confirmations" ts_doc:"Number of confirmations (blocks mined after this tx's block)."` + ConfirmationETABlocks uint32 `json:"confirmationETABlocks,omitempty" ts_doc:"Estimated blocks remaining until confirmation (if unconfirmed)."` + ConfirmationETASeconds int64 `json:"confirmationETASeconds,omitempty" ts_doc:"Estimated seconds remaining until confirmation (if unconfirmed)."` + Blocktime int64 `json:"blockTime" ts_doc:"Unix timestamp of the block in which this transaction was included. 0 if unconfirmed."` + Size int `json:"size,omitempty" ts_doc:"Transaction size in bytes."` + VSize int `json:"vsize,omitempty" ts_doc:"Virtual size in bytes, for SegWit-enabled chains."` + ValueOutSat *Amount `json:"value" ts_doc:"Total value of all outputs (in satoshi or base units)."` + ValueInSat *Amount `json:"valueIn,omitempty" ts_doc:"Total value of all inputs (in satoshi or base units)."` + FeesSat *Amount `json:"fees,omitempty" ts_doc:"Transaction fee (inputs - outputs)."` + Hex string `json:"hex,omitempty" ts_doc:"Raw hex-encoded transaction data."` + Rbf bool `json:"rbf,omitempty" ts_doc:"Indicates if this transaction is replace-by-fee (RBF) enabled."` + CoinSpecificData json.RawMessage `json:"coinSpecificData,omitempty" ts_type:"any" ts_doc:"Blockchain-specific extended data."` + TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty" ts_doc:"List of token transfers that occurred in this transaction."` + EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty" ts_doc:"Ethereum-like blockchain specific data (if applicable)."` + AddressAliases AddressAliasesMap `json:"addressAliases,omitempty" ts_doc:"Aliases for addresses involved in this transaction."` } // FeeStats contains detailed block fee statistics type FeeStats struct { - TxCount int `json:"txCount"` - TotalFeesSat *Amount `json:"totalFeesSat"` - AverageFeePerKb int64 `json:"averageFeePerKb"` - DecilesFeePerKb [11]int64 `json:"decilesFeePerKb"` + TxCount int `json:"txCount" ts_doc:"Number of transactions in the given block."` + TotalFeesSat *Amount `json:"totalFeesSat" ts_doc:"Sum of all fees in satoshi or base units."` + AverageFeePerKb int64 `json:"averageFeePerKb" ts_doc:"Average fee per kilobyte in satoshi or base units."` + DecilesFeePerKb [11]int64 `json:"decilesFeePerKb" ts_doc:"Fee distribution deciles (0%..100%) in satoshi or base units per kB."` } // Paging contains information about paging for address, blocks and block type Paging struct { - Page int `json:"page,omitempty"` - TotalPages int `json:"totalPages,omitempty"` - ItemsOnPage int `json:"itemsOnPage,omitempty"` + Page int `json:"page,omitempty" ts_doc:"Current page index."` + TotalPages int `json:"totalPages,omitempty" ts_doc:"Total number of pages available."` + ItemsOnPage int `json:"itemsOnPage,omitempty" ts_doc:"Number of items returned on this page."` } // TokensToReturn specifies what tokens are returned by GetAddress and GetXpubAddress @@ -335,74 +339,74 @@ const ( // AddressFilter is used to filter data returned from GetAddress api method type AddressFilter struct { - Vout int - Contract string - FromHeight uint32 - ToHeight uint32 - TokensToReturn TokensToReturn + Vout int `ts_doc:"Specifies which output index we are interested in filtering (or use the special constants)."` + Contract string `ts_doc:"Contract address to filter by, if applicable."` + FromHeight uint32 `ts_doc:"Starting block height for filtering transactions."` + ToHeight uint32 `ts_doc:"Ending block height for filtering transactions."` + TokensToReturn TokensToReturn `ts_doc:"Which tokens to include in the result set."` // OnlyConfirmed set to true will ignore mempool transactions; mempool is also ignored if FromHeight/ToHeight filter is specified - OnlyConfirmed bool + OnlyConfirmed bool `ts_doc:"If true, ignores mempool (unconfirmed) transactions."` } // StakingPool holds data about address participation in a staking pool contract type StakingPool struct { - Contract string `json:"contract"` - Name string `json:"name"` - PendingBalance *Amount `json:"pendingBalance"` - PendingDepositedBalance *Amount `json:"pendingDepositedBalance"` - DepositedBalance *Amount `json:"depositedBalance"` - WithdrawTotalAmount *Amount `json:"withdrawTotalAmount"` - ClaimableAmount *Amount `json:"claimableAmount"` - RestakedReward *Amount `json:"restakedReward"` - AutocompoundBalance *Amount `json:"autocompoundBalance"` -} - -// Address holds information about address and its transactions + Contract string `json:"contract" ts_doc:"Staking pool contract address on-chain."` + Name string `json:"name" ts_doc:"Name of the staking pool contract."` + PendingBalance *Amount `json:"pendingBalance" ts_doc:"Balance pending deposit or withdrawal, if any."` + PendingDepositedBalance *Amount `json:"pendingDepositedBalance" ts_doc:"Any pending deposit that is not yet finalized."` + DepositedBalance *Amount `json:"depositedBalance" ts_doc:"Currently deposited/staked balance."` + WithdrawTotalAmount *Amount `json:"withdrawTotalAmount" ts_doc:"Total amount withdrawn from this pool by the address."` + ClaimableAmount *Amount `json:"claimableAmount" ts_doc:"Rewards or principal currently claimable by the address."` + RestakedReward *Amount `json:"restakedReward" ts_doc:"Total rewards that have been restaked automatically."` + AutocompoundBalance *Amount `json:"autocompoundBalance" ts_doc:"Any balance automatically reinvested into the pool."` +} + +// Address holds information about an address and its transactions type Address struct { Paging - AddrStr string `json:"address"` - BalanceSat *Amount `json:"balance"` - TotalReceivedSat *Amount `json:"totalReceived,omitempty"` - TotalSentSat *Amount `json:"totalSent,omitempty"` - UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance"` - UnconfirmedTxs int `json:"unconfirmedTxs"` - UnconfirmedSending *Amount `json:"unconfirmedSending,omitempty"` - UnconfirmedReceiving *Amount `json:"unconfirmedReceiving,omitempty"` - Txs int `json:"txs"` - AddrTxCount int `json:"addrTxCount,omitempty"` - NonTokenTxs int `json:"nonTokenTxs,omitempty"` - InternalTxs int `json:"internalTxs,omitempty"` - Transactions []*Tx `json:"transactions,omitempty"` - Txids []string `json:"txids,omitempty"` - Nonce string `json:"nonce,omitempty"` - UsedTokens int `json:"usedTokens,omitempty"` - Tokens Tokens `json:"tokens,omitempty"` - SecondaryValue float64 `json:"secondaryValue,omitempty"` // address value in secondary currency - TokensBaseValue float64 `json:"tokensBaseValue,omitempty"` - TokensSecondaryValue float64 `json:"tokensSecondaryValue,omitempty"` - TotalBaseValue float64 `json:"totalBaseValue,omitempty"` // value including tokens in base currency - TotalSecondaryValue float64 `json:"totalSecondaryValue,omitempty"` // value including tokens in secondary currency - ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"` + AddrStr string `json:"address" ts_doc:"The address string in standard format."` + BalanceSat *Amount `json:"balance" ts_doc:"Current confirmed balance (in satoshi or base units)."` + TotalReceivedSat *Amount `json:"totalReceived,omitempty" ts_doc:"Total amount ever received by this address."` + TotalSentSat *Amount `json:"totalSent,omitempty" ts_doc:"Total amount ever sent by this address."` + UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance" ts_doc:"Unconfirmed balance for this address."` + UnconfirmedTxs int `json:"unconfirmedTxs" ts_doc:"Number of unconfirmed transactions for this address."` + UnconfirmedSending *Amount `json:"unconfirmedSending,omitempty" ts_doc:"Unconfirmed outgoing balance for this address."` + UnconfirmedReceiving *Amount `json:"unconfirmedReceiving,omitempty" ts_doc:"Unconfirmed incoming balance for this address."` + Txs int `json:"txs" ts_doc:"Number of transactions for this address (including confirmed)."` + AddrTxCount int `json:"addrTxCount,omitempty" ts_doc:"Historical total count of transactions, if known."` + NonTokenTxs int `json:"nonTokenTxs,omitempty" ts_doc:"Number of transactions not involving tokens (pure coin transfers)."` + InternalTxs int `json:"internalTxs,omitempty" ts_doc:"Number of internal transactions (e.g., Ethereum calls)."` + Transactions []*Tx `json:"transactions,omitempty" ts_doc:"List of transaction details (if requested)."` + Txids []string `json:"txids,omitempty" ts_doc:"List of transaction IDs (if detailed data is not requested)."` + Nonce string `json:"nonce,omitempty" ts_doc:"Current transaction nonce for Ethereum-like addresses."` + UsedTokens int `json:"usedTokens,omitempty" ts_doc:"Number of tokens with any historical usage at this address."` + Tokens Tokens `json:"tokens,omitempty" ts_doc:"List of tokens associated with this address."` + SecondaryValue float64 `json:"secondaryValue,omitempty" ts_doc:"Total value of the address in secondary currency (e.g. fiat)."` + TokensBaseValue float64 `json:"tokensBaseValue,omitempty" ts_doc:"Sum of token values in base currency."` + TokensSecondaryValue float64 `json:"tokensSecondaryValue,omitempty" ts_doc:"Sum of token values in secondary currency (fiat)."` + TotalBaseValue float64 `json:"totalBaseValue,omitempty" ts_doc:"Address's entire value in base currency, including tokens."` + TotalSecondaryValue float64 `json:"totalSecondaryValue,omitempty" ts_doc:"Address's entire value in secondary currency, including tokens."` + ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty" ts_doc:"Extra info if the address is a contract (ABI, type)."` // Deprecated: replaced by ContractInfo Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty" ts_doc:"@deprecated: replaced by contractInfo"` - AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` - StakingPools []StakingPool `json:"stakingPools,omitempty"` + AddressAliases AddressAliasesMap `json:"addressAliases,omitempty" ts_doc:"Aliases assigned to this address."` + StakingPools []StakingPool `json:"stakingPools,omitempty" ts_doc:"List of staking pool data if address interacts with staking."` // helpers for explorer - Filter string `json:"-"` - XPubAddresses map[string]struct{} `json:"-"` + Filter string `json:"-" ts_doc:"Filter used internally for data retrieval."` + XPubAddresses map[string]struct{} `json:"-" ts_doc:"Set of derived XPUB addresses (internal usage)."` } // Utxo is one unspent transaction output type Utxo struct { - Txid string `json:"txid"` - Vout int32 `json:"vout"` - AmountSat *Amount `json:"value"` - Height int `json:"height,omitempty"` - Confirmations int `json:"confirmations"` - Address string `json:"address,omitempty"` - Path string `json:"path,omitempty"` - Locktime uint32 `json:"lockTime,omitempty"` - Coinbase bool `json:"coinbase,omitempty"` + Txid string `json:"txid" ts_doc:"Transaction ID in which this UTXO was created."` + Vout int32 `json:"vout" ts_doc:"Index of the output in that transaction."` + AmountSat *Amount `json:"value" ts_doc:"Value of this UTXO (in satoshi or base units)."` + Height int `json:"height,omitempty" ts_doc:"Block height in which the UTXO was confirmed."` + Confirmations int `json:"confirmations" ts_doc:"Number of confirmations for this UTXO."` + Address string `json:"address,omitempty" ts_doc:"Address to which this UTXO belongs."` + Path string `json:"path,omitempty" ts_doc:"Derivation path for XPUB-based wallets, if applicable."` + Locktime uint32 `json:"lockTime,omitempty" ts_doc:"If non-zero, locktime required before spending this UTXO."` + Coinbase bool `json:"coinbase,omitempty" ts_doc:"Indicates if this UTXO originated from a coinbase transaction."` } // Utxos is array of Utxo @@ -425,13 +429,13 @@ func (a Utxos) Less(i, j int) bool { // BalanceHistory contains info about one point in time of balance history type BalanceHistory struct { - Time uint32 `json:"time"` - Txs uint32 `json:"txs"` - ReceivedSat *Amount `json:"received"` - SentSat *Amount `json:"sent"` - SentToSelfSat *Amount `json:"sentToSelf"` - FiatRates map[string]float32 `json:"rates,omitempty"` - Txid string `json:"txid,omitempty"` + Time uint32 `json:"time" ts_doc:"Unix timestamp for this point in the balance history."` + Txs uint32 `json:"txs" ts_doc:"Number of transactions in this interval."` + ReceivedSat *Amount `json:"received" ts_doc:"Amount received in this interval (in satoshi or base units)."` + SentSat *Amount `json:"sent" ts_doc:"Amount sent in this interval (in satoshi or base units)."` + SentToSelfSat *Amount `json:"sentToSelf" ts_doc:"Amount sent to the same address (self-transfer)."` + FiatRates map[string]float32 `json:"rates,omitempty" ts_doc:"Exchange rates at this point in time, if available."` + Txid string `json:"txid,omitempty" ts_doc:"Transaction ID if the time corresponds to a specific tx."` } // BalanceHistories is array of BalanceHistory @@ -493,105 +497,105 @@ func (a BalanceHistories) SortAndAggregate(groupByTime uint32) BalanceHistories // Blocks is list of blocks with paging information type Blocks struct { Paging - Blocks []db.BlockInfo `json:"blocks"` + Blocks []db.BlockInfo `json:"blocks" ts_doc:"List of blocks."` } // BlockInfo contains extended block header data and a list of block txids type BlockInfo struct { - Hash string `json:"hash"` - Prev string `json:"previousBlockHash,omitempty"` - Next string `json:"nextBlockHash,omitempty"` - Height uint32 `json:"height"` - Confirmations int `json:"confirmations"` - Size int `json:"size"` - Time int64 `json:"time,omitempty"` - Version common.JSONNumber `json:"version"` - MerkleRoot string `json:"merkleRoot"` - Nonce string `json:"nonce"` - Bits string `json:"bits"` - Difficulty string `json:"difficulty"` - Txids []string `json:"tx,omitempty"` + Hash string `json:"hash" ts_doc:"Block hash."` + Prev string `json:"previousBlockHash,omitempty" ts_doc:"Hash of the previous block in the chain."` + Next string `json:"nextBlockHash,omitempty" ts_doc:"Hash of the next block, if known."` + Height uint32 `json:"height" ts_doc:"Block height (0-based index in the chain)."` + Confirmations int `json:"confirmations" ts_doc:"Number of confirmations of this block (distance from best chain tip)."` + Size int `json:"size" ts_doc:"Size of the block in bytes."` + Time int64 `json:"time,omitempty" ts_doc:"Timestamp of when this block was mined."` + Version common.JSONNumber `json:"version" ts_doc:"Block version (chain-specific meaning)."` + MerkleRoot string `json:"merkleRoot" ts_doc:"Merkle root of the block's transactions."` + Nonce string `json:"nonce" ts_doc:"Nonce used in the mining process."` + Bits string `json:"bits" ts_doc:"Compact representation of the target threshold."` + Difficulty string `json:"difficulty" ts_doc:"Difficulty target for mining this block."` + Txids []string `json:"tx,omitempty" ts_doc:"List of transaction IDs included in this block."` } // Block contains information about block type Block struct { Paging BlockInfo - TxCount int `json:"txCount"` - Transactions []*Tx `json:"txs,omitempty"` - AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` + TxCount int `json:"txCount" ts_doc:"Total count of transactions in this block."` + Transactions []*Tx `json:"txs,omitempty" ts_doc:"List of full transaction details (if requested)."` + AddressAliases AddressAliasesMap `json:"addressAliases,omitempty" ts_doc:"Optional aliases for addresses found in this block."` } // BlockRaw contains raw block in hex type BlockRaw struct { - Hex string `json:"hex"` + Hex string `json:"hex" ts_doc:"Hex-encoded block data."` } // BlockbookInfo contains information about the running blockbook instance type BlockbookInfo struct { - Coin string `json:"coin"` - Network string `json:"network"` - Host string `json:"host"` - Version string `json:"version"` - GitCommit string `json:"gitCommit"` - BuildTime string `json:"buildTime"` - SyncMode bool `json:"syncMode"` - InitialSync bool `json:"initialSync"` - InSync bool `json:"inSync"` - BestHeight uint32 `json:"bestHeight"` - LastBlockTime time.Time `json:"lastBlockTime"` - InSyncMempool bool `json:"inSyncMempool"` - LastMempoolTime time.Time `json:"lastMempoolTime"` - MempoolSize int `json:"mempoolSize"` - Decimals int `json:"decimals"` - DbSize int64 `json:"dbSize"` - HasFiatRates bool `json:"hasFiatRates,omitempty"` - HasTokenFiatRates bool `json:"hasTokenFiatRates,omitempty"` - CurrentFiatRatesTime *time.Time `json:"currentFiatRatesTime,omitempty"` - HistoricalFiatRatesTime *time.Time `json:"historicalFiatRatesTime,omitempty"` - HistoricalTokenFiatRatesTime *time.Time `json:"historicalTokenFiatRatesTime,omitempty"` - SupportedStakingPools []string `json:"supportedStakingPools,omitempty"` - DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty"` - DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty"` - About string `json:"about"` + Coin string `json:"coin" ts_doc:"Coin name, e.g. 'Bitcoin'."` + Network string `json:"network" ts_doc:"Network shortcut, e.g. 'BTC'."` + Host string `json:"host" ts_doc:"Hostname of the blockbook instance, e.g. 'backend5'."` + Version string `json:"version" ts_doc:"Running blockbook version, e.g. '0.4.0'."` + GitCommit string `json:"gitCommit" ts_doc:"Git commit hash of the running blockbook, e.g. 'a0960c8e'."` + BuildTime string `json:"buildTime" ts_doc:"Build time of running blockbook, e.g. '2024-08-08T12:32:50+00:00'."` + SyncMode bool `json:"syncMode" ts_doc:"If true, blockbook is syncing from scratch or in a special sync mode."` + InitialSync bool `json:"initialSync" ts_doc:"Indicates if blockbook is in its initial sync phase."` + InSync bool `json:"inSync" ts_doc:"Indicates if the backend is fully synced with the blockchain."` + BestHeight uint32 `json:"bestHeight" ts_doc:"Best (latest) block height according to this instance."` + LastBlockTime time.Time `json:"lastBlockTime" ts_doc:"Timestamp of the latest block in the chain."` + InSyncMempool bool `json:"inSyncMempool" ts_doc:"Indicates if mempool info is synced as well."` + LastMempoolTime time.Time `json:"lastMempoolTime" ts_doc:"Timestamp of the last mempool update."` + MempoolSize int `json:"mempoolSize" ts_doc:"Number of unconfirmed transactions in the mempool."` + Decimals int `json:"decimals" ts_doc:"Number of decimals for this coin's base unit."` + DbSize int64 `json:"dbSize" ts_doc:"Size of the underlying database in bytes."` + HasFiatRates bool `json:"hasFiatRates,omitempty" ts_doc:"Whether this instance provides fiat exchange rates."` + HasTokenFiatRates bool `json:"hasTokenFiatRates,omitempty" ts_doc:"Whether this instance provides fiat exchange rates for tokens."` + CurrentFiatRatesTime *time.Time `json:"currentFiatRatesTime,omitempty" ts_doc:"Timestamp of the latest fiat rates update."` + HistoricalFiatRatesTime *time.Time `json:"historicalFiatRatesTime,omitempty" ts_doc:"Timestamp of the latest historical fiat rates update."` + HistoricalTokenFiatRatesTime *time.Time `json:"historicalTokenFiatRatesTime,omitempty" ts_doc:"Timestamp of the latest historical token fiat rates update."` + SupportedStakingPools []string `json:"supportedStakingPools,omitempty" ts_doc:"List of contract addresses supported for staking."` + DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty" ts_doc:"Optional calculated DB size from columns."` + DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty" ts_doc:"List of columns/tables in the DB for internal state."` + About string `json:"about" ts_doc:"Additional human-readable info about this blockbook instance."` } // SystemInfo contains information about the running blockbook and backend instance type SystemInfo struct { - Blockbook *BlockbookInfo `json:"blockbook"` - Backend *common.BackendInfo `json:"backend"` + Blockbook *BlockbookInfo `json:"blockbook" ts_doc:"Blockbook instance information."` + Backend *common.BackendInfo `json:"backend" ts_doc:"Information about the connected backend node."` } // MempoolTxid contains information about a transaction in mempool type MempoolTxid struct { - Time int64 `json:"time"` - Txid string `json:"txid"` + Time int64 `json:"time" ts_doc:"Timestamp when the transaction was received in the mempool."` + Txid string `json:"txid" ts_doc:"Transaction hash for this mempool entry."` } // MempoolTxids contains a list of mempool txids with paging information type MempoolTxids struct { Paging - Mempool []MempoolTxid `json:"mempool"` - MempoolSize int `json:"mempoolSize"` + Mempool []MempoolTxid `json:"mempool" ts_doc:"List of transactions currently in the mempool."` + MempoolSize int `json:"mempoolSize" ts_doc:"Number of unconfirmed transactions in the mempool."` } // FiatTicker contains formatted CurrencyRatesTicker data type FiatTicker struct { - Timestamp int64 `json:"ts,omitempty"` - Rates map[string]float32 `json:"rates"` - Error string `json:"error,omitempty"` + Timestamp int64 `json:"ts,omitempty" ts_doc:"Unix timestamp for these fiat rates."` + Rates map[string]float32 `json:"rates" ts_doc:"Map of currency codes to their exchange rate."` + Error string `json:"error,omitempty" ts_doc:"Any error message encountered while fetching rates."` } // FiatTickers contains a formatted CurrencyRatesTicker list type FiatTickers struct { - Tickers []FiatTicker `json:"tickers"` + Tickers []FiatTicker `json:"tickers" ts_doc:"List of fiat tickers with timestamps and rates."` } // AvailableVsCurrencies contains formatted data about available versus currencies for exchange rates type AvailableVsCurrencies struct { - Timestamp int64 `json:"ts,omitempty"` - Tickers []string `json:"available_currencies"` - Error string `json:"error,omitempty"` + Timestamp int64 `json:"ts,omitempty" ts_doc:"Timestamp for the available currency list."` + Tickers []string `json:"available_currencies" ts_doc:"List of currency codes (e.g., USD, EUR) supported by the rates."` + Error string `json:"error,omitempty" ts_doc:"Error message, if any, when fetching the available currencies."` } // Eip1559Fee diff --git a/bchain/types.go b/bchain/types.go index 04c87e73d1..40e54d6e87 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -39,81 +39,81 @@ var ( // Outpoint is txid together with output (or input) index type Outpoint struct { - Txid string - Vout int32 + Txid string `ts_doc:"Transaction ID of the referenced outpoint."` + Vout int32 `ts_doc:"Index of the specific output in the transaction."` } // ScriptSig contains data about input script type ScriptSig struct { // Asm string `json:"asm"` - Hex string `json:"hex"` + Hex string `json:"hex" ts_doc:"Hex-encoded representation of the scriptSig."` } // Vin contains data about tx input type Vin struct { - Coinbase string `json:"coinbase"` - Txid string `json:"txid"` - Vout uint32 `json:"vout"` - ScriptSig ScriptSig `json:"scriptSig"` - Sequence uint32 `json:"sequence"` - Addresses []string `json:"addresses"` - Witness [][]byte `json:"-"` + Coinbase string `json:"coinbase" ts_doc:"Coinbase data if this is a coinbase input."` + Txid string `json:"txid" ts_doc:"Transaction ID of the input being spent."` + Vout uint32 `json:"vout" ts_doc:"Output index in the referenced transaction."` + ScriptSig ScriptSig `json:"scriptSig" ts_doc:"scriptSig object containing the spending script data."` + Sequence uint32 `json:"sequence" ts_doc:"Sequence number for the input."` + Addresses []string `json:"addresses" ts_doc:"Addresses derived from this input's script (if known)."` + Witness [][]byte `json:"-" ts_doc:"Witness data for SegWit inputs (not exposed via JSON)."` } // ScriptPubKey contains data about output script type ScriptPubKey struct { // Asm string `json:"asm"` - Hex string `json:"hex,omitempty"` + Hex string `json:"hex,omitempty" ts_doc:"Hex-encoded representation of the scriptPubKey."` // Type string `json:"type"` - Addresses []string `json:"addresses"` + Addresses []string `json:"addresses" ts_doc:"Addresses derived from this output's script (if known)."` } // Vout contains data about tx output type Vout struct { - ValueSat big.Int - JsonValue common.JSONNumber `json:"value"` - N uint32 `json:"n"` - ScriptPubKey ScriptPubKey `json:"scriptPubKey"` + ValueSat big.Int `ts_doc:"Amount (in satoshi or base unit) for this output."` + JsonValue common.JSONNumber `json:"value" ts_doc:"String-based amount for JSON usage."` + N uint32 `json:"n" ts_doc:"Index of this output in the transaction."` + ScriptPubKey ScriptPubKey `json:"scriptPubKey" ts_doc:"scriptPubKey object containing the output script data."` } // Tx is blockchain transaction // unnecessary fields are commented out to avoid overhead type Tx struct { - Hex string `json:"hex"` - Txid string `json:"txid"` - Version int32 `json:"version"` - LockTime uint32 `json:"locktime"` - VSize int64 `json:"vsize,omitempty"` - Vin []Vin `json:"vin"` - Vout []Vout `json:"vout"` - BlockHeight uint32 `json:"blockHeight,omitempty"` + Hex string `json:"hex" ts_doc:"Hex-encoded transaction data."` + Txid string `json:"txid" ts_doc:"Transaction ID (hash)."` + Version int32 `json:"version" ts_doc:"Transaction version number."` + LockTime uint32 `json:"locktime" ts_doc:"Locktime specifying earliest time/block a tx can be mined."` + VSize int64 `json:"vsize,omitempty" ts_doc:"Virtual size of the transaction (for SegWit-based networks)."` + Vin []Vin `json:"vin" ts_doc:"List of inputs."` + Vout []Vout `json:"vout" ts_doc:"List of outputs."` + BlockHeight uint32 `json:"blockHeight,omitempty" ts_doc:"Block height in which this transaction was included."` // BlockHash string `json:"blockhash,omitempty"` - Confirmations uint32 `json:"confirmations,omitempty"` - Time int64 `json:"time,omitempty"` - Blocktime int64 `json:"blocktime,omitempty"` - CoinSpecificData interface{} `json:"-"` + Confirmations uint32 `json:"confirmations,omitempty" ts_doc:"Number of confirmations the transaction has."` + Time int64 `json:"time,omitempty" ts_doc:"Timestamp when the transaction was broadcast or included in a block."` + Blocktime int64 `json:"blocktime,omitempty" ts_doc:"Timestamp of the block in which the transaction was mined."` + CoinSpecificData interface{} `json:"-" ts_doc:"Additional chain-specific data (not exposed via JSON)."` } -// MempoolVin contains data about tx input +// MempoolVin contains data about tx input specifically in mempool type MempoolVin struct { Vin - AddrDesc AddressDescriptor `json:"-"` - ValueSat big.Int + AddrDesc AddressDescriptor `json:"-" ts_doc:"Internal descriptor for the input address (not exposed)."` + ValueSat big.Int `ts_doc:"Amount (in satoshi or base unit) of the input."` } // MempoolTx is blockchain transaction in mempool // optimized for onNewTx notification type MempoolTx struct { - Hex string `json:"hex"` - Txid string `json:"txid"` - Version int32 `json:"version"` - LockTime uint32 `json:"locktime"` - VSize int64 `json:"vsize,omitempty"` - Vin []MempoolVin `json:"vin"` - Vout []Vout `json:"vout"` - Blocktime int64 `json:"blocktime,omitempty"` - TokenTransfers TokenTransfers `json:"-"` - CoinSpecificData interface{} `json:"-"` + Hex string `json:"hex" ts_doc:"Hex-encoded transaction data."` + Txid string `json:"txid" ts_doc:"Transaction ID (hash)."` + Version int32 `json:"version" ts_doc:"Transaction version number."` + LockTime uint32 `json:"locktime" ts_doc:"Locktime specifying earliest time/block a tx can be mined."` + VSize int64 `json:"vsize,omitempty" ts_doc:"Virtual size of the transaction (if applicable)."` + Vin []MempoolVin `json:"vin" ts_doc:"List of inputs in this mempool transaction."` + Vout []Vout `json:"vout" ts_doc:"List of outputs in this mempool transaction."` + Blocktime int64 `json:"blocktime,omitempty" ts_doc:"Timestamp for the block in which tx might eventually be mined, if known."` + TokenTransfers TokenTransfers `json:"-" ts_doc:"Token transfers discovered in this mempool transaction (not exposed by default)."` + CoinSpecificData interface{} `json:"-" ts_doc:"Additional chain-specific data (not exposed via JSON)."` } // TokenStandard - standard of token @@ -150,71 +150,71 @@ func (a TokenTransfers) Less(i, j int) bool { // Block is block header and list of transactions type Block struct { BlockHeader - Txs []Tx `json:"tx"` - CoinSpecificData interface{} `json:"-"` + Txs []Tx `json:"tx" ts_doc:"List of full transactions included in this block."` + CoinSpecificData interface{} `json:"-" ts_doc:"Additional chain-specific data (not exposed via JSON)."` } // BlockHeader contains limited data (as needed for indexing) from backend block header type BlockHeader struct { - Hash string `json:"hash"` - Prev string `json:"previousblockhash"` - Next string `json:"nextblockhash"` - Height uint32 `json:"height"` - Confirmations int `json:"confirmations"` - Size int `json:"size"` - Time int64 `json:"time,omitempty"` + Hash string `json:"hash" ts_doc:"Block hash."` + Prev string `json:"previousblockhash" ts_doc:"Hash of the previous block in the chain."` + Next string `json:"nextblockhash" ts_doc:"Hash of the next block, if known."` + Height uint32 `json:"height" ts_doc:"Block height (0-based index in the chain)."` + Confirmations int `json:"confirmations" ts_doc:"Number of confirmations (distance from best chain tip)."` + Size int `json:"size" ts_doc:"Block size in bytes."` + Time int64 `json:"time,omitempty" ts_doc:"Timestamp of when this block was mined."` } // BlockInfo contains extended block header data and a list of block txids type BlockInfo struct { BlockHeader - Version common.JSONNumber `json:"version"` - MerkleRoot string `json:"merkleroot"` - Nonce common.JSONNumber `json:"nonce"` - Bits string `json:"bits"` - Difficulty common.JSONNumber `json:"difficulty"` - Txids []string `json:"tx,omitempty"` + Version common.JSONNumber `json:"version" ts_doc:"Block version (chain-specific meaning)."` + MerkleRoot string `json:"merkleroot" ts_doc:"Merkle root of the block's transactions."` + Nonce common.JSONNumber `json:"nonce" ts_doc:"Nonce used in the mining process."` + Bits string `json:"bits" ts_doc:"Compact representation of the target threshold."` + Difficulty common.JSONNumber `json:"difficulty" ts_doc:"Difficulty target for mining this block."` + Txids []string `json:"tx,omitempty" ts_doc:"List of transaction IDs included in this block."` } // MempoolEntry is used to get data about mempool entry type MempoolEntry struct { - Size uint32 `json:"size"` - FeeSat big.Int - Fee common.JSONNumber `json:"fee"` - ModifiedFeeSat big.Int - ModifiedFee common.JSONNumber `json:"modifiedfee"` - Time uint64 `json:"time"` - Height uint32 `json:"height"` - DescendantCount uint32 `json:"descendantcount"` - DescendantSize uint32 `json:"descendantsize"` - DescendantFees uint32 `json:"descendantfees"` - AncestorCount uint32 `json:"ancestorcount"` - AncestorSize uint32 `json:"ancestorsize"` - AncestorFees uint32 `json:"ancestorfees"` - Depends []string `json:"depends"` + Size uint32 `json:"size" ts_doc:"Size of the transaction in bytes, as stored in mempool."` + FeeSat big.Int `ts_doc:"Transaction fee in satoshi/base units."` + Fee common.JSONNumber `json:"fee" ts_doc:"String-based fee for JSON usage."` + ModifiedFeeSat big.Int `ts_doc:"Modified fee in satoshi/base units after priority adjustments."` + ModifiedFee common.JSONNumber `json:"modifiedfee" ts_doc:"String-based modified fee for JSON usage."` + Time uint64 `json:"time" ts_doc:"Unix timestamp when the tx entered the mempool."` + Height uint32 `json:"height" ts_doc:"Block height when the tx entered the mempool."` + DescendantCount uint32 `json:"descendantcount" ts_doc:"Number of descendant transactions in mempool."` + DescendantSize uint32 `json:"descendantsize" ts_doc:"Total size of all descendant transactions in bytes."` + DescendantFees uint32 `json:"descendantfees" ts_doc:"Combined fees of all descendant transactions."` + AncestorCount uint32 `json:"ancestorcount" ts_doc:"Number of ancestor transactions in mempool."` + AncestorSize uint32 `json:"ancestorsize" ts_doc:"Total size of all ancestor transactions in bytes."` + AncestorFees uint32 `json:"ancestorfees" ts_doc:"Combined fees of all ancestor transactions."` + Depends []string `json:"depends" ts_doc:"List of txids this transaction depends on."` } // ChainInfo is used to get information about blockchain type ChainInfo struct { - Chain string `json:"chain"` - Blocks int `json:"blocks"` - Headers int `json:"headers"` - Bestblockhash string `json:"bestblockhash"` - Difficulty string `json:"difficulty"` - SizeOnDisk int64 `json:"size_on_disk"` - Version string `json:"version"` - Subversion string `json:"subversion"` - ProtocolVersion string `json:"protocolversion"` - Timeoffset float64 `json:"timeoffset"` - Warnings string `json:"warnings"` - ConsensusVersion string `json:"consensus_version,omitempty"` - Consensus interface{} `json:"consensus,omitempty"` + Chain string `json:"chain" ts_doc:"Name of the chain (e.g. 'main')."` + Blocks int `json:"blocks" ts_doc:"Number of fully verified blocks in the chain."` + Headers int `json:"headers" ts_doc:"Number of block headers in the chain (can be ahead of full blocks)."` + Bestblockhash string `json:"bestblockhash" ts_doc:"Hash of the best (latest) block."` + Difficulty string `json:"difficulty" ts_doc:"Current difficulty of the network."` + SizeOnDisk int64 `json:"size_on_disk" ts_doc:"Size of the blockchain data on disk in bytes."` + Version string `json:"version" ts_doc:"Version of the blockchain backend."` + Subversion string `json:"subversion" ts_doc:"Subversion string of the blockchain backend."` + ProtocolVersion string `json:"protocolversion" ts_doc:"Protocol version for this chain node."` + Timeoffset float64 `json:"timeoffset" ts_doc:"Time offset (in seconds) reported by the node."` + Warnings string `json:"warnings" ts_doc:"Any warnings generated by the node regarding the chain state."` + ConsensusVersion string `json:"consensus_version,omitempty" ts_doc:"Version of the chain's consensus protocol, if available."` + Consensus interface{} `json:"consensus,omitempty" ts_doc:"Additional consensus details, structure depends on chain."` } // RPCError defines rpc error returned by backend type RPCError struct { - Code int `json:"code"` - Message string `json:"message"` + Code int `json:"code" ts_doc:"Error code returned by the backend RPC."` + Message string `json:"message" ts_doc:"Human-readable error message."` } func (e *RPCError) Error() string { @@ -245,8 +245,8 @@ func AddressDescriptorFromString(s string) (AddressDescriptor, error) { // MempoolTxidEntry contains mempool txid with first seen time type MempoolTxidEntry struct { - Txid string - Time uint32 + Txid string `ts_doc:"Transaction ID (hash) of the mempool entry."` + Time uint32 `ts_doc:"Unix timestamp when the transaction was first seen in the mempool."` } // ScriptType - type of output script parsed from xpub (descriptor) @@ -263,12 +263,12 @@ const ( // XpubDescriptor contains parsed data from xpub descriptor type XpubDescriptor struct { - XpubDescriptor string // The whole descriptor - Xpub string // Xpub part of the descriptor - Type ScriptType - Bip string - ChangeIndexes []uint32 - ExtKey interface{} // extended key parsed from xpub, usually of type *hdkeychain.ExtendedKey + XpubDescriptor string `ts_doc:"Full descriptor string including xpub and script type."` + Xpub string `ts_doc:"The xpub part itself extracted from the descriptor."` + Type ScriptType `ts_doc:"Parsed script type (P2PKH, P2WPKH, etc.)."` + Bip string `ts_doc:"BIP standard (e.g. BIP44) inferred from the descriptor."` + ChangeIndexes []uint32 `ts_doc:"Indexes designated as change addresses."` + ExtKey interface{} `ts_doc:"Extended key object parsed from xpub (implementation-specific)."` } // MempoolTxidEntries is array of MempoolTxidEntry @@ -277,8 +277,8 @@ type MempoolTxidEntries []MempoolTxidEntry // MempoolTxidFilterEntries is a map of txids to mempool golomb filters // Also contains a flag whether constant zeroed key was used when calculating the filters type MempoolTxidFilterEntries struct { - Entries map[string]string `json:"entries,omitempty"` - UsedZeroedKey bool `json:"usedZeroedKey,omitempty"` + Entries map[string]string `json:"entries,omitempty" ts_doc:"Map of txid to filter data (hex-encoded)."` + UsedZeroedKey bool `json:"usedZeroedKey,omitempty" ts_doc:"Indicates if a zeroed key was used in filter calculation."` } // OnNewBlockFunc is used to send notification about a new block diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index f29602ebe8..b93632ec45 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -8,35 +8,35 @@ import ( // EthereumInternalTransfer contains data about internal transfer type EthereumInternalTransfer struct { - Type EthereumInternalTransactionType `json:"type"` - From string `json:"from"` - To string `json:"to"` - Value big.Int `json:"value"` + Type EthereumInternalTransactionType `json:"type" ts_doc:"The type of internal transaction (CALL, CREATE, SELFDESTRUCT)."` + From string `json:"from" ts_doc:"Sender address of this internal transfer."` + To string `json:"to" ts_doc:"Recipient address of this internal transfer."` + Value big.Int `json:"value" ts_doc:"Amount (in Wei) transferred internally."` } -// FourByteSignature contains data about about a contract function signature +// FourByteSignature contains data about a contract function signature type FourByteSignature struct { // stored in DB - Name string - Parameters []string + Name string `ts_doc:"Original function name as stored in the database."` + Parameters []string `ts_doc:"Raw parameter type definitions (e.g. ['uint256','address'])."` // processed from DB data and stored only in cache - DecamelName string - Function string - ParsedParameters []abi.Type + DecamelName string `ts_doc:"A decamelized version of the function name for readability."` + Function string `ts_doc:"Reconstructed function definition string (e.g. 'transfer(address,uint256)')."` + ParsedParameters []abi.Type `ts_doc:"ABI-parsed parameter types (cached for efficiency)."` } // EthereumParsedInputParam contains data about a contract function parameter type EthereumParsedInputParam struct { - Type string `json:"type"` - Values []string `json:"values,omitempty"` + Type string `json:"type" ts_doc:"Parameter type (e.g. 'uint256')."` + Values []string `json:"values,omitempty" ts_doc:"List of stringified parameter values."` } // EthereumParsedInputData contains the parsed data for an input data hex payload type EthereumParsedInputData struct { - MethodId string `json:"methodId"` - Name string `json:"name"` - Function string `json:"function,omitempty"` - Params []EthereumParsedInputParam `json:"params,omitempty"` + MethodId string `json:"methodId" ts_doc:"First 4 bytes of the input data (method signature ID)."` + Name string `json:"name" ts_doc:"Parsed function name if recognized."` + Function string `json:"function,omitempty" ts_doc:"Full function signature (including parameter types)."` + Params []EthereumParsedInputParam `json:"params,omitempty" ts_doc:"List of parsed parameters for this function call."` } // EthereumInternalTransactionType - type of ethereum transaction from internal data @@ -49,12 +49,12 @@ const ( SELFDESTRUCT ) -// EthereumInternalTransaction contains internal transfers +// EthereumInternalData contains internal transfers type EthereumInternalData struct { - Type EthereumInternalTransactionType `json:"type"` - Contract string `json:"contract,omitempty"` - Transfers []EthereumInternalTransfer `json:"transfers,omitempty"` - Error string + Type EthereumInternalTransactionType `json:"type" ts_doc:"High-level type of the internal transaction (CALL, CREATE, etc.)."` + Contract string `json:"contract,omitempty" ts_doc:"Address of the contract involved, if any."` + Transfers []EthereumInternalTransfer `json:"transfers,omitempty" ts_doc:"List of internal transfers associated with this data."` + Error string `ts_doc:"Error message if something went wrong while processing."` } // ContractInfo contains info about a contract @@ -62,12 +62,12 @@ type ContractInfo struct { // Deprecated: Use Standard instead. Type TokenStandardName `json:"type" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'" ts_doc:"@deprecated: Use standard instead."` Standard TokenStandardName `json:"standard" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'"` - Contract string `json:"contract"` - Name string `json:"name"` - Symbol string `json:"symbol"` - Decimals int `json:"decimals"` - CreatedInBlock uint32 `json:"createdInBlock,omitempty"` - DestructedInBlock uint32 `json:"destructedInBlock,omitempty"` + Contract string `json:"contract" ts_doc:"Smart contract address."` + Name string `json:"name" ts_doc:"Readable name of the contract."` + Symbol string `json:"symbol" ts_doc:"Symbol for tokens under this contract, if applicable."` + Decimals int `json:"decimals" ts_doc:"Number of decimal places, if applicable."` + CreatedInBlock uint32 `json:"createdInBlock,omitempty" ts_doc:"Block height where contract was first created."` + DestructedInBlock uint32 `json:"destructedInBlock,omitempty" ts_doc:"Block height where contract was destroyed (if any)."` } // Ethereum token standard names @@ -81,37 +81,38 @@ const ( // the map must match all bchain.TokenStandard to avoid index out of range panic var EthereumTokenStandardMap = []TokenStandardName{ERC20TokenStandard, ERC771TokenStandard, ERC1155TokenStandard} +// MultiTokenValue holds one ID-value pair for multi-token standards like ERC1155 type MultiTokenValue struct { - Id big.Int - Value big.Int + Id big.Int `ts_doc:"Token ID for this multi-token entry."` + Value big.Int `ts_doc:"Amount of the token ID transferred or owned."` } // TokenTransfer contains a single token transfer type TokenTransfer struct { - Standard TokenStandard - Contract string - From string - To string - Value big.Int - MultiTokenValues []MultiTokenValue + Standard TokenStandard `ts_doc:"Integer value od the token standard."` + Contract string `ts_doc:"Smart contract address for the token."` + From string `ts_doc:"Sender address of the token transfer."` + To string `ts_doc:"Recipient address of the token transfer."` + Value big.Int `ts_doc:"Amount of tokens transferred (for fungible tokens)."` + MultiTokenValues []MultiTokenValue `ts_doc:"List of ID-value pairs for multi-token transfers (e.g., ERC1155)."` } // RpcTransaction is returned by eth_getTransactionByHash type RpcTransaction struct { - AccountNonce string `json:"nonce"` - GasPrice string `json:"gasPrice"` + AccountNonce string `json:"nonce" ts_doc:"Transaction nonce from the sender's account."` + GasPrice string `json:"gasPrice" ts_doc:"Gas price bid by the sender in Wei."` MaxPriorityFeePerGas string `json:"maxPriorityFeePerGas,omitempty"` MaxFeePerGas string `json:"maxFeePerGas,omitempty"` BaseFeePerGas string `json:"baseFeePerGas,omitempty"` - GasLimit string `json:"gas"` - To string `json:"to"` // nil means contract creation - Value string `json:"value"` - Payload string `json:"input"` - Hash string `json:"hash"` - BlockNumber string `json:"blockNumber"` - BlockHash string `json:"blockHash,omitempty"` - From string `json:"from"` - TransactionIndex string `json:"transactionIndex"` + GasLimit string `json:"gas" ts_doc:"Maximum gas allowed for this transaction."` + To string `json:"to" ts_doc:"Recipient address if not a contract creation. Empty if it's contract creation."` + Value string `json:"value" ts_doc:"Amount of Ether (in Wei) sent in this transaction."` + Payload string `json:"input" ts_doc:"Hex-encoded input data for contract calls."` + Hash string `json:"hash" ts_doc:"Transaction hash."` + BlockNumber string `json:"blockNumber" ts_doc:"Block number where this transaction was included, if mined."` + BlockHash string `json:"blockHash,omitempty" ts_doc:"Hash of the block in which this transaction was included, if mined."` + From string `json:"from" ts_doc:"Sender's address derived by the backend."` + TransactionIndex string `json:"transactionIndex" ts_doc:"Index of the transaction within the block, if mined."` // Signature values - ignored // V string `json:"v"` // R string `json:"r"` @@ -120,53 +121,53 @@ type RpcTransaction struct { // RpcLog is returned by eth_getLogs type RpcLog struct { - Address string `json:"address"` - Topics []string `json:"topics"` - Data string `json:"data"` + Address string `json:"address" ts_doc:"Contract or address from which this log originated."` + Topics []string `json:"topics" ts_doc:"Indexed event signatures and parameters."` + Data string `json:"data" ts_doc:"Unindexed event data in hex form."` } -// RpcLog is returned by eth_getTransactionReceipt +// RpcReceipt is returned by eth_getTransactionReceipt type RpcReceipt struct { - GasUsed string `json:"gasUsed"` - Status string `json:"status"` - Logs []*RpcLog `json:"logs"` - L1Fee string `json:"l1Fee,omitempty"` - L1FeeScalar string `json:"l1FeeScalar,omitempty"` - L1GasPrice string `json:"l1GasPrice,omitempty"` - L1GasUsed string `json:"l1GasUsed,omitempty"` + GasUsed string `json:"gasUsed" ts_doc:"Amount of gas actually used by the transaction."` + Status string `json:"status" ts_doc:"Transaction execution status (0x0 = fail, 0x1 = success)."` + Logs []*RpcLog `json:"logs" ts_doc:"Array of log entries generated by this transaction."` + L1Fee string `json:"l1Fee,omitempty" ts_doc:"Additional Layer 1 fee, if on a rollup network."` + L1FeeScalar string `json:"l1FeeScalar,omitempty" ts_doc:"Fee scaling factor for L1 fees on some L2s."` + L1GasPrice string `json:"l1GasPrice,omitempty" ts_doc:"Gas price used on L1 for the rollup network."` + L1GasUsed string `json:"l1GasUsed,omitempty" ts_doc:"Amount of L1 gas used by the transaction, if any."` } // EthereumSpecificData contains data specific to Ethereum transactions type EthereumSpecificData struct { - Tx *RpcTransaction `json:"tx"` - InternalData *EthereumInternalData `json:"internalData,omitempty"` - Receipt *RpcReceipt `json:"receipt,omitempty"` + Tx *RpcTransaction `json:"tx" ts_doc:"Raw transaction details from the blockchain node."` + InternalData *EthereumInternalData `json:"internalData,omitempty" ts_doc:"Summary of internal calls/transfers, if any."` + Receipt *RpcReceipt `json:"receipt,omitempty" ts_doc:"Transaction receipt info, including logs and gas usage."` } // AddressAliasRecord maps address to ENS name type AddressAliasRecord struct { - Address string - Name string + Address string `ts_doc:"Address whose alias is being stored."` + Name string `ts_doc:"The resolved name/alias (e.g. ENS domain)."` } // EthereumBlockSpecificData contain data specific for Ethereum block type EthereumBlockSpecificData struct { - InternalDataError string - AddressAliasRecords []AddressAliasRecord - Contracts []ContractInfo + InternalDataError string `ts_doc:"Error message for processing block internal data, if any."` + AddressAliasRecords []AddressAliasRecord `ts_doc:"List of address-to-alias mappings discovered in this block."` + Contracts []ContractInfo `ts_doc:"List of contracts created or updated in this block."` } -// StakingPool holds data about address participation in a staking pool contract +// StakingPoolData holds data about address participation in a staking pool contract type StakingPoolData struct { - Contract string `json:"contract"` - Name string `json:"name"` - PendingBalance big.Int `json:"pendingBalance"` // pendingBalanceOf method - PendingDepositedBalance big.Int `json:"pendingDepositedBalance"` // pendingDepositedBalanceOf method - DepositedBalance big.Int `json:"depositedBalance"` // depositedBalanceOf method - WithdrawTotalAmount big.Int `json:"withdrawTotalAmount"` // withdrawRequest method, return value [0] - ClaimableAmount big.Int `json:"claimableAmount"` // withdrawRequest method, return value [1] - RestakedReward big.Int `json:"restakedReward"` // restakedRewardOf method - AutocompoundBalance big.Int `json:"autocompoundBalance"` // autocompoundBalanceOf method + Contract string `json:"contract" ts_doc:"Address of the staking pool contract."` + Name string `json:"name" ts_doc:"Human-readable name of the staking pool."` + PendingBalance big.Int `json:"pendingBalance" ts_doc:"Amount not yet finalized in the pool (pendingBalanceOf)."` + PendingDepositedBalance big.Int `json:"pendingDepositedBalance" ts_doc:"Amount pending deposit (pendingDepositedBalanceOf)."` + DepositedBalance big.Int `json:"depositedBalance" ts_doc:"Total amount currently deposited (depositedBalanceOf)."` + WithdrawTotalAmount big.Int `json:"withdrawTotalAmount" ts_doc:"Total amount requested for withdrawal (withdrawRequest[0])."` + ClaimableAmount big.Int `json:"claimableAmount" ts_doc:"Amount that can be claimed (withdrawRequest[1])."` + RestakedReward big.Int `json:"restakedReward" ts_doc:"Total reward that has been restaked (restakedRewardOf)."` + AutocompoundBalance big.Int `json:"autocompoundBalance" ts_doc:"Auto-compounded balance (autocompoundBalanceOf)."` } // Eip1559Fee diff --git a/common/internalstate.go b/common/internalstate.go index 9e452ce7e3..2122c1784d 100644 --- a/common/internalstate.go +++ b/common/internalstate.go @@ -23,85 +23,85 @@ var inShutdown int32 // InternalStateColumn contains the data of a db column type InternalStateColumn struct { - Name string `json:"name"` - Version uint32 `json:"version"` - Rows int64 `json:"rows"` - KeyBytes int64 `json:"keyBytes"` - ValueBytes int64 `json:"valueBytes"` - Updated time.Time `json:"updated"` + Name string `json:"name" ts_doc:"Name of the database column."` + Version uint32 `json:"version" ts_doc:"Version or schema version of the column."` + Rows int64 `json:"rows" ts_doc:"Number of rows stored in this column."` + KeyBytes int64 `json:"keyBytes" ts_doc:"Total size (in bytes) of keys stored in this column."` + ValueBytes int64 `json:"valueBytes" ts_doc:"Total size (in bytes) of values stored in this column."` + Updated time.Time `json:"updated" ts_doc:"Timestamp of the last update to this column."` } // BackendInfo is used to get information about blockchain type BackendInfo struct { - BackendError string `json:"error,omitempty"` - Chain string `json:"chain,omitempty"` - Blocks int `json:"blocks,omitempty"` - Headers int `json:"headers,omitempty"` - BestBlockHash string `json:"bestBlockHash,omitempty"` - Difficulty string `json:"difficulty,omitempty"` - SizeOnDisk int64 `json:"sizeOnDisk,omitempty"` - Version string `json:"version,omitempty"` - Subversion string `json:"subversion,omitempty"` - ProtocolVersion string `json:"protocolVersion,omitempty"` - Timeoffset float64 `json:"timeOffset,omitempty"` - Warnings string `json:"warnings,omitempty"` - ConsensusVersion string `json:"consensus_version,omitempty"` - Consensus interface{} `json:"consensus,omitempty"` + BackendError string `json:"error,omitempty" ts_doc:"Error message if something went wrong in the backend."` + Chain string `json:"chain,omitempty" ts_doc:"Name of the chain - e.g. 'main'."` + Blocks int `json:"blocks,omitempty" ts_doc:"Number of fully verified blocks in the chain."` + Headers int `json:"headers,omitempty" ts_doc:"Number of block headers in the chain."` + BestBlockHash string `json:"bestBlockHash,omitempty" ts_doc:"Hash of the best block in hex."` + Difficulty string `json:"difficulty,omitempty" ts_doc:"Current difficulty of the network."` + SizeOnDisk int64 `json:"sizeOnDisk,omitempty" ts_doc:"Size of the blockchain data on disk in bytes."` + Version string `json:"version,omitempty" ts_doc:"Version of the blockchain backend - e.g. '280000'."` + Subversion string `json:"subversion,omitempty" ts_doc:"Subversion of the blockchain backend - e.g. '/Satoshi:28.0.0/'."` + ProtocolVersion string `json:"protocolVersion,omitempty" ts_doc:"Protocol version of the blockchain backend - e.g. '70016'."` + Timeoffset float64 `json:"timeOffset,omitempty" ts_doc:"Time offset (in seconds) reported by the backend."` + Warnings string `json:"warnings,omitempty" ts_doc:"Any warnings given by the backend regarding the chain state."` + ConsensusVersion string `json:"consensus_version,omitempty" ts_doc:"Version or details of the consensus protocol in use."` + Consensus interface{} `json:"consensus,omitempty" ts_doc:"Additional chain-specific consensus data."` } // InternalState contains the data of the internal state type InternalState struct { - mux sync.Mutex + mux sync.Mutex `ts_doc:"Mutex for synchronized access to the internal state."` - Coin string `json:"coin"` - CoinShortcut string `json:"coinShortcut"` - CoinLabel string `json:"coinLabel"` - Host string `json:"host"` - Network string `json:"network,omitempty"` + Coin string `json:"coin" ts_doc:"Coin name (e.g. 'Bitcoin')."` + CoinShortcut string `json:"coinShortcut" ts_doc:"Short code for the coin (e.g. 'BTC')."` + CoinLabel string `json:"coinLabel" ts_doc:"Human-readable label for the coin (e.g. 'Bitcoin main')."` + Host string `json:"host" ts_doc:"Hostname of the node or backend."` + Network string `json:"network,omitempty" ts_doc:"Network name if different from CoinShortcut (e.g. 'testnet')."` - DbState uint32 `json:"dbState"` - ExtendedIndex bool `json:"extendedIndex"` + DbState uint32 `json:"dbState" ts_doc:"State of the database (closed=0, open=1, inconsistent=2)."` + ExtendedIndex bool `json:"extendedIndex" ts_doc:"Indicates if an extended indexing strategy is used."` - LastStore time.Time `json:"lastStore"` + LastStore time.Time `json:"lastStore" ts_doc:"Time when the internal state was last stored/persisted."` // true if application is with flag --sync - SyncMode bool `json:"syncMode"` + SyncMode bool `json:"syncMode" ts_doc:"Flag indicating if the node is in sync mode."` - InitialSync bool `json:"initialSync"` - IsSynchronized bool `json:"isSynchronized"` - BestHeight uint32 `json:"bestHeight"` - StartSync time.Time `json:"-"` - LastSync time.Time `json:"lastSync"` - BlockTimes []uint32 `json:"-"` - AvgBlockPeriod uint32 `json:"-"` + InitialSync bool `json:"initialSync" ts_doc:"If true, the system is in the initial sync phase."` + IsSynchronized bool `json:"isSynchronized" ts_doc:"If true, the main index is fully synced to BestHeight."` + BestHeight uint32 `json:"bestHeight" ts_doc:"Current best block height known to the indexer."` + StartSync time.Time `json:"-" ts_doc:"Timestamp when sync started (not exposed via JSON)."` + LastSync time.Time `json:"lastSync" ts_doc:"Timestamp of the last successful sync."` + BlockTimes []uint32 `json:"-" ts_doc:"List of block timestamps (per height) for calculating historical stats (not exposed via JSON)."` + AvgBlockPeriod uint32 `json:"-" ts_doc:"Average time (in seconds) per block for the last 100 blocks (not exposed via JSON)."` - IsMempoolSynchronized bool `json:"isMempoolSynchronized"` - MempoolSize int `json:"mempoolSize"` - LastMempoolSync time.Time `json:"lastMempoolSync"` + IsMempoolSynchronized bool `json:"isMempoolSynchronized" ts_doc:"If true, mempool data is in sync."` + MempoolSize int `json:"mempoolSize" ts_doc:"Number of transactions in the current mempool."` + LastMempoolSync time.Time `json:"lastMempoolSync" ts_doc:"Timestamp of the last mempool sync."` - DbColumns []InternalStateColumn `json:"dbColumns"` + DbColumns []InternalStateColumn `json:"dbColumns" ts_doc:"List of database column statistics."` - HasFiatRates bool `json:"-"` - HasTokenFiatRates bool `json:"-"` - HistoricalFiatRatesTime time.Time `json:"historicalFiatRatesTime"` - HistoricalTokenFiatRatesTime time.Time `json:"historicalTokenFiatRatesTime"` + HasFiatRates bool `json:"-" ts_doc:"True if fiat rates are supported (not exposed via JSON)."` + HasTokenFiatRates bool `json:"-" ts_doc:"True if token fiat rates are supported (not exposed via JSON)."` + HistoricalFiatRatesTime time.Time `json:"historicalFiatRatesTime" ts_doc:"Timestamp of the last historical fiat rates update."` + HistoricalTokenFiatRatesTime time.Time `json:"historicalTokenFiatRatesTime" ts_doc:"Timestamp of the last historical token fiat rates update."` - EnableSubNewTx bool `json:"-"` + EnableSubNewTx bool `json:"-" ts_doc:"Internal flag controlling subscription to new transactions (not exposed)."` - BackendInfo BackendInfo `json:"-"` + BackendInfo BackendInfo `json:"-" ts_doc:"Information about the connected blockchain backend (not exposed in JSON)."` // database migrations - UtxoChecked bool `json:"utxoChecked"` - SortedAddressContracts bool `json:"sortedAddressContracts"` + UtxoChecked bool `json:"utxoChecked" ts_doc:"Indicates if UTXO consistency checks have been performed."` + SortedAddressContracts bool `json:"sortedAddressContracts" ts_doc:"Indicates if address/contract sorting has been completed."` // golomb filter settings - BlockGolombFilterP uint8 `json:"block_golomb_filter_p"` - BlockFilterScripts string `json:"block_filter_scripts"` - BlockFilterUseZeroedKey bool `json:"block_filter_use_zeroed_key"` + BlockGolombFilterP uint8 `json:"block_golomb_filter_p" ts_doc:"Parameter P for building Golomb-Rice filters for blocks."` + BlockFilterScripts string `json:"block_filter_scripts" ts_doc:"Scripts included in block filters (e.g., 'p2pkh,p2sh')."` + BlockFilterUseZeroedKey bool `json:"block_filter_use_zeroed_key" ts_doc:"If true, uses a zeroed key for building block filters."` // allowed number of fetched accounts over websocket - WsGetAccountInfoLimit int `json:"-"` - WsLimitExceedingIPs map[string]int `json:"-"` + WsGetAccountInfoLimit int `json:"-" ts_doc:"Limit of how many getAccountInfo calls can be made via WS (not exposed)."` + WsLimitExceedingIPs map[string]int `json:"-" ts_doc:"Tracks IP addresses exceeding the WS limit (not exposed)."` } // StartedSync signals start of synchronization diff --git a/server/ws_types.go b/server/ws_types.go index 111b698e1c..6732b4ead9 100644 --- a/server/ws_types.go +++ b/server/ws_types.go @@ -6,150 +6,176 @@ import ( "github.com/trezor/blockbook/api" ) +// WsReq represents a generic WebSocket request with an ID, method, and raw parameters. type WsReq struct { - ID string `json:"id"` - Method string `json:"method" ts_type:"'getAccountInfo' | 'getInfo' | 'getBlockHash'| 'getBlock' | 'getAccountUtxo' | 'getBalanceHistory' | 'getTransaction' | 'getTransactionSpecific' | 'estimateFee' | 'sendTransaction' | 'subscribeNewBlock' | 'unsubscribeNewBlock' | 'subscribeNewTransaction' | 'unsubscribeNewTransaction' | 'subscribeAddresses' | 'unsubscribeAddresses' | 'subscribeFiatRates' | 'unsubscribeFiatRates' | 'ping' | 'getCurrentFiatRates' | 'getFiatRatesForTimestamps' | 'getFiatRatesTickersList' | 'getMempoolFilters'"` - Params json.RawMessage `json:"params" ts_type:"any"` + ID string `json:"id" ts_doc:"Unique request identifier."` + Method string `json:"method" ts_type:"'getAccountInfo' | 'getInfo' | 'getBlockHash'| 'getBlock' | 'getAccountUtxo' | 'getBalanceHistory' | 'getTransaction' | 'getTransactionSpecific' | 'estimateFee' | 'sendTransaction' | 'subscribeNewBlock' | 'unsubscribeNewBlock' | 'subscribeNewTransaction' | 'unsubscribeNewTransaction' | 'subscribeAddresses' | 'unsubscribeAddresses' | 'subscribeFiatRates' | 'unsubscribeFiatRates' | 'ping' | 'getCurrentFiatRates' | 'getFiatRatesForTimestamps' | 'getFiatRatesTickersList' | 'getMempoolFilters'" ts_doc:"Requested method name."` + Params json.RawMessage `json:"params" ts_type:"any" ts_doc:"Parameters for the requested method in raw JSON format."` } +// WsRes represents a generic WebSocket response with an ID and arbitrary data. type WsRes struct { - ID string `json:"id"` - Data interface{} `json:"data"` + ID string `json:"id" ts_doc:"Corresponding request identifier."` + Data interface{} `json:"data" ts_doc:"Payload of the response, structure depends on the request."` } +// WsAccountInfoReq carries parameters for the 'getAccountInfo' method. type WsAccountInfoReq struct { - Descriptor string `json:"descriptor"` - Details string `json:"details,omitempty" ts_type:"'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txslight' | 'txs'"` - Tokens string `json:"tokens,omitempty" ts_type:"'derived' | 'used' | 'nonzero'"` - PageSize int `json:"pageSize,omitempty"` - Page int `json:"page,omitempty"` - FromHeight int `json:"from,omitempty"` - ToHeight int `json:"to,omitempty"` - ContractFilter string `json:"contractFilter,omitempty"` - SecondaryCurrency string `json:"secondaryCurrency,omitempty"` - Gap int `json:"gap,omitempty"` -} - + Descriptor string `json:"descriptor" ts_doc:"Address or XPUB descriptor to query."` + Details string `json:"details,omitempty" ts_type:"'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txslight' | 'txs'" ts_doc:"Level of detail to retrieve about the account."` + Tokens string `json:"tokens,omitempty" ts_type:"'derived' | 'used' | 'nonzero'" ts_doc:"Which tokens to include in the account info."` + PageSize int `json:"pageSize,omitempty" ts_doc:"Number of items per page, if paging is used."` + Page int `json:"page,omitempty" ts_doc:"Requested page index, if paging is used."` + FromHeight int `json:"from,omitempty" ts_doc:"Starting block height for transaction filtering."` + ToHeight int `json:"to,omitempty" ts_doc:"Ending block height for transaction filtering."` + ContractFilter string `json:"contractFilter,omitempty" ts_doc:"Filter by specific contract address (for token data)."` + SecondaryCurrency string `json:"secondaryCurrency,omitempty" ts_doc:"Currency code to convert values into (e.g. 'USD')."` + Gap int `json:"gap,omitempty" ts_doc:"Gap limit for XPUB scanning, if relevant."` +} + +// WsBackendInfo holds extended info about the connected backend node. type WsBackendInfo struct { - Version string `json:"version,omitempty"` - Subversion string `json:"subversion,omitempty"` - ConsensusVersion string `json:"consensus_version,omitempty"` - Consensus interface{} `json:"consensus,omitempty"` + Version string `json:"version,omitempty" ts_doc:"Backend version string."` + Subversion string `json:"subversion,omitempty" ts_doc:"Backend sub-version string."` + ConsensusVersion string `json:"consensus_version,omitempty" ts_doc:"Consensus protocol version in use."` + Consensus interface{} `json:"consensus,omitempty" ts_doc:"Additional consensus details, structure depends on blockchain."` } +// WsInfoRes is returned by 'getInfo' requests, containing basic blockchain info. type WsInfoRes struct { - Name string `json:"name"` - Shortcut string `json:"shortcut"` - Network string `json:"network"` - Decimals int `json:"decimals"` - Version string `json:"version"` - BestHeight int `json:"bestHeight"` - BestHash string `json:"bestHash"` - Block0Hash string `json:"block0Hash"` - Testnet bool `json:"testnet"` - Backend WsBackendInfo `json:"backend"` -} - + Name string `json:"name" ts_doc:"Human-readable blockchain name."` + Shortcut string `json:"shortcut" ts_doc:"Short code for the blockchain (e.g. BTC, ETH)."` + Network string `json:"network" ts_doc:"Network identifier (e.g. mainnet, testnet)."` + Decimals int `json:"decimals" ts_doc:"Number of decimals in the base unit of the coin."` + Version string `json:"version" ts_doc:"Version of the blockbook or backend service."` + BestHeight int `json:"bestHeight" ts_doc:"Current best chain height according to the backend."` + BestHash string `json:"bestHash" ts_doc:"Block hash of the best (latest) block."` + Block0Hash string `json:"block0Hash" ts_doc:"Genesis block hash or identifier."` + Testnet bool `json:"testnet" ts_doc:"Indicates if this is a test network."` + Backend WsBackendInfo `json:"backend" ts_doc:"Additional backend-related information."` +} + +// WsBlockHashReq holds a single integer for querying the block hash at that height. type WsBlockHashReq struct { - Height int `json:"height"` + Height int `json:"height" ts_doc:"Block height for which the hash is requested."` } +// WsBlockHashRes returns the block hash for a requested height. type WsBlockHashRes struct { - Hash string `json:"hash"` + Hash string `json:"hash" ts_doc:"Block hash at the requested height."` } +// WsBlockReq is used to request details of a block (by ID) with paging options. type WsBlockReq struct { - Id string `json:"id"` - PageSize int `json:"pageSize,omitempty"` - Page int `json:"page,omitempty"` + Id string `json:"id" ts_doc:"Block identifier (hash)."` + PageSize int `json:"pageSize,omitempty" ts_doc:"Number of transactions per page in the block."` + Page int `json:"page,omitempty" ts_doc:"Page index to retrieve if multiple pages of transactions are available."` } +// WsAccountUtxoReq is used to request unspent transaction outputs (UTXOs) for a given xpub/address. type WsAccountUtxoReq struct { - Descriptor string `json:"descriptor"` + Descriptor string `json:"descriptor" ts_doc:"Address or XPUB descriptor to retrieve UTXOs for."` } +// WsBalanceHistoryReq is used to retrieve a historical balance chart or intervals for an account. type WsBalanceHistoryReq struct { - Descriptor string `json:"descriptor"` - From int64 `json:"from,omitempty"` - To int64 `json:"to,omitempty"` - Currencies []string `json:"currencies,omitempty"` - Gap int `json:"gap,omitempty"` - GroupBy uint32 `json:"groupBy,omitempty"` + Descriptor string `json:"descriptor" ts_doc:"Address or XPUB descriptor to query history for."` + From int64 `json:"from,omitempty" ts_doc:"Unix timestamp from which to start the history."` + To int64 `json:"to,omitempty" ts_doc:"Unix timestamp at which to end the history."` + Currencies []string `json:"currencies,omitempty" ts_doc:"List of currency codes for which to fetch exchange rates at each interval."` + Gap int `json:"gap,omitempty" ts_doc:"Gap limit for XPUB scanning, if relevant."` + GroupBy uint32 `json:"groupBy,omitempty" ts_doc:"Size of each aggregated time window in seconds."` } +// WsTransactionReq requests details for a specific transaction by its txid. type WsTransactionReq struct { - Txid string `json:"txid"` + Txid string `json:"txid" ts_doc:"Transaction ID to retrieve details for."` } +// WsMempoolFiltersReq requests mempool filters for scripts of a specific type, after a given timestamp. type WsMempoolFiltersReq struct { - ScriptType string `json:"scriptType"` - FromTimestamp uint32 `json:"fromTimestamp"` - ParamM uint64 `json:"M,omitempty"` + ScriptType string `json:"scriptType" ts_doc:"Type of script we are filtering for (e.g., P2PKH, P2SH)."` + FromTimestamp uint32 `json:"fromTimestamp" ts_doc:"Only retrieve filters for mempool txs after this timestamp."` + ParamM uint64 `json:"M,omitempty" ts_doc:"Optional parameter for certain filter logic (e.g., n-bloom)."` } +// WsBlockFilterReq requests a filter for a given block hash and script type. type WsBlockFilterReq struct { - ScriptType string `json:"scriptType"` - BlockHash string `json:"blockHash"` - ParamM uint64 `json:"M,omitempty"` + ScriptType string `json:"scriptType" ts_doc:"Type of script filter (e.g., P2PKH, P2SH)."` + BlockHash string `json:"blockHash" ts_doc:"Block hash for which we want the filter."` + ParamM uint64 `json:"M,omitempty" ts_doc:"Optional parameter for certain filter logic."` } +// WsBlockFiltersBatchReq is used to request batch filters for consecutive blocks. type WsBlockFiltersBatchReq struct { - ScriptType string `json:"scriptType"` - BlockHash string `json:"bestKnownBlockHash"` - PageSize int `json:"pageSize,omitempty"` - ParamM uint64 `json:"M,omitempty"` + ScriptType string `json:"scriptType" ts_doc:"Type of script filter (e.g., P2PKH, P2SH)."` + BlockHash string `json:"bestKnownBlockHash" ts_doc:"Hash of the latest known block. Filters will be retrieved backward from here."` + PageSize int `json:"pageSize,omitempty" ts_doc:"Number of block filters per request."` + ParamM uint64 `json:"M,omitempty" ts_doc:"Optional parameter for certain filter logic."` } +// WsTransactionSpecificReq requests blockchain-specific transaction info that might go beyond standard fields. type WsTransactionSpecificReq struct { - Txid string `json:"txid"` + Txid string `json:"txid" ts_doc:"Transaction ID for the detailed blockchain-specific data."` } +// WsEstimateFeeReq requests an estimation of transaction fees for a set of blocks or with specific parameters. type WsEstimateFeeReq struct { - Blocks []int `json:"blocks,omitempty"` - Specific map[string]interface{} `json:"specific,omitempty" ts_type:"{conservative?: boolean;txsize?: number;from?: string;to?: string;data?: string;value?: string;}"` + Blocks []int `json:"blocks,omitempty" ts_doc:"Block confirmations targets for which fees should be estimated."` + Specific map[string]interface{} `json:"specific,omitempty" ts_type:"{conservative?: boolean; txsize?: number; from?: string; to?: string; data?: string; value?: string;}" ts_doc:"Additional chain-specific parameters (e.g. for Ethereum)."` } +// WsEstimateFeeRes is returned in response to a fee estimation request. type WsEstimateFeeRes struct { - FeePerTx string `json:"feePerTx,omitempty"` - FeePerUnit string `json:"feePerUnit,omitempty"` - FeeLimit string `json:"feeLimit,omitempty"` + FeePerTx string `json:"feePerTx,omitempty" ts_doc:"Estimated total fee per transaction, if relevant."` + FeePerUnit string `json:"feePerUnit,omitempty" ts_doc:"Estimated fee per unit (sat/byte, Wei/gas, etc.)."` + FeeLimit string `json:"feeLimit,omitempty" ts_doc:"Max fee limit for blockchains like Ethereum."` Eip1559 *api.Eip1559Fees `json:"eip1559,omitempty"` } +// WsSendTransactionReq is used to broadcast a transaction to the network. type WsSendTransactionReq struct { - Hex string `json:"hex"` + Hex string `json:"hex" ts_doc:"Hex-encoded transaction data to broadcast."` } +// WsSubscribeAddressesReq is used to subscribe to updates on a list of addresses. type WsSubscribeAddressesReq struct { - Addresses []string `json:"addresses"` + Addresses []string `json:"addresses" ts_doc:"List of addresses to subscribe for updates (e.g., new transactions)."` } + +// WsSubscribeFiatRatesReq subscribes to updates of fiat rates for a specific currency or set of tokens. type WsSubscribeFiatRatesReq struct { - Currency string `json:"currency,omitempty"` - Tokens []string `json:"tokens,omitempty"` + Currency string `json:"currency,omitempty" ts_doc:"Fiat currency code (e.g. 'USD')."` + Tokens []string `json:"tokens,omitempty" ts_doc:"List of token symbols or IDs to get fiat rates for."` } +// WsCurrentFiatRatesReq requests the current fiat rates for specified currencies (and optionally a token). type WsCurrentFiatRatesReq struct { - Currencies []string `json:"currencies,omitempty"` - Token string `json:"token,omitempty"` + Currencies []string `json:"currencies,omitempty" ts_doc:"List of fiat currencies, e.g. ['USD','EUR']."` + Token string `json:"token,omitempty" ts_doc:"Token symbol or ID if asking for token fiat rates (e.g. 'ETH')."` } +// WsFiatRatesForTimestampsReq requests historical fiat rates for given timestamps. type WsFiatRatesForTimestampsReq struct { - Timestamps []int64 `json:"timestamps"` - Currencies []string `json:"currencies,omitempty"` - Token string `json:"token,omitempty"` + Timestamps []int64 `json:"timestamps" ts_doc:"List of Unix timestamps for which to retrieve fiat rates."` + Currencies []string `json:"currencies,omitempty" ts_doc:"List of fiat currencies, e.g. ['USD','EUR']."` + Token string `json:"token,omitempty" ts_doc:"Token symbol or ID if asking for token fiat rates."` } +// WsFiatRatesTickersListReq requests a list of tickers for a given timestamp (and possibly a token). type WsFiatRatesTickersListReq struct { - Timestamp int64 `json:"timestamp,omitempty"` - Token string `json:"token,omitempty"` + Timestamp int64 `json:"timestamp,omitempty" ts_doc:"Timestamp for which the list of available tickers is needed."` + Token string `json:"token,omitempty" ts_doc:"Token symbol or ID if asking for token-specific fiat rates."` } +// WsRpcCallReq is used for raw RPC calls (for example, on an Ethereum-like backend). type WsRpcCallReq struct { - From string `json:"from,omitempty"` - To string `json:"to"` - Data string `json:"data"` + From string `json:"from,omitempty" ts_doc:"Address from which the RPC call is originated (if relevant)."` + To string `json:"to" ts_doc:"Contract or address to which the RPC call is made."` + Data string `json:"data" ts_doc:"Hex-encoded call data (function signature + parameters)."` } +// WsRpcCallRes returns the result of an RPC call in hex form. type WsRpcCallRes struct { - Data string `json:"data"` + Data string `json:"data" ts_doc:"Hex-encoded return data from the call."` } From bb02eb54f7436f6a5d23153072f219017f44b6a9 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 23 May 2025 00:12:07 +0200 Subject: [PATCH 473/530] Update blockbook-api.ts --- blockbook-api.ts | 313 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) diff --git a/blockbook-api.ts b/blockbook-api.ts index bf0f9ad4fc..133d0d29a3 100644 --- a/blockbook-api.ts +++ b/blockbook-api.ts @@ -1,213 +1,373 @@ /* Do not change, this code is generated from Golang structs */ export interface APIError { + /** Human-readable error message describing the issue. */ Text: string; + /** Whether the error message can safely be shown to the end user. */ Public: boolean; } export interface AddressAlias { + /** Type of alias, e.g., user-defined name or contract name. */ Type: string; + /** Alias string for the address. */ Alias: string; } export interface EthereumInternalTransfer { + /** Type of internal transfer (CALL, CREATE, etc.). */ type: number; + /** Address from which the transfer originated. */ from: string; + /** Address to which the transfer was sent. */ to: string; + /** Value transferred internally (in Wei or base units). */ value: string; } export interface EthereumParsedInputParam { + /** Parameter type (e.g. 'uint256'). */ type: string; + /** List of stringified parameter values. */ values?: string[]; } export interface EthereumParsedInputData { + /** First 4 bytes of the input data (method signature ID). */ methodId: string; + /** Parsed function name if recognized. */ name: string; + /** Full function signature (including parameter types). */ function?: string; + /** List of parsed parameters for this function call. */ params?: EthereumParsedInputParam[]; } export interface EthereumSpecific { + /** High-level type of the Ethereum tx (e.g., 'call', 'create'). */ type?: number; + /** Address of contract created by this transaction, if any. */ createdContract?: string; + /** Execution status of the transaction (1: success, 0: fail, -1: pending). */ status: number; + /** Error encountered during execution, if any. */ error?: string; + /** Transaction nonce (sequential number from the sender). */ nonce: number; + /** Maximum gas allowed by the sender for this transaction. */ gasLimit: number; + /** Actual gas consumed by the transaction execution. */ gasUsed?: number; + /** Price (in Wei or base units) per gas unit. */ gasPrice?: string; maxPriorityFeePerGas?: string; maxFeePerGas?: string; baseFeePerGas?: string; + /** Fee used for L1 part in rollups (e.g. Optimism). */ l1Fee?: number; + /** Scaling factor for L1 fees in certain Layer 2 solutions. */ l1FeeScalar?: string; + /** Gas price for L1 component, if applicable. */ l1GasPrice?: string; + /** Amount of gas used in L1 for this tx, if applicable. */ l1GasUsed?: number; + /** Hex-encoded input data for the transaction. */ data?: string; + /** Decoded transaction data (function name, params, etc.). */ parsedData?: EthereumParsedInputData; + /** List of internal (sub-call) transfers. */ internalTransfers?: EthereumInternalTransfer[]; } export interface MultiTokenValue { + /** Token ID (for ERC1155). */ id?: string; + /** Amount of that specific token ID. */ value?: string; } export interface TokenTransfer { /** @deprecated: Use standard instead. */ type: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; standard: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; + /** Source address of the token transfer. */ from: string; + /** Destination address of the token transfer. */ to: string; + /** Contract address of the token. */ contract: string; + /** Token name. */ name?: string; + /** Token symbol. */ symbol?: string; + /** Number of decimals for this token (if applicable). */ decimals: number; + /** Amount (in base units) of tokens transferred. */ value?: string; + /** List of multiple ID-value pairs for ERC1155 transfers. */ multiTokenValues?: MultiTokenValue[]; } export interface Vout { + /** Amount (in satoshi or base units) of the output. */ value?: string; + /** Relative index of this output within the transaction. */ n: number; + /** Indicates whether this output has been spent. */ spent?: boolean; + /** Transaction ID in which this output was spent. */ spentTxId?: string; + /** Index of the input that spent this output. */ spentIndex?: number; + /** Block height at which this output was spent. */ spentHeight?: number; + /** Raw script hex data for this output - aka ScriptPubKey. */ hex?: string; + /** Disassembled script for this output. */ asm?: string; + /** List of addresses associated with this output. */ addresses: string[]; + /** Indicates whether this output is owned by valid address. */ isAddress: boolean; + /** Indicates if this output belongs to the wallet in context. */ isOwn?: boolean; + /** Output script type (e.g., 'P2PKH', 'P2SH'). */ type?: string; } export interface Vin { + /** ID/hash of the originating transaction (where the UTXO comes from). */ txid?: string; + /** Index of the output in the referenced transaction. */ vout?: number; + /** Sequence number for this input (e.g. 4294967293). */ sequence?: number; + /** Relative index of this input within the transaction. */ n: number; + /** List of addresses associated with this input. */ addresses?: string[]; + /** Indicates if this input is from a known address. */ isAddress: boolean; + /** Indicates if this input belongs to the wallet in context. */ isOwn?: boolean; + /** Amount (in satoshi or base units) of the input. */ value?: string; + /** Raw script hex data for this input. */ hex?: string; + /** Disassembled script for this input. */ asm?: string; + /** Data for coinbase inputs (when mining). */ coinbase?: string; } export interface Tx { + /** Transaction ID (hash). */ txid: string; + /** Version of the transaction (if applicable). */ version?: number; + /** Locktime indicating earliest time/height transaction can be mined. */ lockTime?: number; + /** Array of inputs for this transaction. */ vin: Vin[]; + /** Array of outputs for this transaction. */ vout: Vout[]; + /** Hash of the block containing this transaction. */ blockHash?: string; + /** Block height in which this transaction was included. */ blockHeight: number; + /** Number of confirmations (blocks mined after this tx's block). */ confirmations: number; + /** Estimated blocks remaining until confirmation (if unconfirmed). */ confirmationETABlocks?: number; + /** Estimated seconds remaining until confirmation (if unconfirmed). */ confirmationETASeconds?: number; + /** Unix timestamp of the block in which this transaction was included. 0 if unconfirmed. */ blockTime: number; + /** Transaction size in bytes. */ size?: number; + /** Virtual size in bytes, for SegWit-enabled chains. */ vsize?: number; + /** Total value of all outputs (in satoshi or base units). */ value: string; + /** Total value of all inputs (in satoshi or base units). */ valueIn?: string; + /** Transaction fee (inputs - outputs). */ fees?: string; + /** Raw hex-encoded transaction data. */ hex?: string; + /** Indicates if this transaction is replace-by-fee (RBF) enabled. */ rbf?: boolean; + /** Blockchain-specific extended data. */ coinSpecificData?: any; + /** List of token transfers that occurred in this transaction. */ tokenTransfers?: TokenTransfer[]; + /** Ethereum-like blockchain specific data (if applicable). */ ethereumSpecific?: EthereumSpecific; + /** Aliases for addresses involved in this transaction. */ addressAliases?: { [key: string]: AddressAlias }; } export interface FeeStats { + /** Number of transactions in the given block. */ txCount: number; + /** Sum of all fees in satoshi or base units. */ totalFeesSat: string; + /** Average fee per kilobyte in satoshi or base units. */ averageFeePerKb: number; + /** Fee distribution deciles (0%..100%) in satoshi or base units per kB. */ decilesFeePerKb: number[]; } export interface StakingPool { + /** Staking pool contract address on-chain. */ contract: string; + /** Name of the staking pool contract. */ name: string; + /** Balance pending deposit or withdrawal, if any. */ pendingBalance: string; + /** Any pending deposit that is not yet finalized. */ pendingDepositedBalance: string; + /** Currently deposited/staked balance. */ depositedBalance: string; + /** Total amount withdrawn from this pool by the address. */ withdrawTotalAmount: string; + /** Rewards or principal currently claimable by the address. */ claimableAmount: string; + /** Total rewards that have been restaked automatically. */ restakedReward: string; + /** Any balance automatically reinvested into the pool. */ autocompoundBalance: string; } export interface ContractInfo { /** @deprecated: Use standard instead. */ type: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; standard: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; + /** Smart contract address. */ contract: string; + /** Readable name of the contract. */ name: string; + /** Symbol for tokens under this contract, if applicable. */ symbol: string; + /** Number of decimal places, if applicable. */ decimals: number; + /** Block height where contract was first created. */ createdInBlock?: number; + /** Block height where contract was destroyed (if any). */ destructedInBlock?: number; } export interface Token { /** @deprecated: Use standard instead. */ type: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; standard: '' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'; + /** Readable name of the token. */ name: string; + /** Derivation path if this token is derived from an XPUB-based address. */ path?: string; + /** Contract address on-chain. */ contract?: string; + /** Total number of token transfers for this address. */ transfers: number; + /** Symbol for the token (e.g., 'ETH', 'USDT'). */ symbol?: string; + /** Number of decimals for this token. */ decimals: number; + /** Current token balance (in minimal base units). */ balance?: string; + /** Value in the base currency (e.g. ETH for ERC20 tokens). */ baseValue?: number; + /** Value in a secondary currency (e.g. fiat), if available. */ secondaryValue?: number; + /** List of token IDs (for ERC721, each ID is a unique collectible). */ ids?: string[]; + /** Multiple ERC1155 token balances (id + value). */ multiTokenValues?: MultiTokenValue[]; + /** Total amount of tokens received. */ totalReceived?: string; + /** Total amount of tokens sent. */ totalSent?: string; } export interface Address { + /** Current page index. */ page?: number; + /** Total number of pages available. */ totalPages?: number; + /** Number of items returned on this page. */ itemsOnPage?: number; + /** The address string in standard format. */ address: string; + /** Current confirmed balance (in satoshi or base units). */ balance: string; + /** Total amount ever received by this address. */ totalReceived?: string; + /** Total amount ever sent by this address. */ totalSent?: string; + /** Unconfirmed balance for this address. */ unconfirmedBalance: string; + /** Number of unconfirmed transactions for this address. */ unconfirmedTxs: number; + /** Unconfirmed outgoing balance for this address. */ unconfirmedSending?: string; + /** Unconfirmed incoming balance for this address. */ unconfirmedReceiving?: string; + /** Number of transactions for this address (including confirmed). */ txs: number; + /** Historical total count of transactions, if known. */ addrTxCount?: number; + /** Number of transactions not involving tokens (pure coin transfers). */ nonTokenTxs?: number; + /** Number of internal transactions (e.g., Ethereum calls). */ internalTxs?: number; + /** List of transaction details (if requested). */ transactions?: Tx[]; + /** List of transaction IDs (if detailed data is not requested). */ txids?: string[]; + /** Current transaction nonce for Ethereum-like addresses. */ nonce?: string; + /** Number of tokens with any historical usage at this address. */ usedTokens?: number; + /** List of tokens associated with this address. */ tokens?: Token[]; + /** Total value of the address in secondary currency (e.g. fiat). */ secondaryValue?: number; + /** Sum of token values in base currency. */ tokensBaseValue?: number; + /** Sum of token values in secondary currency (fiat). */ tokensSecondaryValue?: number; + /** Address's entire value in base currency, including tokens. */ totalBaseValue?: number; + /** Address's entire value in secondary currency, including tokens. */ totalSecondaryValue?: number; + /** Extra info if the address is a contract (ABI, type). */ contractInfo?: ContractInfo; /** @deprecated: replaced by contractInfo */ erc20Contract?: ContractInfo; + /** Aliases assigned to this address. */ addressAliases?: { [key: string]: AddressAlias }; + /** List of staking pool data if address interacts with staking. */ stakingPools?: StakingPool[]; } export interface Utxo { + /** Transaction ID in which this UTXO was created. */ txid: string; + /** Index of the output in that transaction. */ vout: number; + /** Value of this UTXO (in satoshi or base units). */ value: string; + /** Block height in which the UTXO was confirmed. */ height?: number; + /** Number of confirmations for this UTXO. */ confirmations: number; + /** Address to which this UTXO belongs. */ address?: string; + /** Derivation path for XPUB-based wallets, if applicable. */ path?: string; + /** If non-zero, locktime required before spending this UTXO. */ lockTime?: number; + /** Indicates if this UTXO originated from a coinbase transaction. */ coinbase?: boolean; } export interface BalanceHistory { + /** Unix timestamp for this point in the balance history. */ time: number; + /** Number of transactions in this interval. */ txs: number; + /** Amount received in this interval (in satoshi or base units). */ received: string; + /** Amount sent in this interval (in satoshi or base units). */ sent: string; + /** Amount sent to the same address (self-transfer). */ sentToSelf: string; + /** Exchange rates at this point in time, if available. */ rates?: { [key: string]: number }; + /** Transaction ID if the time corresponds to a specific tx. */ txid?: string; } export interface BlockInfo { @@ -218,105 +378,185 @@ export interface BlockInfo { Height: number; } export interface Blocks { + /** Current page index. */ page?: number; + /** Total number of pages available. */ totalPages?: number; + /** Number of items returned on this page. */ itemsOnPage?: number; + /** List of blocks. */ blocks: BlockInfo[]; } export interface Block { + /** Current page index. */ page?: number; + /** Total number of pages available. */ totalPages?: number; + /** Number of items returned on this page. */ itemsOnPage?: number; + /** Block hash. */ hash: string; + /** Hash of the previous block in the chain. */ previousBlockHash?: string; + /** Hash of the next block, if known. */ nextBlockHash?: string; + /** Block height (0-based index in the chain). */ height: number; + /** Number of confirmations of this block (distance from best chain tip). */ confirmations: number; + /** Size of the block in bytes. */ size: number; + /** Timestamp of when this block was mined. */ time?: number; + /** Block version (chain-specific meaning). */ version: string; + /** Merkle root of the block's transactions. */ merkleRoot: string; + /** Nonce used in the mining process. */ nonce: string; + /** Compact representation of the target threshold. */ bits: string; + /** Difficulty target for mining this block. */ difficulty: string; + /** List of transaction IDs included in this block. */ tx?: string[]; + /** Total count of transactions in this block. */ txCount: number; + /** List of full transaction details (if requested). */ txs?: Tx[]; + /** Optional aliases for addresses found in this block. */ addressAliases?: { [key: string]: AddressAlias }; } export interface BlockRaw { + /** Hex-encoded block data. */ hex: string; } export interface BackendInfo { + /** Error message if something went wrong in the backend. */ error?: string; + /** Name of the chain - e.g. 'main'. */ chain?: string; + /** Number of fully verified blocks in the chain. */ blocks?: number; + /** Number of block headers in the chain. */ headers?: number; + /** Hash of the best block in hex. */ bestBlockHash?: string; + /** Current difficulty of the network. */ difficulty?: string; + /** Size of the blockchain data on disk in bytes. */ sizeOnDisk?: number; + /** Version of the blockchain backend - e.g. '280000'. */ version?: string; + /** Subversion of the blockchain backend - e.g. '/Satoshi:28.0.0/'. */ subversion?: string; + /** Protocol version of the blockchain backend - e.g. '70016'. */ protocolVersion?: string; + /** Time offset (in seconds) reported by the backend. */ timeOffset?: number; + /** Any warnings given by the backend regarding the chain state. */ warnings?: string; + /** Version or details of the consensus protocol in use. */ consensus_version?: string; + /** Additional chain-specific consensus data. */ consensus?: any; } export interface InternalStateColumn { + /** Name of the database column. */ name: string; + /** Version or schema version of the column. */ version: number; + /** Number of rows stored in this column. */ rows: number; + /** Total size (in bytes) of keys stored in this column. */ keyBytes: number; + /** Total size (in bytes) of values stored in this column. */ valueBytes: number; + /** Timestamp of the last update to this column. */ updated: string; } export interface BlockbookInfo { + /** Coin name, e.g. 'Bitcoin'. */ coin: string; + /** Network shortcut, e.g. 'BTC'. */ network: string; + /** Hostname of the blockbook instance, e.g. 'backend5'. */ host: string; + /** Running blockbook version, e.g. '0.4.0'. */ version: string; + /** Git commit hash of the running blockbook, e.g. 'a0960c8e'. */ gitCommit: string; + /** Build time of running blockbook, e.g. '2024-08-08T12:32:50+00:00'. */ buildTime: string; + /** If true, blockbook is syncing from scratch or in a special sync mode. */ syncMode: boolean; + /** Indicates if blockbook is in its initial sync phase. */ initialSync: boolean; + /** Indicates if the backend is fully synced with the blockchain. */ inSync: boolean; + /** Best (latest) block height according to this instance. */ bestHeight: number; + /** Timestamp of the latest block in the chain. */ lastBlockTime: string; + /** Indicates if mempool info is synced as well. */ inSyncMempool: boolean; + /** Timestamp of the last mempool update. */ lastMempoolTime: string; + /** Number of unconfirmed transactions in the mempool. */ mempoolSize: number; + /** Number of decimals for this coin's base unit. */ decimals: number; + /** Size of the underlying database in bytes. */ dbSize: number; + /** Whether this instance provides fiat exchange rates. */ hasFiatRates?: boolean; + /** Whether this instance provides fiat exchange rates for tokens. */ hasTokenFiatRates?: boolean; + /** Timestamp of the latest fiat rates update. */ currentFiatRatesTime?: string; + /** Timestamp of the latest historical fiat rates update. */ historicalFiatRatesTime?: string; + /** Timestamp of the latest historical token fiat rates update. */ historicalTokenFiatRatesTime?: string; + /** List of contract addresses supported for staking. */ supportedStakingPools?: string[]; + /** Optional calculated DB size from columns. */ dbSizeFromColumns?: number; + /** List of columns/tables in the DB for internal state. */ dbColumns?: InternalStateColumn[]; + /** Additional human-readable info about this blockbook instance. */ about: string; } export interface SystemInfo { + /** Blockbook instance information. */ blockbook: BlockbookInfo; + /** Information about the connected backend node. */ backend: BackendInfo; } export interface FiatTicker { + /** Unix timestamp for these fiat rates. */ ts?: number; + /** Map of currency codes to their exchange rate. */ rates: { [key: string]: number }; + /** Any error message encountered while fetching rates. */ error?: string; } export interface FiatTickers { + /** List of fiat tickers with timestamps and rates. */ tickers: FiatTicker[]; } export interface AvailableVsCurrencies { + /** Timestamp for the available currency list. */ ts?: number; + /** List of currency codes (e.g., USD, EUR) supported by the rates. */ available_currencies: string[]; + /** Error message, if any, when fetching the available currencies. */ error?: string; } export interface WsReq { + /** Unique request identifier. */ id: string; + /** Requested method name. */ method: | 'getAccountInfo' | 'getInfo' @@ -341,83 +581,133 @@ export interface WsReq { | 'getFiatRatesForTimestamps' | 'getFiatRatesTickersList' | 'getMempoolFilters'; + /** Parameters for the requested method in raw JSON format. */ params: any; } export interface WsRes { + /** Corresponding request identifier. */ id: string; + /** Payload of the response, structure depends on the request. */ data: any; } export interface WsAccountInfoReq { + /** Address or XPUB descriptor to query. */ descriptor: string; + /** Level of detail to retrieve about the account. */ details?: 'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txslight' | 'txs'; + /** Which tokens to include in the account info. */ tokens?: 'derived' | 'used' | 'nonzero'; + /** Number of items per page, if paging is used. */ pageSize?: number; + /** Requested page index, if paging is used. */ page?: number; + /** Starting block height for transaction filtering. */ from?: number; + /** Ending block height for transaction filtering. */ to?: number; + /** Filter by specific contract address (for token data). */ contractFilter?: string; + /** Currency code to convert values into (e.g. 'USD'). */ secondaryCurrency?: string; + /** Gap limit for XPUB scanning, if relevant. */ gap?: number; } export interface WsBackendInfo { + /** Backend version string. */ version?: string; + /** Backend sub-version string. */ subversion?: string; + /** Consensus protocol version in use. */ consensus_version?: string; + /** Additional consensus details, structure depends on blockchain. */ consensus?: any; } export interface WsInfoRes { + /** Human-readable blockchain name. */ name: string; + /** Short code for the blockchain (e.g. BTC, ETH). */ shortcut: string; + /** Network identifier (e.g. mainnet, testnet). */ network: string; + /** Number of decimals in the base unit of the coin. */ decimals: number; + /** Version of the blockbook or backend service. */ version: string; + /** Current best chain height according to the backend. */ bestHeight: number; + /** Block hash of the best (latest) block. */ bestHash: string; + /** Genesis block hash or identifier. */ block0Hash: string; + /** Indicates if this is a test network. */ testnet: boolean; + /** Additional backend-related information. */ backend: WsBackendInfo; } export interface WsBlockHashReq { + /** Block height for which the hash is requested. */ height: number; } export interface WsBlockHashRes { + /** Block hash at the requested height. */ hash: string; } export interface WsBlockReq { + /** Block identifier (hash). */ id: string; + /** Number of transactions per page in the block. */ pageSize?: number; + /** Page index to retrieve if multiple pages of transactions are available. */ page?: number; } export interface WsBlockFilterReq { + /** Type of script filter (e.g., P2PKH, P2SH). */ scriptType: string; + /** Block hash for which we want the filter. */ blockHash: string; + /** Optional parameter for certain filter logic. */ M?: number; } export interface WsBlockFiltersBatchReq { + /** Type of script filter (e.g., P2PKH, P2SH). */ scriptType: string; + /** Hash of the latest known block. Filters will be retrieved backward from here. */ bestKnownBlockHash: string; + /** Number of block filters per request. */ pageSize?: number; + /** Optional parameter for certain filter logic. */ M?: number; } export interface WsAccountUtxoReq { + /** Address or XPUB descriptor to retrieve UTXOs for. */ descriptor: string; } export interface WsBalanceHistoryReq { + /** Address or XPUB descriptor to query history for. */ descriptor: string; + /** Unix timestamp from which to start the history. */ from?: number; + /** Unix timestamp at which to end the history. */ to?: number; + /** List of currency codes for which to fetch exchange rates at each interval. */ currencies?: string[]; + /** Gap limit for XPUB scanning, if relevant. */ gap?: number; + /** Size of each aggregated time window in seconds. */ groupBy?: number; } export interface WsTransactionReq { + /** Transaction ID to retrieve details for. */ txid: string; } export interface WsTransactionSpecificReq { + /** Transaction ID for the detailed blockchain-specific data. */ txid: string; } export interface WsEstimateFeeReq { + /** Block confirmations targets for which fees should be estimated. */ blocks?: number[]; + /** Additional chain-specific parameters (e.g. for Ethereum). */ specific?: { conservative?: boolean; txsize?: number; @@ -447,48 +737,71 @@ export interface Eip1559Fees { baseFeeTrend?: 'up' | 'down'; } export interface WsEstimateFeeRes { + /** Estimated total fee per transaction, if relevant. */ feePerTx?: string; + /** Estimated fee per unit (sat/byte, Wei/gas, etc.). */ feePerUnit?: string; + /** Max fee limit for blockchains like Ethereum. */ feeLimit?: string; eip1559?: Eip1559Fees; } export interface WsSendTransactionReq { + /** Hex-encoded transaction data to broadcast. */ hex: string; } export interface WsSubscribeAddressesReq { + /** List of addresses to subscribe for updates (e.g., new transactions). */ addresses: string[]; } export interface WsSubscribeFiatRatesReq { + /** Fiat currency code (e.g. 'USD'). */ currency?: string; + /** List of token symbols or IDs to get fiat rates for. */ tokens?: string[]; } export interface WsCurrentFiatRatesReq { + /** List of fiat currencies, e.g. ['USD','EUR']. */ currencies?: string[]; + /** Token symbol or ID if asking for token fiat rates (e.g. 'ETH'). */ token?: string; } export interface WsFiatRatesForTimestampsReq { + /** List of Unix timestamps for which to retrieve fiat rates. */ timestamps: number[]; + /** List of fiat currencies, e.g. ['USD','EUR']. */ currencies?: string[]; + /** Token symbol or ID if asking for token fiat rates. */ token?: string; } export interface WsFiatRatesTickersListReq { + /** Timestamp for which the list of available tickers is needed. */ timestamp?: number; + /** Token symbol or ID if asking for token-specific fiat rates. */ token?: string; } export interface WsMempoolFiltersReq { + /** Type of script we are filtering for (e.g., P2PKH, P2SH). */ scriptType: string; + /** Only retrieve filters for mempool txs after this timestamp. */ fromTimestamp: number; + /** Optional parameter for certain filter logic (e.g., n-bloom). */ M?: number; } export interface WsRpcCallReq { + /** Address from which the RPC call is originated (if relevant). */ from?: string; + /** Contract or address to which the RPC call is made. */ to: string; + /** Hex-encoded call data (function signature + parameters). */ data: string; } export interface WsRpcCallRes { + /** Hex-encoded return data from the call. */ data: string; } export interface MempoolTxidFilterEntries { + /** Map of txid to filter data (hex-encoded). */ entries?: { [key: string]: string }; + /** Indicates if a zeroed key was used in filter calculation. */ usedZeroedKey?: boolean; } From cbb179b38aa123a57858d3955773420f0843f000 Mon Sep 17 00:00:00 2001 From: f7b Date: Tue, 27 May 2025 13:05:35 +0200 Subject: [PATCH 474/530] eth (+testnet) 3.0.3 -> 3.0.4 --- configs/coins/ethereum.json | 12 ++++++------ configs/coins/ethereum_archive.json | 12 ++++++------ configs/coins/ethereum_testnet_holesky.json | 12 ++++++------ configs/coins/ethereum_testnet_holesky_archive.json | 12 ++++++------ configs/coins/ethereum_testnet_sepolia.json | 12 ++++++------ configs/coins/ethereum_testnet_sepolia_archive.json | 12 ++++++------ 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index b4dc67a1b4..7ca3241d83 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", + "version": "3.0.4", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", + "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", - "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", + "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" } } }, @@ -73,4 +73,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index ee9ff03950..beae15209e 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", + "version": "3.0.4", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", + "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", - "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", + "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" } } }, @@ -76,4 +76,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index c819b23750..d95223de2a 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", + "version": "3.0.4", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", + "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", - "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", + "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" } } }, @@ -68,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 794ef93753..11d144aaf9 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", + "version": "3.0.4", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", + "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", - "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", + "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" } } }, @@ -76,4 +76,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 6803ba312a..cd547195e5 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", + "version": "3.0.4", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", + "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", - "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", + "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" } } }, @@ -68,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 59926e9f0e..4d8cceb7d1 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.3", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_amd64.tar.gz", + "version": "3.0.4", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "39c0063727151bac6b30d9cd16afb97b93c1f371d6a1d85d491eddd38a3fb01f", + "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.3/erigon_v3.0.3_linux_arm64.tar.gz", - "verification_source": "9d19484f4c10a810bb030c2d26f555719eea043d4f1a23127257f0ce6849bc00" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", + "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" } } }, @@ -74,4 +74,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file From c0132423a7be1c92ef4b89828d4da7050b1265e4 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 23 May 2025 15:19:08 +0200 Subject: [PATCH 475/530] EthereumType: Add alternative provider for send raw tx --- bchain/coins/eth/ethrpc.go | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 0f8bce2660..5c6f50ec28 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -7,6 +7,7 @@ import ( "io" "math/big" "net/http" + "os" "strconv" "strings" "sync" @@ -80,6 +81,8 @@ type EthereumRPC struct { stakingPoolNames []string stakingPoolContracts []string alternativeFeeProvider alternativeFeeProviderInterface + alternativeSendTxURLs []string + alternativeSendTxOnly bool } // ProcessInternalTransactions specifies if internal transactions are processed @@ -127,6 +130,16 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification glog.Info("Using alternative fee provider ", s.ChainConfig.AlternativeEstimateFee) } + network := c.Network + if network == "" { + network = c.CoinShortcut + } + s.alternativeSendTxURLs = strings.Split(os.Getenv(strings.ToUpper(network)+"_ALTERNATIVE_SENDTX_URLS"), ",") + s.alternativeSendTxOnly = strings.ToUpper(os.Getenv(strings.ToUpper(network)+"_ALTERNATIVE_SENDTX_ONLY")) == "TRUE" + if len(s.alternativeSendTxURLs) > 0 { + glog.Infof("Using alternative send transaction providers %v. Use only alternative providers %v", s.alternativeSendTxURLs, s.alternativeSendTxOnly) + } + return s, nil } @@ -1093,6 +1106,23 @@ func (b *EthereumRPC) EthereumTypeGetEip1559Fees() (*bchain.Eip1559Fees, error) // SendRawTransaction sends raw transaction func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) { + if len(b.alternativeSendTxURLs) > 0 { + var retVal string + var retErr error + for i := range b.alternativeSendTxURLs { + glog.Info("eth_sendRawTransaction to ", b.alternativeSendTxURLs[i]) + r, err := b.callHttpStringResult(b.alternativeSendTxURLs[i], "eth_sendRawTransaction", hex) + // set success return value; or error only if there was no previous success + if err == nil || len(retVal) == 0 { + retVal = r + retErr = err + } + } + if b.alternativeSendTxOnly { + return retVal, retErr + } + } + glog.Info("eth_sendRawTransaction default") return b.callRpcStringResult("eth_sendRawTransaction", hex) } @@ -1101,6 +1131,32 @@ func (b *EthereumRPC) EthereumTypeGetRawTransaction(txid string) (string, error) return b.callRpcStringResult("eth_getRawTransactionByHash", txid) } +// Helper function for calling ETH RPC over http with parameters and getting string result. Creates and closes a new client for every call. +func (b *EthereumRPC) callHttpStringResult(url string, rpcMethod string, args ...interface{}) (string, error) { + ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) + defer cancel() + client, err := rpc.DialContext(ctx, url) + if err != nil { + return "", err + } + defer client.Close() + var raw json.RawMessage + err = client.CallContext(ctx, &raw, rpcMethod, args...) + if err != nil { + return "", err + } else if len(raw) == 0 { + return "", errors.New(url + " " + rpcMethod + " : failed") + } + var result string + if err := json.Unmarshal(raw, &result); err != nil { + return "", errors.Annotatef(err, "%s %s raw result %v", url, rpcMethod, raw) + } + if result == "" { + return "", errors.New(url + " " + rpcMethod + " : failed, empty result") + } + return result, nil +} + // Helper function for calling ETH RPC with parameters and getting string result func (b *EthereumRPC) callRpcStringResult(rpcMethod string, args ...interface{}) (string, error) { ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) From 2b155a4bab613c5c8f2db12a91b445a35e5dba2f Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 23 May 2025 15:19:31 +0200 Subject: [PATCH 476/530] EthereumType: Fetch mempool transactions from alternative provider --- bchain/coins/eth/ethrpc.go | 153 +++++++++++++++++++++++++------------ 1 file changed, 103 insertions(+), 50 deletions(-) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 5c6f50ec28..4b7dbbcbc4 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -60,29 +60,32 @@ type Configuration struct { // EthereumRPC is an interface to JSON-RPC eth service. type EthereumRPC struct { *bchain.BaseChain - Client bchain.EVMClient - RPC bchain.EVMRPCClient - MainNetChainID Network - Timeout time.Duration - Parser *EthereumParser - PushHandler func(bchain.NotificationType) - OpenRPC func(string) (bchain.EVMRPCClient, bchain.EVMClient, error) - Mempool *bchain.MempoolEthereumType - mempoolInitialized bool - bestHeaderLock sync.Mutex - bestHeader bchain.EVMHeader - bestHeaderTime time.Time - NewBlock bchain.EVMNewBlockSubscriber - newBlockSubscription bchain.EVMClientSubscription - NewTx bchain.EVMNewTxSubscriber - newTxSubscription bchain.EVMClientSubscription - ChainConfig *Configuration - supportedStakingPools []string - stakingPoolNames []string - stakingPoolContracts []string - alternativeFeeProvider alternativeFeeProviderInterface - alternativeSendTxURLs []string - alternativeSendTxOnly bool + Client bchain.EVMClient + RPC bchain.EVMRPCClient + MainNetChainID Network + Timeout time.Duration + Parser *EthereumParser + PushHandler func(bchain.NotificationType) + OpenRPC func(string) (bchain.EVMRPCClient, bchain.EVMClient, error) + Mempool *bchain.MempoolEthereumType + mempoolInitialized bool + bestHeaderLock sync.Mutex + bestHeader bchain.EVMHeader + bestHeaderTime time.Time + NewBlock bchain.EVMNewBlockSubscriber + newBlockSubscription bchain.EVMClientSubscription + NewTx bchain.EVMNewTxSubscriber + newTxSubscription bchain.EVMClientSubscription + ChainConfig *Configuration + supportedStakingPools []string + stakingPoolNames []string + stakingPoolContracts []string + alternativeFeeProvider alternativeFeeProviderInterface + alternativeSendTxURLs []string + alternativeSendTxOnly bool + alternativeFetchMempoolTx bool + alternativeMempoolTxs map[string]*bchain.RpcTransaction + alternativeMempoolTxsMux sync.Mutex } // ProcessInternalTransactions specifies if internal transactions are processed @@ -136,8 +139,13 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification } s.alternativeSendTxURLs = strings.Split(os.Getenv(strings.ToUpper(network)+"_ALTERNATIVE_SENDTX_URLS"), ",") s.alternativeSendTxOnly = strings.ToUpper(os.Getenv(strings.ToUpper(network)+"_ALTERNATIVE_SENDTX_ONLY")) == "TRUE" + s.alternativeFetchMempoolTx = strings.ToUpper(os.Getenv(strings.ToUpper(network)+"_ALTERNATIVE_FETCH_MEMPOOL_TX")) == "TRUE" if len(s.alternativeSendTxURLs) > 0 { - glog.Infof("Using alternative send transaction providers %v. Use only alternative providers %v", s.alternativeSendTxURLs, s.alternativeSendTxOnly) + glog.Infof("Using alternative send transaction providers %v. Only alternative providers %v", s.alternativeSendTxURLs, s.alternativeSendTxOnly) + } + if s.alternativeFetchMempoolTx { + s.alternativeMempoolTxs = make(map[string]*bchain.RpcTransaction) + glog.Infof("Alternative fetch mempool tx %v", s.alternativeFetchMempoolTx) } return s, nil @@ -830,9 +838,7 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash) } btxs[i] = *btx - if b.mempoolInitialized { - b.Mempool.RemoveTransactionFromMempool(tx.Hash) - } + b.removeTransactionFromMempool(tx.Hash) } bbk := bchain.Block{ BlockHeader: *bbh, @@ -874,20 +880,41 @@ func (b *EthereumRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) return b.GetTransaction(txid) } +func (b *EthereumRPC) removeTransactionFromMempool(txid string) { + // remove tx from mempool + if b.mempoolInitialized { + b.Mempool.RemoveTransactionFromMempool(txid) + } + // remove tx from mempool txs fetched by alternative method + if b.alternativeFetchMempoolTx { + b.alternativeMempoolTxsMux.Lock() + delete(b.alternativeMempoolTxs, txid) + b.alternativeMempoolTxsMux.Unlock() + } +} + // GetTransaction returns a transaction by the transaction ID. func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) defer cancel() - tx := &bchain.RpcTransaction{} + var tx *bchain.RpcTransaction + var txFound bool + var err error hash := ethcommon.HexToHash(txid) - err := b.RPC.CallContext(ctx, tx, "eth_getTransactionByHash", hash) - if err != nil { - return nil, err + if b.alternativeFetchMempoolTx { + b.alternativeMempoolTxsMux.Lock() + tx, txFound = b.alternativeMempoolTxs[txid] + b.alternativeMempoolTxsMux.Unlock() + } + if !txFound { + tx = &bchain.RpcTransaction{} + err = b.RPC.CallContext(ctx, tx, "eth_getTransactionByHash", hash) + if err != nil { + return nil, err + } } if *tx == (bchain.RpcTransaction{}) { - if b.mempoolInitialized { - b.Mempool.RemoveTransactionFromMempool(txid) - } + b.removeTransactionFromMempool(txid) return nil, bchain.ErrTxNotFound } var btx *bchain.Tx @@ -932,10 +959,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { if err != nil { return nil, errors.Annotatef(err, "txid %v", txid) } - // remove tx from mempool if it is there - if b.mempoolInitialized { - b.Mempool.RemoveTransactionFromMempool(txid) - } + b.removeTransactionFromMempool(txid) } return btx, nil } @@ -1106,24 +1130,44 @@ func (b *EthereumRPC) EthereumTypeGetEip1559Fees() (*bchain.Eip1559Fees, error) // SendRawTransaction sends raw transaction func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) { + var txid string + var retErr error if len(b.alternativeSendTxURLs) > 0 { - var retVal string - var retErr error for i := range b.alternativeSendTxURLs { glog.Info("eth_sendRawTransaction to ", b.alternativeSendTxURLs[i]) r, err := b.callHttpStringResult(b.alternativeSendTxURLs[i], "eth_sendRawTransaction", hex) // set success return value; or error only if there was no previous success - if err == nil || len(retVal) == 0 { - retVal = r + if err == nil || len(txid) == 0 { + txid = r retErr = err } } - if b.alternativeSendTxOnly { - return retVal, retErr + if b.alternativeSendTxOnly && b.alternativeFetchMempoolTx { + hash := ethcommon.HexToHash(txid) + raw, err := b.callHttpRawResult(b.alternativeSendTxURLs[0], "eth_getTransactionByHash", hash) + if err != nil || raw == nil { + glog.Errorf("eth_getTransactionByHash from %s returned error %v", b.alternativeSendTxURLs[0], err) + } else { + var tx bchain.RpcTransaction + if err := json.Unmarshal(raw, &tx); err != nil { + glog.Errorf("eth_getTransactionByHash from %s unmarshal returned error %v", b.alternativeSendTxURLs[0], err) + } + b.alternativeMempoolTxsMux.Lock() + b.alternativeMempoolTxs[txid] = &tx + b.alternativeMempoolTxsMux.Unlock() + b.Mempool.AddTransactionToMempool(txid) + } + return txid, retErr } } glog.Info("eth_sendRawTransaction default") - return b.callRpcStringResult("eth_sendRawTransaction", hex) + txid, retErr = b.callRpcStringResult("eth_sendRawTransaction", hex) + if b.ChainConfig.DisableMempoolSync { + // add transactions submitted by us to mempool if sync is disabled + b.Mempool.AddTransactionToMempool(txid) + } + return txid, retErr + } // EthereumTypeGetRawTransaction gets raw transaction in hex format @@ -1131,21 +1175,30 @@ func (b *EthereumRPC) EthereumTypeGetRawTransaction(txid string) (string, error) return b.callRpcStringResult("eth_getRawTransactionByHash", txid) } -// Helper function for calling ETH RPC over http with parameters and getting string result. Creates and closes a new client for every call. -func (b *EthereumRPC) callHttpStringResult(url string, rpcMethod string, args ...interface{}) (string, error) { +// Helper function for calling ETH RPC over http with parameters. Creates and closes a new client for every call. +func (b *EthereumRPC) callHttpRawResult(url string, rpcMethod string, args ...interface{}) (json.RawMessage, error) { ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) defer cancel() client, err := rpc.DialContext(ctx, url) if err != nil { - return "", err + return nil, err } defer client.Close() var raw json.RawMessage err = client.CallContext(ctx, &raw, rpcMethod, args...) if err != nil { - return "", err + return nil, err } else if len(raw) == 0 { - return "", errors.New(url + " " + rpcMethod + " : failed") + return nil, errors.New(url + " " + rpcMethod + " : failed") + } + return raw, nil +} + +// Helper function for calling ETH RPC over http with parameters and getting string result. Creates and closes a new client for every call. +func (b *EthereumRPC) callHttpStringResult(url string, rpcMethod string, args ...interface{}) (string, error) { + raw, err := b.callHttpRawResult(url, rpcMethod, args...) + if err != nil { + return "", err } var result string if err := json.Unmarshal(raw, &result); err != nil { From 04bfd565b74911067367fc0633dceeae7e860f77 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 28 May 2025 13:28:19 +0200 Subject: [PATCH 477/530] EthereumType: Fetch mempool transactions from alternative provider --- bchain/coins/eth/alternativesendtx.go | 206 ++++++++++++++++++++++++++ bchain/coins/eth/ethrpc.go | 102 ++----------- 2 files changed, 222 insertions(+), 86 deletions(-) create mode 100644 bchain/coins/eth/alternativesendtx.go diff --git a/bchain/coins/eth/alternativesendtx.go b/bchain/coins/eth/alternativesendtx.go new file mode 100644 index 0000000000..a1e0b85d33 --- /dev/null +++ b/bchain/coins/eth/alternativesendtx.go @@ -0,0 +1,206 @@ +package eth + +import ( + "context" + "encoding/json" + "os" + "strings" + "sync" + "time" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" + "github.com/golang/glog" + "github.com/juju/errors" + "github.com/trezor/blockbook/bchain" +) + +type storedTx struct { + tx *bchain.RpcTransaction + time uint32 +} + +// AlternativeSendTxProvider handles sending transactions to alternative providers +type AlternativeSendTxProvider struct { + urls []string + onlyAlternative bool + fetchMempoolTx bool + mempoolTxs map[string]storedTx + mempoolTxsMux sync.Mutex + mempoolTxsTimeout time.Duration + rpcTimeout time.Duration + mempool *bchain.MempoolEthereumType + removeTransactionFromMempool func(string) +} + +// NewAlternativeSendTxProvider creates a new alternative send tx provider if enabled +func NewAlternativeSendTxProvider(network string, rpcTimeout int, mempoolTxsTimeout int) *AlternativeSendTxProvider { + urls := strings.Split(os.Getenv(strings.ToUpper(network)+"_ALTERNATIVE_SENDTX_URLS"), ",") + onlyAlternative := strings.ToUpper(os.Getenv(strings.ToUpper(network)+"_ALTERNATIVE_SENDTX_ONLY")) == "TRUE" + fetchMempoolTx := strings.ToUpper(os.Getenv(strings.ToUpper(network)+"_ALTERNATIVE_FETCH_MEMPOOL_TX")) == "TRUE" + if len(urls) == 0 || urls[0] == "" { + return nil + } + + provider := &AlternativeSendTxProvider{ + urls: urls, + onlyAlternative: onlyAlternative, + fetchMempoolTx: fetchMempoolTx, + rpcTimeout: time.Duration(rpcTimeout) * time.Second, + mempoolTxsTimeout: time.Duration(mempoolTxsTimeout) * time.Hour, + mempoolTxs: make(map[string]storedTx), + } + + glog.Infof("Using alternative send transaction providers %v. Only alternative providers %v", urls, onlyAlternative) + if fetchMempoolTx { + glog.Infof("Alternative fetch mempool tx %v", fetchMempoolTx) + } + + return provider +} + +// SetupMempool sets up connection to the mempool +func (p *AlternativeSendTxProvider) SetupMempool(mempool *bchain.MempoolEthereumType, removeTransactionFromMempool func(string)) { + p.mempool = mempool + p.removeTransactionFromMempool = removeTransactionFromMempool +} + +// SendRawTransaction sends raw transaction to alternative providers +func (p *AlternativeSendTxProvider) SendRawTransaction(hex string) (string, error) { + var txid string + var retErr error + + for i := range p.urls { + r, err := p.callHttpStringResult(p.urls[i], "eth_sendRawTransaction", hex) + glog.Infof("eth_sendRawTransaction to %s, txid %s", p.urls[i], r) + // set success return value; or error only if there was no previous success + if err == nil || len(txid) == 0 { + txid = r + retErr = err + } + } + + if p.onlyAlternative && p.fetchMempoolTx { + p.handleMempoolTransaction(txid) + } + + return txid, retErr +} + +// handleMempoolTransaction handles the transaction when using only alternative providers +func (p *AlternativeSendTxProvider) handleMempoolTransaction(txid string) (string, error) { + hash := ethcommon.HexToHash(txid) + raw, err := p.callHttpRawResult(p.urls[0], "eth_getTransactionByHash", hash) + if err != nil || raw == nil { + glog.Errorf("eth_getTransactionByHash from %s returned error %v", p.urls[0], err) + return txid, err + } + + var tx bchain.RpcTransaction + if err := json.Unmarshal(raw, &tx); err != nil { + glog.Errorf("eth_getTransactionByHash from %s unmarshal returned error %v", p.urls[0], err) + return txid, err + } + + p.mempoolTxsMux.Lock() + // remove potential RBF transactions - with equal from and nonce + var rbfTxid string + for rbf, storedTx := range p.mempoolTxs { + if storedTx.tx.From == tx.From && storedTx.tx.AccountNonce == tx.AccountNonce { + rbfTxid = rbf + break + } + } + p.mempoolTxs[txid] = storedTx{tx: &tx, time: uint32(time.Now().Unix())} + p.mempoolTxsMux.Unlock() + + if rbfTxid != "" { + glog.Infof("eth_sendRawTransaction replacing txid %s by %s", rbfTxid, txid) + if p.removeTransactionFromMempool != nil { + p.removeTransactionFromMempool(rbfTxid) + } + } + + if p.mempool != nil { + p.mempool.AddTransactionToMempool(txid) + } + + return txid, nil +} + +// GetTransaction gets a transaction from alternative mempool cache +func (p *AlternativeSendTxProvider) GetTransaction(txid string) (*bchain.RpcTransaction, bool) { + if !p.fetchMempoolTx { + return nil, false + } + + var storedTx storedTx + var found bool + + p.mempoolTxsMux.Lock() + storedTx, found = p.mempoolTxs[txid] + p.mempoolTxsMux.Unlock() + + if found { + if time.Unix(int64(storedTx.time), 0).Before(time.Now().Add(-p.mempoolTxsTimeout)) { + p.mempoolTxsMux.Lock() + delete(p.mempoolTxs, txid) + p.mempoolTxsMux.Unlock() + return nil, false + } + return storedTx.tx, true + } + + return nil, false +} + +// RemoveTransaction removes a transaction from alternative mempool cache +func (p *AlternativeSendTxProvider) RemoveTransaction(txid string) { + if !p.fetchMempoolTx { + return + } + + p.mempoolTxsMux.Lock() + delete(p.mempoolTxs, txid) + p.mempoolTxsMux.Unlock() +} + +// UseOnlyAlternativeProvider returns true if only alternative providers should be used +func (p *AlternativeSendTxProvider) UseOnlyAlternativeProvider() bool { + return p.onlyAlternative +} + +// Helper function for calling ETH RPC over http with parameters. Creates and closes a new client for every call. +func (p *AlternativeSendTxProvider) callHttpRawResult(url string, rpcMethod string, args ...interface{}) (json.RawMessage, error) { + ctx, cancel := context.WithTimeout(context.Background(), p.rpcTimeout) + defer cancel() + client, err := rpc.DialContext(ctx, url) + if err != nil { + return nil, err + } + defer client.Close() + var raw json.RawMessage + err = client.CallContext(ctx, &raw, rpcMethod, args...) + if err != nil { + return nil, err + } else if len(raw) == 0 { + return nil, errors.New(url + " " + rpcMethod + " : failed") + } + return raw, nil +} + +// Helper function for calling ETH RPC over http with parameters and getting string result. Creates and closes a new client for every call. +func (p *AlternativeSendTxProvider) callHttpStringResult(url string, rpcMethod string, args ...interface{}) (string, error) { + raw, err := p.callHttpRawResult(url, rpcMethod, args...) + if err != nil { + return "", err + } + var result string + if err := json.Unmarshal(raw, &result); err != nil { + return "", errors.Annotatef(err, "%s %s raw result %v", url, rpcMethod, raw) + } + if result == "" { + return "", errors.New(url + " " + rpcMethod + " : failed, empty result") + } + return result, nil +} diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 4b7dbbcbc4..b96515b923 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -7,7 +7,6 @@ import ( "io" "math/big" "net/http" - "os" "strconv" "strings" "sync" @@ -81,11 +80,7 @@ type EthereumRPC struct { stakingPoolNames []string stakingPoolContracts []string alternativeFeeProvider alternativeFeeProviderInterface - alternativeSendTxURLs []string - alternativeSendTxOnly bool - alternativeFetchMempoolTx bool - alternativeMempoolTxs map[string]*bchain.RpcTransaction - alternativeMempoolTxsMux sync.Mutex + alternativeSendTxProvider *AlternativeSendTxProvider } // ProcessInternalTransactions specifies if internal transactions are processed @@ -137,16 +132,8 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification if network == "" { network = c.CoinShortcut } - s.alternativeSendTxURLs = strings.Split(os.Getenv(strings.ToUpper(network)+"_ALTERNATIVE_SENDTX_URLS"), ",") - s.alternativeSendTxOnly = strings.ToUpper(os.Getenv(strings.ToUpper(network)+"_ALTERNATIVE_SENDTX_ONLY")) == "TRUE" - s.alternativeFetchMempoolTx = strings.ToUpper(os.Getenv(strings.ToUpper(network)+"_ALTERNATIVE_FETCH_MEMPOOL_TX")) == "TRUE" - if len(s.alternativeSendTxURLs) > 0 { - glog.Infof("Using alternative send transaction providers %v. Only alternative providers %v", s.alternativeSendTxURLs, s.alternativeSendTxOnly) - } - if s.alternativeFetchMempoolTx { - s.alternativeMempoolTxs = make(map[string]*bchain.RpcTransaction) - glog.Infof("Alternative fetch mempool tx %v", s.alternativeFetchMempoolTx) - } + + s.alternativeSendTxProvider = NewAlternativeSendTxProvider(network, c.RPCTimeout, c.MempoolTxTimeoutHours) return s, nil } @@ -218,6 +205,10 @@ func (b *EthereumRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, er if b.Mempool == nil { b.Mempool = bchain.NewMempoolEthereumType(chain, b.ChainConfig.MempoolTxTimeoutHours, b.ChainConfig.QueryBackendOnMempoolResync) glog.Info("mempool created, MempoolTxTimeoutHours=", b.ChainConfig.MempoolTxTimeoutHours, ", QueryBackendOnMempoolResync=", b.ChainConfig.QueryBackendOnMempoolResync, ", DisableMempoolSync=", b.ChainConfig.DisableMempoolSync) + if b.alternativeSendTxProvider != nil { + b.alternativeSendTxProvider.SetupMempool(b.Mempool, b.removeTransactionFromMempool) + } + } return b.Mempool, nil } @@ -886,10 +877,8 @@ func (b *EthereumRPC) removeTransactionFromMempool(txid string) { b.Mempool.RemoveTransactionFromMempool(txid) } // remove tx from mempool txs fetched by alternative method - if b.alternativeFetchMempoolTx { - b.alternativeMempoolTxsMux.Lock() - delete(b.alternativeMempoolTxs, txid) - b.alternativeMempoolTxsMux.Unlock() + if b.alternativeSendTxProvider != nil { + b.alternativeSendTxProvider.RemoveTransaction(txid) } } @@ -901,10 +890,8 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { var txFound bool var err error hash := ethcommon.HexToHash(txid) - if b.alternativeFetchMempoolTx { - b.alternativeMempoolTxsMux.Lock() - tx, txFound = b.alternativeMempoolTxs[txid] - b.alternativeMempoolTxsMux.Unlock() + if b.alternativeSendTxProvider != nil { + tx, txFound = b.alternativeSendTxProvider.GetTransaction(txid) } if !txFound { tx = &bchain.RpcTransaction{} @@ -1132,42 +1119,20 @@ func (b *EthereumRPC) EthereumTypeGetEip1559Fees() (*bchain.Eip1559Fees, error) func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) { var txid string var retErr error - if len(b.alternativeSendTxURLs) > 0 { - for i := range b.alternativeSendTxURLs { - glog.Info("eth_sendRawTransaction to ", b.alternativeSendTxURLs[i]) - r, err := b.callHttpStringResult(b.alternativeSendTxURLs[i], "eth_sendRawTransaction", hex) - // set success return value; or error only if there was no previous success - if err == nil || len(txid) == 0 { - txid = r - retErr = err - } - } - if b.alternativeSendTxOnly && b.alternativeFetchMempoolTx { - hash := ethcommon.HexToHash(txid) - raw, err := b.callHttpRawResult(b.alternativeSendTxURLs[0], "eth_getTransactionByHash", hash) - if err != nil || raw == nil { - glog.Errorf("eth_getTransactionByHash from %s returned error %v", b.alternativeSendTxURLs[0], err) - } else { - var tx bchain.RpcTransaction - if err := json.Unmarshal(raw, &tx); err != nil { - glog.Errorf("eth_getTransactionByHash from %s unmarshal returned error %v", b.alternativeSendTxURLs[0], err) - } - b.alternativeMempoolTxsMux.Lock() - b.alternativeMempoolTxs[txid] = &tx - b.alternativeMempoolTxsMux.Unlock() - b.Mempool.AddTransactionToMempool(txid) - } + + if b.alternativeSendTxProvider != nil { + txid, retErr = b.alternativeSendTxProvider.SendRawTransaction(hex) + if b.alternativeSendTxProvider.UseOnlyAlternativeProvider() { return txid, retErr } } - glog.Info("eth_sendRawTransaction default") + txid, retErr = b.callRpcStringResult("eth_sendRawTransaction", hex) if b.ChainConfig.DisableMempoolSync { // add transactions submitted by us to mempool if sync is disabled b.Mempool.AddTransactionToMempool(txid) } return txid, retErr - } // EthereumTypeGetRawTransaction gets raw transaction in hex format @@ -1175,41 +1140,6 @@ func (b *EthereumRPC) EthereumTypeGetRawTransaction(txid string) (string, error) return b.callRpcStringResult("eth_getRawTransactionByHash", txid) } -// Helper function for calling ETH RPC over http with parameters. Creates and closes a new client for every call. -func (b *EthereumRPC) callHttpRawResult(url string, rpcMethod string, args ...interface{}) (json.RawMessage, error) { - ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) - defer cancel() - client, err := rpc.DialContext(ctx, url) - if err != nil { - return nil, err - } - defer client.Close() - var raw json.RawMessage - err = client.CallContext(ctx, &raw, rpcMethod, args...) - if err != nil { - return nil, err - } else if len(raw) == 0 { - return nil, errors.New(url + " " + rpcMethod + " : failed") - } - return raw, nil -} - -// Helper function for calling ETH RPC over http with parameters and getting string result. Creates and closes a new client for every call. -func (b *EthereumRPC) callHttpStringResult(url string, rpcMethod string, args ...interface{}) (string, error) { - raw, err := b.callHttpRawResult(url, rpcMethod, args...) - if err != nil { - return "", err - } - var result string - if err := json.Unmarshal(raw, &result); err != nil { - return "", errors.Annotatef(err, "%s %s raw result %v", url, rpcMethod, raw) - } - if result == "" { - return "", errors.New(url + " " + rpcMethod + " : failed, empty result") - } - return result, nil -} - // Helper function for calling ETH RPC with parameters and getting string result func (b *EthereumRPC) callRpcStringResult(rpcMethod string, args ...interface{}) (string, error) { ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) From e98b435128875a76bdb4ff5708a75c1b186f66f2 Mon Sep 17 00:00:00 2001 From: CPUchain Date: Fri, 23 May 2025 05:12:28 +0000 Subject: [PATCH 478/530] Fix for windows support --- db/dboptions.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/db/dboptions.go b/db/dboptions.go index 90f4b02eb2..47f8df55fc 100644 --- a/db/dboptions.go +++ b/db/dboptions.go @@ -2,6 +2,7 @@ package db // #include "rocksdb/c.h" import "C" +import "flag" import "github.com/linxGnu/grocksdb" /* @@ -38,6 +39,10 @@ func boolToChar(b bool) C.uchar { } */ +var ( + noCompression = flag.Bool("noCompression", false, "disable rocksdb compression when rocksdb library can't find compression library linked with binary") +) + func createAndSetDBOptions(bloomBits int, c *grocksdb.Cache, maxOpenFiles int) *grocksdb.Options { blockOpts := grocksdb.NewDefaultBlockBasedTableOptions() blockOpts.SetBlockSize(32 << 10) // 32kB @@ -57,6 +62,11 @@ func createAndSetDBOptions(bloomBits int, c *grocksdb.Cache, maxOpenFiles int) * opts.SetWriteBufferSize(1 << 27) // 128MB opts.SetMaxBytesForLevelBase(1 << 27) // 128MB opts.SetMaxOpenFiles(maxOpenFiles) - opts.SetCompression(grocksdb.LZ4HCCompression) + if *noCompression { + // resolve error rocksDB: Invalid argument: Compression type LZ4HC is not linked with the binary + opts.SetCompression(grocksdb.NoCompression) + } else { + opts.SetCompression(grocksdb.LZ4HCCompression) + } return opts } From 6cfc6fa04fb5c7548393a5862ffe967838489eed Mon Sep 17 00:00:00 2001 From: JoHnY Date: Mon, 9 Jun 2025 12:51:18 +0200 Subject: [PATCH 479/530] changed infura periods --- configs/coins/arbitrum_archive.json | 2 +- configs/coins/base_archive.json | 2 +- configs/coins/ethereum_archive.json | 2 +- configs/coins/ethereum_testnet_holesky_archive.json | 3 --- configs/coins/optimism_archive.json | 2 +- configs/coins/polygon_archive.json | 2 +- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/configs/coins/arbitrum_archive.json b/configs/coins/arbitrum_archive.json index e34daf1a21..09d0a5af9d 100644 --- a/configs/coins/arbitrum_archive.json +++ b/configs/coins/arbitrum_archive.json @@ -53,7 +53,7 @@ "address_aliases": true, "eip1559Fees": true, "alternative_estimate_fee": "infura", - "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/42161/suggestedGasFees\", \"periodSeconds\": 8}", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/42161/suggestedGasFees\", \"periodSeconds\": 16}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, diff --git a/configs/coins/base_archive.json b/configs/coins/base_archive.json index f4f6005340..57a1805754 100644 --- a/configs/coins/base_archive.json +++ b/configs/coins/base_archive.json @@ -55,7 +55,7 @@ "address_aliases": true, "eip1559Fees": true, "alternative_estimate_fee": "infura", - "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/8453/suggestedGasFees\", \"periodSeconds\": 4}", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/8453/suggestedGasFees\", \"periodSeconds\": 8}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index beae15209e..19c894be08 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -61,7 +61,7 @@ "address_aliases": true, "eip1559Fees": true, "alternative_estimate_fee": "infura", - "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/1/suggestedGasFees\", \"periodSeconds\": 4}", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/1/suggestedGasFees\", \"periodSeconds\": 8}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 11d144aaf9..851aa28e99 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -60,9 +60,6 @@ "additional_params": { "consensusNodeVersion": "http://localhost:17536/eth/v1/node/version", "address_aliases": true, - "eip1559Fees": true, - "alternative_estimate_fee": "infura", - "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/17000/suggestedGasFees\", \"periodSeconds\": 60}", "mempoolTxTimeoutHours": 12, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, diff --git a/configs/coins/optimism_archive.json b/configs/coins/optimism_archive.json index 9719efcca5..3ae0e9d9d9 100644 --- a/configs/coins/optimism_archive.json +++ b/configs/coins/optimism_archive.json @@ -55,7 +55,7 @@ "address_aliases": true, "eip1559Fees": true, "alternative_estimate_fee": "infura", - "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/10/suggestedGasFees\", \"periodSeconds\": 10}", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/10/suggestedGasFees\", \"periodSeconds\": 20}", "processInternalTransactions": true, "queryBackendOnMempoolResync": false, "fiat_rates": "coingecko", diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index 0c8f4e4793..5deb81301d 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -60,7 +60,7 @@ "address_aliases": true, "eip1559Fees": true, "alternative_estimate_fee": "infura", - "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/137/suggestedGasFees\", \"periodSeconds\": 4}", + "alternative_estimate_fee_params": "{\"url\": \"https://gas.api.infura.io/v3/${api_key}/networks/137/suggestedGasFees\", \"periodSeconds\": 8}", "mempoolTxTimeoutHours": 48, "processInternalTransactions": true, "queryBackendOnMempoolResync": false, From bc43907e54be43427d39e95e3692891f7b681942 Mon Sep 17 00:00:00 2001 From: f7b Date: Fri, 6 Jun 2025 11:45:26 +0200 Subject: [PATCH 480/530] prysm (+testnets) 6.0.3 -> 6.0.4 --- configs/coins/ethereum_archive_consensus.json | 10 +++++----- configs/coins/ethereum_consensus.json | 10 +++++----- .../ethereum_testnet_holesky_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_consensus.json | 10 +++++----- .../ethereum_testnet_sepolia_archive_consensus.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_consensus.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index c08434a6aa..d3b1e20ef5 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.2", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", + "version": "6.0.4", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", "verification_type": "sha256", - "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", + "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", - "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", + "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" } } }, diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index bb5c5009ce..79fc973e06 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.2", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", + "version": "6.0.4", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", "verification_type": "sha256", - "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", + "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", - "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", + "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive_consensus.json b/configs/coins/ethereum_testnet_holesky_archive_consensus.json index 3870519530..5d92537bbd 100644 --- a/configs/coins/ethereum_testnet_holesky_archive_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.2", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", + "version": "6.0.4", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", "verification_type": "sha256", - "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", + "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13636 --p2p-udp-port=12636 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", - "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", + "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_consensus.json b/configs/coins/ethereum_testnet_holesky_consensus.json index 0abeb4eb2d..36ea18def5 100644 --- a/configs/coins/ethereum_testnet_holesky_consensus.json +++ b/configs/coins/ethereum_testnet_holesky_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.2", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", + "version": "6.0.4", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", "verification_type": "sha256", - "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", + "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", - "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", + "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json index c874cdbaff..57f28b4114 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.2", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", + "version": "6.0.4", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", "verification_type": "sha256", - "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", + "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", - "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", + "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json index 692f9f2e2c..54de2069b3 100644 --- a/configs/coins/ethereum_testnet_sepolia_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.2", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-amd64", + "version": "6.0.4", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", "verification_type": "sha256", - "verification_source": "043e7f2e319569b6e59edaaeeb4cb36e3c4c070f7f1cd8629c8da39ad23e3193", + "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.2/beacon-chain-v6.0.2-linux-arm64", - "verification_source": "15504e2e8548d7b84913d32e1dce1ed578e0dfc36e374a21b4076200a998d7f1" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", + "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" } } }, From 8989ab75c240ae569f83622038820260bc79afbc Mon Sep 17 00:00:00 2001 From: f7b Date: Thu, 5 Jun 2025 12:19:48 +0200 Subject: [PATCH 481/530] eth (+testnets) 3.0.4 -> 3.0.5 --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_holesky.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_archive.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 7ca3241d83..4a6491e0f7 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.4", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", + "version": "3.0.5", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", + "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", - "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", + "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 19c894be08..a8809405c7 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.4", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", + "version": "3.0.5", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", + "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", - "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", + "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" } } }, diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index d95223de2a..9da4cb0a94 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.4", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", + "version": "3.0.5", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", + "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", - "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", + "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 851aa28e99..293a310157 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.4", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", + "version": "3.0.5", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", + "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", - "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", + "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index cd547195e5..aa440e7997 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.4", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", + "version": "3.0.5", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", + "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", - "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", + "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 4d8cceb7d1..1741d362c2 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.4", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_amd64.tar.gz", + "version": "3.0.5", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "1401c78aadf04f6f7e06a514f43a2636a2c668fded926b74446f791194cbb54c", + "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.4/erigon_v3.0.4_linux_arm64.tar.gz", - "verification_source": "deed38eb4dff0eb6f5e4e20ca1aaf3906133fa5073865c125dac9ff408125ecf" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", + "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" } } }, From ae0172dddfdf7a554937429ea635395be5ec34c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Musil?= Date: Mon, 9 Jun 2025 14:09:15 +0200 Subject: [PATCH 482/530] Add longTermFeeRate websocket endpoint (#1262) * feat: add longTermFeeRate websocket endpoint * chore: regenerate blockbook-api.ts with longTermFeeRate --- api/types.go | 5 ++++ bchain/basechain.go | 5 ++++ bchain/coins/blockchain.go | 5 ++++ bchain/coins/btc/bitcoinrpc.go | 15 ++++++++++++ bchain/types.go | 7 ++++++ blockbook-api.ts | 6 +++++ build/tools/typescriptify/typescriptify.go | 1 + server/websocket.go | 14 +++++++++++ server/ws_types.go | 6 +++++ static/test-websocket.html | 27 ++++++++++++++++++++++ 10 files changed, 91 insertions(+) diff --git a/api/types.go b/api/types.go index a126eb6710..3a0fe913d2 100644 --- a/api/types.go +++ b/api/types.go @@ -620,3 +620,8 @@ type Eip1559Fees struct { PriorityFeeTrend string `json:"priorityFeeTrend,omitempty" ts_type:"'up' | 'down'"` BaseFeeTrend string `json:"baseFeeTrend,omitempty" ts_type:"'up' | 'down'"` } + +type LongTermFeeRate struct { + FeePerUnit string `json:"feePerUnit" ts_doc:"Long term fee rate (in sat/kByte)."` + Blocks uint64 `json:"blocks" ts_doc:"Amount of blocks used for the long term fee rate estimation."` +} diff --git a/bchain/basechain.go b/bchain/basechain.go index 2d7cdd2118..7e34c988ca 100644 --- a/bchain/basechain.go +++ b/bchain/basechain.go @@ -39,6 +39,11 @@ func (b *BaseChain) GetMempoolEntry(txid string) (*MempoolEntry, error) { return nil, errors.New("GetMempoolEntry: not supported") } +// LongTermFeeRate returns smallest fee rate from historic blocks. +func (b *BaseChain) LongTermFeeRate() (*LongTermFeeRate, error) { + return nil, errors.New("not supported") +} + // EthereumTypeGetBalance is not supported func (b *BaseChain) EthereumTypeGetBalance(addrDesc AddressDescriptor) (*big.Int, error) { return nil, errors.New("not supported") diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index f4704919f8..d004896f52 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -297,6 +297,11 @@ func (c *blockChainWithMetrics) EstimateFee(blocks int) (v big.Int, err error) { return c.b.EstimateFee(blocks) } +func (c *blockChainWithMetrics) LongTermFeeRate() (v *bchain.LongTermFeeRate, err error) { + defer func(s time.Time) { c.observeRPCLatency("LongTermFeeRate", s, err) }(time.Now()) + return c.b.LongTermFeeRate() +} + func (c *blockChainWithMetrics) SendRawTransaction(tx string) (v string, err error) { defer func(s time.Time) { c.observeRPCLatency("SendRawTransaction", s, err) }(time.Now()) return c.b.SendRawTransaction(tx) diff --git a/bchain/coins/btc/bitcoinrpc.go b/bchain/coins/btc/bitcoinrpc.go index 00b3f468ea..91bdee9793 100644 --- a/bchain/coins/btc/bitcoinrpc.go +++ b/bchain/coins/btc/bitcoinrpc.go @@ -873,6 +873,21 @@ func (b *BitcoinRPC) EstimateFee(blocks int) (big.Int, error) { return r, nil } +// LongTermFeeRate returns smallest fee rate from historic blocks. +func (b *BitcoinRPC) LongTermFeeRate() (*bchain.LongTermFeeRate, error) { + blocks := 1008 // ~7 days of blocks, highest number estimatesmartfee supports + glog.V(1).Info("rpc: estimatesmartfee (long term fee rate) - ", blocks) + // Going for the ECONOMICAL mode, to get the lowest fee rate + feePerUnit, err := b.blockchainEstimateSmartFee(blocks, false) + if err != nil { + return nil, err + } + return &bchain.LongTermFeeRate{ + Blocks: uint64(blocks), + FeePerUnit: feePerUnit, + }, nil +} + // SendRawTransaction sends raw transaction func (b *BitcoinRPC) SendRawTransaction(tx string) (string, error) { glog.V(1).Info("rpc: sendrawtransaction") diff --git a/bchain/types.go b/bchain/types.go index 40e54d6e87..e6c2d12606 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -211,6 +211,12 @@ type ChainInfo struct { Consensus interface{} `json:"consensus,omitempty" ts_doc:"Additional consensus details, structure depends on chain."` } +// LongTermFeeRate gets information about the fee rate over longer period of time. +type LongTermFeeRate struct { + FeePerUnit big.Int `json:"feePerUnit" ts_doc:"Long term fee rate (in sat/kByte)."` + Blocks uint64 `json:"blocks" ts_doc:"Amount of blocks used for the long term fee rate estimation."` +} + // RPCError defines rpc error returned by backend type RPCError struct { Code int `json:"code" ts_doc:"Error code returned by the backend RPC."` @@ -324,6 +330,7 @@ type BlockChain interface { GetTransactionSpecific(tx *Tx) (json.RawMessage, error) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) EstimateFee(blocks int) (big.Int, error) + LongTermFeeRate() (*LongTermFeeRate, error) SendRawTransaction(tx string) (string, error) GetMempoolEntry(txid string) (*MempoolEntry, error) GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error) diff --git a/blockbook-api.ts b/blockbook-api.ts index 133d0d29a3..a327e8d104 100644 --- a/blockbook-api.ts +++ b/blockbook-api.ts @@ -745,6 +745,12 @@ export interface WsEstimateFeeRes { feeLimit?: string; eip1559?: Eip1559Fees; } +export interface WsLongTermFeeRateRes { + /** Long term fee rate (in sat/kByte). */ + feePerUnit: string; + /** Amount of blocks used for the long term fee rate estimation. */ + blocks: number; +} export interface WsSendTransactionReq { /** Hex-encoded transaction data to broadcast. */ hex: string; diff --git a/build/tools/typescriptify/typescriptify.go b/build/tools/typescriptify/typescriptify.go index 8ee0563e31..731c8ff230 100644 --- a/build/tools/typescriptify/typescriptify.go +++ b/build/tools/typescriptify/typescriptify.go @@ -54,6 +54,7 @@ func main() { t.Add(server.WsTransactionSpecificReq{}) t.Add(server.WsEstimateFeeReq{}) t.Add(server.WsEstimateFeeRes{}) + t.Add(server.WsLongTermFeeRateRes{}) t.Add(server.WsSendTransactionReq{}) t.Add(server.WsSubscribeAddressesReq{}) t.Add(server.WsSubscribeFiatRatesReq{}) diff --git a/server/websocket.go b/server/websocket.go index 3dc30a5464..74619a8944 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -369,6 +369,9 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *WsRe "estimateFee": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) { return s.estimateFee(req.Params) }, + "longTermFeeRate": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) { + return s.longTermFeeRate() + }, "sendTransaction": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) { r := WsSendTransactionReq{} err = json.Unmarshal(req.Params, &r) @@ -737,6 +740,17 @@ func (s *WebsocketServer) estimateFee(params []byte) (interface{}, error) { return res, nil } +func (s *WebsocketServer) longTermFeeRate() (res interface{}, err error) { + feeRate, err := s.chain.LongTermFeeRate() + if err != nil { + return nil, err + } + return WsLongTermFeeRateRes{ + FeePerUnit: feeRate.FeePerUnit.String(), + Blocks: feeRate.Blocks, + }, nil +} + func (s *WebsocketServer) sendTransaction(tx string) (res resultSendTransaction, err error) { txid, err := s.chain.SendRawTransaction(tx) if err != nil { diff --git a/server/ws_types.go b/server/ws_types.go index 6732b4ead9..5add2356c0 100644 --- a/server/ws_types.go +++ b/server/ws_types.go @@ -133,6 +133,12 @@ type WsEstimateFeeRes struct { Eip1559 *api.Eip1559Fees `json:"eip1559,omitempty"` } +// WsLongTermFeeRateRes is returned in response to a long term fee rate request. +type WsLongTermFeeRateRes struct { + FeePerUnit string `json:"feePerUnit" ts_doc:"Long term fee rate (in sat/kByte)."` + Blocks uint64 `json:"blocks" ts_doc:"Amount of blocks used for the long term fee rate estimation."` +} + // WsSendTransactionReq is used to broadcast a transaction to the network. type WsSendTransactionReq struct { Hex string `json:"hex" ts_doc:"Hex-encoded transaction data to broadcast."` diff --git a/static/test-websocket.html b/static/test-websocket.html index e4e2b22bf0..438b5f6f4c 100644 --- a/static/test-websocket.html +++ b/static/test-websocket.html @@ -289,6 +289,19 @@ } } + function longTermFeeRate() { + try { + const method = 'longTermFeeRate'; + send(method, {}, function (result) { + document.getElementById('longTermFeeRateResult').innerText = JSON.stringify( + result, + ).replace(/,/g, ', '); + }); + } catch (e) { + document.getElementById('longTermFeeRateResult').innerText = e; + } + } + function sendTransaction() { var hex = document.getElementById('sendTransactionHex').value.trim(); const method = 'sendTransaction'; @@ -892,6 +905,20 @@

Blockbook Websocket Test Page

+
+
+ +
+
+
+
+
+
Date: Fri, 13 Jun 2025 15:32:32 +0200 Subject: [PATCH 483/530] Fix ETH contract cmETH parameters --- configs/contract-fix/ethereum.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/contract-fix/ethereum.json b/configs/contract-fix/ethereum.json index 767f09681e..1580329a18 100644 --- a/configs/contract-fix/ethereum.json +++ b/configs/contract-fix/ethereum.json @@ -1 +1 @@ -[{"standard":"ERC20","contract":"0xC19B6A4Ac7C7Cc24459F08984Bbd09664af17bD1","name":"Sensorium","symbol":"SENSO","decimals":0,"createdInBlock":11098997},{"standard":"ERC20","contract":"0xd5F7838F5C461fefF7FE49ea5ebaF7728bB0ADfa","name":"mETH","symbol":"mETH","decimals":18,"createdInBlock":18290587}] +[{"standard":"ERC20","contract":"0xC19B6A4Ac7C7Cc24459F08984Bbd09664af17bD1","name":"Sensorium","symbol":"SENSO","decimals":0,"createdInBlock":11098997},{"standard":"ERC20","contract":"0xd5F7838F5C461fefF7FE49ea5ebaF7728bB0ADfa","name":"mETH","symbol":"mETH","decimals":18,"createdInBlock":18290587},{"standard":"ERC20","contract":"0xE6829d9a7eE3040e1276Fa75293Bde931859e8fA","name":"cmETH","symbol":"cmETH","decimals":18,"createdInBlock":20439180}] From 0f90ea11ba0b73a9d6c585e6caa3696b63ae6c00 Mon Sep 17 00:00:00 2001 From: f7b Date: Tue, 17 Jun 2025 15:22:53 +0200 Subject: [PATCH 484/530] polygon-bor 2.0.3 -> 2.1.1 --- configs/coins/polygon.json | 12 ++++++------ configs/coins/polygon_archive.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/polygon.json b/configs/coins/polygon.json index d900742ed1..902cf7fe5f 100644 --- a/configs/coins/polygon.json +++ b/configs/coins/polygon.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "2.0.3", - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.3/bor-v2.0.3-amd64.deb", + "version": "2.1.1", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.1.1/bor-v2.1.1-amd64.deb", "verification_type": "sha256", - "verification_source": "bf0a1316411dfa70decc7a12e1d46c0690e7f3a25d78d59a63721d0a271da183", + "verification_source": "73f03aaccbc0c7a85024db4219449d29c929646047ca4f2ef53bff3c1b4cd990", "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.3/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.1.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.3/bor-v2.0.3-arm64.deb", - "verification_source": "0d8ed9eba551cd242ac2616c8c531969f58655e951403b374810a227d0c5b6d6" + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.1.1/bor-v2.1.1-arm64.deb", + "verification_source": "481c564a8f094f5fe7981ff848a1b53c9ef27dab8acb3ecfee149eab3e750f2a" } } }, diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index 5deb81301d..7c3ce3678d 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-archive-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "2.0.3", - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.3/bor-v2.0.3-amd64.deb", + "version": "2.1.1", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.1.1/bor-v2.1.1-amd64.deb", "verification_type": "sha256", - "verification_source": "bf0a1316411dfa70decc7a12e1d46c0690e7f3a25d78d59a63721d0a271da183", + "verification_source": "73f03aaccbc0c7a85024db4219449d29c929646047ca4f2ef53bff3c1b4cd990", "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.0.3/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.1.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.0.3/bor-v2.0.3-arm64.deb", - "verification_source": "0d8ed9eba551cd242ac2616c8c531969f58655e951403b374810a227d0c5b6d6" + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.1.1/bor-v2.1.1-arm64.deb", + "verification_source": "481c564a8f094f5fe7981ff848a1b53c9ef27dab8acb3ecfee149eab3e750f2a" } } }, From 27cf2ff31a23125b5c94ed7c2235cbc727e0a555 Mon Sep 17 00:00:00 2001 From: f7b Date: Tue, 24 Jun 2025 08:54:11 +0200 Subject: [PATCH 485/530] eth (+testnets) 3.0.5 -> 3.0.8 --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_holesky.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_archive.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 4a6491e0f7..e60e6af1a9 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.5", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", + "version": "3.0.8", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", + "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", - "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", + "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index a8809405c7..bffc36d892 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.5", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", + "version": "3.0.8", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", + "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", - "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", + "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" } } }, diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index 9da4cb0a94..298114f782 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.5", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", + "version": "3.0.8", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", + "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", - "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", + "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 293a310157..acd1211bdb 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.5", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", + "version": "3.0.8", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", + "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", - "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", + "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index aa440e7997..805b36e890 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.5", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", + "version": "3.0.8", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", + "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", - "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", + "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 1741d362c2..d09ce0bf7a 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.5", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_amd64.tar.gz", + "version": "3.0.8", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "64e6bcad4de63dc24c45dd308180238c78972bfc9553bcbe2724c0d4c0de0074", + "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.5/erigon_v3.0.5_linux_arm64.tar.gz", - "verification_source": "741d327a6620c44225d36def78629b8cac5427d7bc2ff0bb20e97191c94e61e4" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", + "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" } } }, From 24e34590f0f34d24f52eacaeba29b9528d024cd7 Mon Sep 17 00:00:00 2001 From: cranycrane Date: Mon, 16 Jun 2025 06:30:52 +0000 Subject: [PATCH 486/530] rocksdb v7.5.3 -> rocksdb v9.10.0 --- docs/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build.md b/docs/build.md index e9f0b14c72..9d39d3f1fa 100644 --- a/docs/build.md +++ b/docs/build.md @@ -209,7 +209,7 @@ sudo apt-get update && sudo apt-get install -y \ build-essential git wget pkg-config libzmq3-dev libgflags-dev libsnappy-dev zlib1g-dev libzstd-dev libbz2-dev liblz4-dev git clone https://github.com/facebook/rocksdb.git cd rocksdb -git checkout v7.5.3 +git checkout v9.10.0 CFLAGS=-fPIC CXXFLAGS=-fPIC make release ``` From d36714720b585458fffbb8990a26358124e3cb7e Mon Sep 17 00:00:00 2001 From: justanwar <42809091+justanwar@users.noreply.github.com> Date: Sat, 21 Jun 2025 18:28:36 +0800 Subject: [PATCH 487/530] Update Firo daemon 0.14.14.1 (mandatory) --- configs/coins/firo.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/firo.json b/configs/coins/firo.json index 14a0f65b97..41d0ebe335 100644 --- a/configs/coins/firo.json +++ b/configs/coins/firo.json @@ -22,10 +22,10 @@ "package_name": "backend-firo", "package_revision": "satoshilabs-1", "system_user": "firo", - "version": "0.14.14.0", - "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.14.0/firo-0.14.14.0-linux64.tar.gz", + "version": "0.14.14.1", + "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.14.1/firo-0.14.14.1-linux64.tar.gz", "verification_type": "sha256", - "verification_source": "0f8c914286031830d8c9eb1ab86b3e21f349917aea7bc2ab12229ab4c638cbe8", + "verification_source": "059da5f978bbda1615fdbd1a16a0e854c4c6a15480b2d50de4a8911f6f5b4636", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/firo-qt", From ca17fc5a956a8202730c91696ed75fd1a1e49138 Mon Sep 17 00:00:00 2001 From: highcloudwind Date: Tue, 1 Apr 2025 21:30:30 +0800 Subject: [PATCH 488/530] chore: fix some function names in comment Signed-off-by: highcloudwind --- fiat/coingecko.go | 2 +- server/websocket.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fiat/coingecko.go b/fiat/coingecko.go index 68c7722cd9..7e79dd4222 100644 --- a/fiat/coingecko.go +++ b/fiat/coingecko.go @@ -377,7 +377,7 @@ func (cg *Coingecko) HourlyTickers() (*[]common.CurrencyRatesTicker, error) { return cg.getHighGranularityTickers("90") } -// HourlyTickers returns the array of the exchange rates in five minutes granularity +// FiveMinutesTickers returns the array of the exchange rates in five minutes granularity func (cg *Coingecko) FiveMinutesTickers() (*[]common.CurrencyRatesTicker, error) { return cg.getHighGranularityTickers("1") } diff --git a/server/websocket.go b/server/websocket.go index 74619a8944..cdaa340c12 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -900,7 +900,7 @@ func (s *WebsocketServer) unmarshalAddresses(params []byte) ([]string, error) { return rv, nil } -// unsubscribe addresses without addressSubscriptionsLock - can be called only from subscribeAddresses and unsubscribeAddresses +// doUnsubscribeAddresses addresses without addressSubscriptionsLock - can be called only from subscribeAddresses and unsubscribeAddresses func (s *WebsocketServer) doUnsubscribeAddresses(c *websocketChannel) { for _, ads := range c.addrDescs { sa, e := s.addressSubscriptions[ads] @@ -945,7 +945,7 @@ func (s *WebsocketServer) unsubscribeAddresses(c *websocketChannel) (res interfa return &subscriptionResponse{false}, nil } -// unsubscribe fiat rates without fiatRatesSubscriptionsLock - can be called only from subscribeFiatRates and unsubscribeFiatRates +// doUnsubscribeFiatRates fiat rates without fiatRatesSubscriptionsLock - can be called only from subscribeFiatRates and unsubscribeFiatRates func (s *WebsocketServer) doUnsubscribeFiatRates(c *websocketChannel) { for fr, sa := range s.fiatRatesSubscriptions { for sc := range sa { From 775cb4c04b18f96c6ff1ab53883b07c1216aa498 Mon Sep 17 00:00:00 2001 From: f7b Date: Thu, 3 Jul 2025 16:08:46 +0200 Subject: [PATCH 489/530] =?UTF-8?q?polygon-heimdall=201.2.2=20=E2=86=92=20?= =?UTF-8?q?1.6.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/polygon_heimdall.json | 12 ++++++------ configs/coins/polygon_heimdall_archive.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/polygon_heimdall.json b/configs/coins/polygon_heimdall.json index 9b723e82da..a7444c8882 100644 --- a/configs/coins/polygon_heimdall.json +++ b/configs/coins/polygon_heimdall.json @@ -16,16 +16,16 @@ "package_name": "backend-polygon-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.2.2", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.2.tar.gz", + "version": "1.6.0", + "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.6.0.tar.gz", "verification_type": "sha256", - "verification_source": "ada133877c9a3e18dd2f7ad24c66f7110013e1c59489ad76cf78af53ddc518b2", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.2.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "8cf23d79724d29b38565ea0ca6574efb62ab6b929450ed38ae2ef785679adc39", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.6.0.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.2/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.6.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -37,4 +37,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/polygon_heimdall_archive.json b/configs/coins/polygon_heimdall_archive.json index 8f93492b76..ce2c39df75 100644 --- a/configs/coins/polygon_heimdall_archive.json +++ b/configs/coins/polygon_heimdall_archive.json @@ -16,16 +16,16 @@ "package_name": "backend-polygon-archive-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.2.2", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.2.2.tar.gz", + "version": "1.6.0", + "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.6.0.tar.gz", "verification_type": "sha256", - "verification_source": "ada133877c9a3e18dd2f7ad24c66f7110013e1c59489ad76cf78af53ddc518b2", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.2.2.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "8cf23d79724d29b38565ea0ca6574efb62ab6b929450ed38ae2ef785679adc39", + "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.6.0.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.2.2/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.6.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -37,4 +37,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file From 304d596428bb3d8139388cb26848006572f10758 Mon Sep 17 00:00:00 2001 From: f7b Date: Fri, 11 Jul 2025 07:59:54 +0200 Subject: [PATCH 490/530] polygon-bor 2.1.1 -> 2.2.8 --- configs/coins/polygon.json | 12 ++++++------ configs/coins/polygon_archive.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/polygon.json b/configs/coins/polygon.json index 902cf7fe5f..146a7b61a5 100644 --- a/configs/coins/polygon.json +++ b/configs/coins/polygon.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "2.1.1", - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.1.1/bor-v2.1.1-amd64.deb", + "version": "2.2.8", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.8/bor-v2.2.8-amd64.deb", "verification_type": "sha256", - "verification_source": "73f03aaccbc0c7a85024db4219449d29c929646047ca4f2ef53bff3c1b4cd990", + "verification_source": "f24a2ab7ee5b1eb2ec1d98549c08e7e117cb33f34049e645238e1348292fcc90", "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.1.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.2.8/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.1.1/bor-v2.1.1-arm64.deb", - "verification_source": "481c564a8f094f5fe7981ff848a1b53c9ef27dab8acb3ecfee149eab3e750f2a" + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.8/bor-v2.2.8-arm64.deb", + "verification_source": "72f97c6bc88f2a38a4cfa27a8e7aee240d77250096a8c4650fe654fb0b170008" } } }, diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index 7c3ce3678d..a57ea94942 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-archive-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "2.1.1", - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.1.1/bor-v2.1.1-amd64.deb", + "version": "2.2.8", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.8/bor-v2.2.8-amd64.deb", "verification_type": "sha256", - "verification_source": "73f03aaccbc0c7a85024db4219449d29c929646047ca4f2ef53bff3c1b4cd990", + "verification_source": "f24a2ab7ee5b1eb2ec1d98549c08e7e117cb33f34049e645238e1348292fcc90", "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.1.1/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.2.8/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.1.1/bor-v2.1.1-arm64.deb", - "verification_source": "481c564a8f094f5fe7981ff848a1b53c9ef27dab8acb3ecfee149eab3e750f2a" + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.8/bor-v2.2.8-arm64.deb", + "verification_source": "72f97c6bc88f2a38a4cfa27a8e7aee240d77250096a8c4650fe654fb0b170008" } } }, From 50310a7a9b6f58d04eb6832286b48e7557d66cb7 Mon Sep 17 00:00:00 2001 From: f7b Date: Wed, 2 Jul 2025 09:52:07 +0200 Subject: [PATCH 491/530] eth (+testnets) 3.0.8 -> 3.0.11 --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_holesky.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_archive.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index e60e6af1a9..76cfa2c25c 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.8", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", + "version": "3.0.11", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", + "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", - "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", + "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index bffc36d892..acc78e048c 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.8", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", + "version": "3.0.11", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", + "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", - "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", + "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" } } }, diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index 298114f782..df848c452a 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.8", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", + "version": "3.0.11", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", + "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", - "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", + "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index acd1211bdb..07d36ed745 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.8", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", + "version": "3.0.11", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", + "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", - "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", + "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 805b36e890..af93256c10 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.8", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", + "version": "3.0.11", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", + "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", - "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", + "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index d09ce0bf7a..9440bf053e 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.8", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_amd64.tar.gz", + "version": "3.0.11", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "0bf4d6eea0054017360eaddf6ac4b4a25ebeede8df1e89dfaddab207558394fb", + "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.8/erigon_v3.0.8_linux_arm64.tar.gz", - "verification_source": "4a2505601cbd1877a6a70302153cbd33a3729708db1cc56ce601a797a533def1" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", + "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" } } }, From 17fa06fed98b532fa7905e71d4f5e60ce13a7fe5 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 27 Jun 2025 15:54:51 +0200 Subject: [PATCH 492/530] =?UTF-8?q?avalanche=201.13.0=20=E2=86=92=201.13.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/avalanche.json | 12 ++++++------ configs/coins/avalanche_archive.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/avalanche.json b/configs/coins/avalanche.json index 97ce0d7c77..7a1c8b723d 100644 --- a/configs/coins/avalanche.json +++ b/configs/coins/avalanche.json @@ -19,13 +19,13 @@ "package_name": "backend-avalanche", "package_revision": "satoshilabs-1", "system_user": "avalanche", - "version": "1.13.0", - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-amd64-v1.13.0.tar.gz", + "version": "1.13.2", + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.2/avalanchego-linux-amd64-v1.13.2.tar.gz", "verification_type": "gpg", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-amd64-v1.13.0.tar.gz.sig", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.2/avalanchego-linux-amd64-v1.13.2.tar.gz.sig", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMEtmUT09IgogIH0KfQ==", + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMHNDaUFnSW5OMFlYUmxMWE41Ym1NdFpXNWhZbXhsWkNJNklHWmhiSE5sQ24wPSIKICB9Cn0=", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-arm64-v1.13.0.tar.gz", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-arm64-v1.13.0.tar.gz.sig" + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.2/avalanchego-linux-arm64-v1.13.2.tar.gz", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.2/avalanchego-linux-arm64-v1.13.2.tar.gz.sig" } } }, diff --git a/configs/coins/avalanche_archive.json b/configs/coins/avalanche_archive.json index 10ee49457d..7f7b7c5b67 100644 --- a/configs/coins/avalanche_archive.json +++ b/configs/coins/avalanche_archive.json @@ -19,13 +19,13 @@ "package_name": "backend-avalanche-archive", "package_revision": "satoshilabs-1", "system_user": "avalanche", - "version": "1.13.0", - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-amd64-v1.13.0.tar.gz", + "version": "1.13.2", + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.2/avalanchego-linux-amd64-v1.13.2.tar.gz", "verification_type": "gpg", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-amd64-v1.13.0.tar.gz.sig", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.2/avalanchego-linux-amd64-v1.13.2.tar.gz.sig", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [], - "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMHNDaUFnSW5CeWRXNXBibWN0Wlc1aFlteGxaQ0k2Wm1Gc2MyVUtmUT09IgogIH0KfQ==", + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMHNDaUFnSW5CeWRXNXBibWN0Wlc1aFlteGxaQ0k2Wm1Gc2MyVXNDaUFnSW5OMFlYUmxMWE41Ym1NdFpXNWhZbXhsWkNJNklHWmhiSE5sQ24wPSIKICB9Cn0=", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", "postinst_script_template": "", "service_type": "simple", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-arm64-v1.13.0.tar.gz", - "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.0/avalanchego-linux-arm64-v1.13.0.tar.gz.sig" + "binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.2/avalanchego-linux-arm64-v1.13.2.tar.gz", + "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.13.2/avalanchego-linux-arm64-v1.13.2.tar.gz.sig" } } }, From ebc34c7a16e4d54e0bfe7d21ad9ab497104b6d9a Mon Sep 17 00:00:00 2001 From: f7b Date: Thu, 17 Jul 2025 13:27:10 +0200 Subject: [PATCH 493/530] polygon-bor 2.2.8 -> 2.2.9 --- configs/coins/polygon.json | 12 ++++++------ configs/coins/polygon_archive.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/coins/polygon.json b/configs/coins/polygon.json index 146a7b61a5..a9552c19f7 100644 --- a/configs/coins/polygon.json +++ b/configs/coins/polygon.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "2.2.8", - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.8/bor-v2.2.8-amd64.deb", + "version": "2.2.9", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.9/bor-v2.2.9-amd64.deb", "verification_type": "sha256", - "verification_source": "f24a2ab7ee5b1eb2ec1d98549c08e7e117cb33f34049e645238e1348292fcc90", + "verification_source": "8125ae8f2c5e2485ba112e065bcbfa40468a113a41a3dfa34871dd239fd12f6e", "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.2.8/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.2.9/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.8/bor-v2.2.8-arm64.deb", - "verification_source": "72f97c6bc88f2a38a4cfa27a8e7aee240d77250096a8c4650fe654fb0b170008" + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.9/bor-v2.2.9-arm64.deb", + "verification_source": "344bbd01a230250a43373ee559cb596bc8afb95026ce4aa9652c46077740414f" } } }, diff --git a/configs/coins/polygon_archive.json b/configs/coins/polygon_archive.json index a57ea94942..bfd19f9404 100644 --- a/configs/coins/polygon_archive.json +++ b/configs/coins/polygon_archive.json @@ -21,16 +21,16 @@ "package_name": "backend-polygon-archive-bor", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "2.2.8", - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.8/bor-v2.2.8-amd64.deb", + "version": "2.2.9", + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.9/bor-v2.2.9-amd64.deb", "verification_type": "sha256", - "verification_source": "f24a2ab7ee5b1eb2ec1d98549c08e7e117cb33f34049e645238e1348292fcc90", + "verification_source": "8125ae8f2c5e2485ba112e065bcbfa40468a113a41a3dfa34871dd239fd12f6e", "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/bor > backend/bor && chmod +x backend/bor && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_bor_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_bor.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.2.8/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", + "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/bor/v2.2.9/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.8/bor-v2.2.8-arm64.deb", - "verification_source": "72f97c6bc88f2a38a4cfa27a8e7aee240d77250096a8c4650fe654fb0b170008" + "binary_url": "https://github.com/maticnetwork/bor/releases/download/v2.2.9/bor-v2.2.9-arm64.deb", + "verification_source": "344bbd01a230250a43373ee559cb596bc8afb95026ce4aa9652c46077740414f" } } }, From 4da97b8ab884b5db7d69b2ca94062e55a6ab12f6 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 25 Jul 2025 13:03:16 +0200 Subject: [PATCH 494/530] Fix GetChainInfo for Avalanche --- bchain/coins/avalanche/avalancherpc.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/bchain/coins/avalanche/avalancherpc.go b/bchain/coins/avalanche/avalancherpc.go index eec69d3e80..0692174ed3 100644 --- a/bchain/coins/avalanche/avalancherpc.go +++ b/bchain/coins/avalanche/avalancherpc.go @@ -121,12 +121,10 @@ func (b *AvalancheRPC) GetChainInfo() (*bchain.ChainInfo, error) { VMVersions map[string]string `json:"vmVersions"` } - if err := b.info.CallContext(ctx, &v, "info.getNodeVersion"); err != nil { - return nil, err - } - - if avm, ok := v.VMVersions["avm"]; ok { - ci.Version = avm + if err := b.info.CallContext(ctx, &v, "info.getNodeVersion"); err == nil { + if avm, ok := v.VMVersions["avm"]; ok { + ci.Version = avm + } } return ci, nil From 20234811f671b6e74c2bdb0d454c8085ebb6729c Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 24 Jun 2025 17:11:52 +0200 Subject: [PATCH 495/530] Use Zebra as ZCash backend node --- bchain/coins/zec/zcashrpc.go | 195 ++++++++++++++++++---- build/templates/backend/config/zcash.conf | 57 +++++++ configs/coins/zcash.json | 30 ++-- 3 files changed, 236 insertions(+), 46 deletions(-) create mode 100644 build/templates/backend/config/zcash.conf diff --git a/bchain/coins/zec/zcashrpc.go b/bchain/coins/zec/zcashrpc.go index c7f40566de..b3ee93f11e 100644 --- a/bchain/coins/zec/zcashrpc.go +++ b/bchain/coins/zec/zcashrpc.go @@ -1,7 +1,10 @@ package zec import ( + "bytes" "encoding/json" + "os/exec" + "reflect" "github.com/golang/glog" "github.com/juju/errors" @@ -42,7 +45,7 @@ func NewZCashRPC(config json.RawMessage, pushHandler func(bchain.NotificationTyp z := &ZCashRPC{ BitcoinRPC: b.(*btc.BitcoinRPC), } - z.RPCMarshaler = btc.JSONMarshalerV1{} + z.RPCMarshaler = JSONMarshalerV1Zebra{} z.ChainConfig.SupportsEstimateSmartFee = false return z, nil } @@ -84,13 +87,16 @@ func (z *ZCashRPC) GetChainInfo() (*bchain.ChainInfo, error) { return nil, chainInfo.Error } + // networkinfo not supported by zebra networkInfo := btc.ResGetNetworkInfo{} - err = z.Call(&btc.CmdGetNetworkInfo{Method: "getnetworkinfo"}, &networkInfo) - if err != nil { - return nil, err - } - if networkInfo.Error != nil { - return nil, networkInfo.Error + + zebrad := "zebra" + cmd := exec.Command("/opt/coins/nodes/zcash/bin/zebrad", "--version") + var out bytes.Buffer + cmd.Stdout = &out + err = cmd.Run() + if err == nil { + zebrad = out.String() } return &bchain.ChainInfo{ @@ -100,7 +106,7 @@ func (z *ZCashRPC) GetChainInfo() (*bchain.ChainInfo, error) { Difficulty: string(chainInfo.Result.Difficulty), Headers: chainInfo.Result.Headers, SizeOnDisk: chainInfo.Result.SizeOnDisk, - Version: string(networkInfo.Result.Version), + Version: zebrad, Subversion: string(networkInfo.Result.Subversion), ProtocolVersion: string(networkInfo.Result.ProtocolVersion), Timeoffset: networkInfo.Result.Timeoffset, @@ -111,6 +117,22 @@ func (z *ZCashRPC) GetChainInfo() (*bchain.ChainInfo, error) { // GetBlock returns block with given hash. func (z *ZCashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { + type rpcBlock struct { + bchain.BlockHeader + Txs []bchain.Tx `json:"tx"` + } + type rpcBlockTxids struct { + Txids []string `json:"tx"` + } + type resGetBlockV1 struct { + Error *bchain.RPCError `json:"error"` + Result rpcBlockTxids `json:"result"` + } + type resGetBlockV2 struct { + Error *bchain.RPCError `json:"error"` + Result rpcBlock `json:"result"` + } + var err error if hash == "" && height > 0 { hash, err = z.GetBlockHash(height) @@ -119,40 +141,86 @@ func (z *ZCashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { } } - glog.V(1).Info("rpc: getblock (verbosity=1) ", hash) - - res := btc.ResGetBlockThin{} + var rawResponse json.RawMessage + resV2 := resGetBlockV2{} req := btc.CmdGetBlock{Method: "getblock"} req.Params.BlockHash = hash - req.Params.Verbosity = 1 - err = z.Call(&req, &res) - + req.Params.Verbosity = 2 + err = z.Call(&req, &rawResponse) if err != nil { return nil, errors.Annotatef(err, "hash %v", hash) } - if res.Error != nil { - return nil, errors.Annotatef(res.Error, "hash %v", hash) + // hack for ZCash, where the field "valueZat" is used instead of "valueSat" + rawResponse = bytes.ReplaceAll(rawResponse, []byte(`"valueZat"`), []byte(`"valueSat"`)) + err = json.Unmarshal(rawResponse, &resV2) + if err != nil { + return nil, errors.Annotatef(err, "hash %v", hash) } - txs := make([]bchain.Tx, 0, len(res.Result.Txids)) - for _, txid := range res.Result.Txids { - tx, err := z.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) + if resV2.Error != nil { + return nil, errors.Annotatef(resV2.Error, "hash %v", hash) } block := &bchain.Block{ - BlockHeader: res.Result.BlockHeader, - Txs: txs, + BlockHeader: resV2.Result.BlockHeader, + Txs: resV2.Result.Txs, + } + + // transactions fetched in block with verbosity 2 do not contain txids, so we need to get it separately + resV1 := resGetBlockV1{} + req.Params.Verbosity = 1 + err = z.Call(&req, &resV1) + if err != nil { + return nil, errors.Annotatef(err, "hash %v", hash) + } + if resV1.Error != nil { + return nil, errors.Annotatef(resV1.Error, "hash %v", hash) + } + for i := range resV1.Result.Txids { + block.Txs[i].Txid = resV1.Result.Txids[i] } return block, nil } +// GetTransaction returns a transaction by the transaction ID +func (z *ZCashRPC) GetTransaction(txid string) (*bchain.Tx, error) { + r, err := z.getRawTransaction(txid) + if err != nil { + return nil, err + } + // hack for ZCash, where the field "valueZat" is used instead of "valueSat" + r = bytes.ReplaceAll(r, []byte(`"valueZat"`), []byte(`"valueSat"`)) + tx, err := z.Parser.ParseTxFromJson(r) + if err != nil { + return nil, errors.Annotatef(err, "txid %v", txid) + } + tx.Blocktime = tx.Time + tx.Txid = txid + tx.CoinSpecificData = r + return tx, nil +} + +// getRawTransaction returns json as returned by backend, with all coin specific data +func (z *ZCashRPC) getRawTransaction(txid string) (json.RawMessage, error) { + glog.V(1).Info("rpc: getrawtransaction ", txid) + + res := btc.ResGetRawTransaction{} + req := btc.CmdGetRawTransaction{Method: "getrawtransaction"} + req.Params.Txid = txid + req.Params.Verbose = true + err := z.Call(&req, &res) + + if err != nil { + return nil, errors.Annotatef(err, "txid %v", txid) + } + if res.Error != nil { + if btc.IsMissingTx(res.Error) { + return nil, bchain.ErrTxNotFound + } + return nil, errors.Annotatef(res.Error, "txid %v", txid) + } + return res.Result, nil +} + // GetTransactionForMempool returns a transaction by the transaction ID. // It could be optimized for mempool, i.e. without block time and confirmations func (z *ZCashRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) { @@ -168,3 +236,72 @@ func (z *ZCashRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error) { func (z *ZCashRPC) GetBlockRaw(hash string) (string, error) { return "", errors.New("GetBlockRaw: not supported") } + +// JSONMarshalerV1 is used for marshalling requests to legacy Bitcoin Type RPC interfaces +type JSONMarshalerV1Zebra struct{} + +// Marshal converts struct passed by parameter to JSON +func (JSONMarshalerV1Zebra) Marshal(v interface{}) ([]byte, error) { + u := cmdUntypedParams{} + + switch v := v.(type) { + case *btc.CmdGetBlock: + u.Method = v.Method + u.Params = append(u.Params, v.Params.BlockHash) + u.Params = append(u.Params, v.Params.Verbosity) + case *btc.CmdGetRawTransaction: + var n int + if v.Params.Verbose { + n = 1 + } + u.Method = v.Method + u.Params = append(u.Params, v.Params.Txid) + u.Params = append(u.Params, n) + default: + { + v := reflect.ValueOf(v).Elem() + + f := v.FieldByName("Method") + if !f.IsValid() || f.Kind() != reflect.String { + return nil, btc.ErrInvalidValue + } + u.Method = f.String() + + f = v.FieldByName("Params") + if f.IsValid() { + var arr []interface{} + switch f.Kind() { + case reflect.Slice: + arr = make([]interface{}, f.Len()) + for i := 0; i < f.Len(); i++ { + arr[i] = f.Index(i).Interface() + } + case reflect.Struct: + arr = make([]interface{}, f.NumField()) + for i := 0; i < f.NumField(); i++ { + arr[i] = f.Field(i).Interface() + } + default: + return nil, btc.ErrInvalidValue + } + u.Params = arr + } + } + } + u.Id = "-" + if u.Params == nil { + u.Params = make([]interface{}, 0) + } + d, err := json.Marshal(u) + if err != nil { + return nil, err + } + + return d, nil +} + +type cmdUntypedParams struct { + Method string `json:"method"` + Id string `json:"id"` + Params []interface{} `json:"params"` +} diff --git a/build/templates/backend/config/zcash.conf b/build/templates/backend/config/zcash.conf new file mode 100644 index 0000000000..25103a7841 --- /dev/null +++ b/build/templates/backend/config/zcash.conf @@ -0,0 +1,57 @@ +{{define "main" -}}[consensus] +checkpoint_sync = true + +[mempool] +eviction_memory_time = "1h" +tx_cost_limit = 80000000 + +[metrics] + +[mining] +debug_like_zcashd = true +internal_miner = false + +[network] +cache_dir = true +crawl_new_peer_interval = "1m 1s" +initial_mainnet_peers = [ + "dnsseed.z.cash:8233", + "dnsseed.str4d.xyz:8233", + "mainnet.seeder.zfnd.org:8233", + "mainnet.is.yolo.money:8233", +] +initial_testnet_peers = [ + "dnsseed.testnet.z.cash:18233", + "testnet.seeder.zfnd.org:18233", + "testnet.is.yolo.money:18233", +] +listen_addr = "0.0.0.0:8233" +max_connections_per_ip = 1 +network = "Mainnet" +peerset_initial_target_size = 25 + +[rpc] +cookie_dir = "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend" +debug_force_finished_sync = false +enable_cookie_auth = false +parallel_cpu_threads = 0 +listen_addr = '127.0.0.1:{{.Ports.BackendRPC}}' + +[state] +cache_dir = "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/zebra" +delete_old_database = true +ephemeral = false + +[sync] +checkpoint_verify_concurrency_limit = 1000 +download_concurrency_limit = 50 +full_verify_concurrency_limit = 20 +parallel_cpu_threads = 0 + +[tracing] +buffer_limit = 128000 +force_use_color = false +use_color = true +use_journald = false +log_file = "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/zebra.log" +{{end}} \ No newline at end of file diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index 566c3066de..9ddb165822 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,25 +22,21 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "6.2.0", - "binary_url": "https://download.z.cash/downloads/zcash-6.2.0-linux64-debian-bullseye.tar.gz", - "verification_type": "sha256", - "verification_source": "71cf378c27582a4b9f9d57cafc2b5a57a46e9e52a5eda33be112dc9790c64c6f", - "extract_command": "tar -C backend --strip 1 -xf", + "version": "2.3.0", + "docker_image": "zfnd/zebra:2.3.0", + "verification_type": "docker", + "verification_source": "70835d84cc6dceeda160707a35c611b11e616acb3627c99862e92bb5f0789ab4", + "extract_command": "mkdir backend/bin && docker cp extract:/usr/local/bin/zebrad backend/bin/zebrad", "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", + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/zebrad --config {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/zcash.conf start", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log", - "postinst_script_template": "HOME={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/zcash-fetch-params", - "service_type": "forking", - "service_additional_params_template": "Environment=\"HOME={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend\"", - "protect_memory": false, + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, "mainnet": true, - "server_config_file": "bitcoin_like.conf", - "client_config_file": "bitcoin_like_client.conf", - "additional_params": { - "addnode": ["mainnet.z.cash"], - "i-am-aware-zcashd-will-be-replaced-by-zebrad-and-zallet-in-2025": 1 - } + "server_config_file": "zcash.conf", + "client_config_file": "bitcoin_like_client.conf" }, "blockbook": { "package_name": "blockbook-zcash", @@ -48,7 +44,7 @@ "internal_binding_template": ":{{.Ports.BlockbookInternal}}", "public_binding_template": ":{{.Ports.BlockbookPublic}}", "explorer_url": "", - "additional_params": "", + "additional_params": "-resyncindexperiod=50000 -resyncmempoolperiod=3000", "block_chain": { "parse": true, "mempool_workers": 4, From aa445e696ad46986032d646ecfec8cf00fc6b874 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 30 Jul 2025 15:03:28 +0200 Subject: [PATCH 496/530] Polygon heimdall v2 --- .../backend/scripts/polygon_archive_heimdall.sh | 17 +++++------------ .../backend/scripts/polygon_heimdall.sh | 17 +++++------------ configs/coins/polygon_heimdall.json | 11 +++++------ configs/coins/polygon_heimdall_archive.json | 11 +++++------ 4 files changed, 20 insertions(+), 36 deletions(-) diff --git a/build/templates/backend/scripts/polygon_archive_heimdall.sh b/build/templates/backend/scripts/polygon_archive_heimdall.sh index 30c55058ce..988956ab6e 100644 --- a/build/templates/backend/scripts/polygon_archive_heimdall.sh +++ b/build/templates/backend/scripts/polygon_archive_heimdall.sh @@ -8,29 +8,22 @@ INSTALL_DIR={{.Env.BackendInstallPath}}/{{.Coin.Alias}} DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend HEIMDALL_BIN=$INSTALL_DIR/heimdalld -HOME_DIR=$DATA_DIR/heimdalld +HOME_DIR=$DATA_DIR CONFIG_DIR=$HOME_DIR/config if [ ! -d "$CONFIG_DIR" ]; then # init chain - $HEIMDALL_BIN init --home $HOME_DIR - - # overwrite genesis file - cp $INSTALL_DIR/genesis.json $CONFIG_DIR/genesis.json + $HEIMDALL_BIN init $(hostname -s) --home $HOME_DIR --chain-id heimdallv2-137 fi # --bor_rpc_url: backend-polygon-bor-archive ports.backend_http # --eth_rpc_url: backend-ethereum-archive ports.backend_http $HEIMDALL_BIN start \ --home $HOME_DIR \ - --chain=mainnet \ --rpc.laddr tcp://127.0.0.1:{{.Ports.BackendRPC}} \ --p2p.laddr tcp://0.0.0.0:{{.Ports.BackendP2P}} \ - --laddr tcp://127.0.0.1:{{.Ports.BackendHttp}} \ - --p2p.seeds "f4f605d60b8ffaaf15240564e58a81103510631c@159.203.9.164:26656,4fb1bc820088764a564d4f66bba1963d47d82329@44.232.55.71:26656,2eadba4be3ce47ac8db0a3538cb923b57b41c927@35.199.4.13:26656,3b23b20017a6f348d329c102ddc0088f0a10a444@35.221.13.28:26656,25f5f65a09c56e9f1d2d90618aa70cd358aa68da@35.230.116.151:26656,4cd60c1d76e44b05f7dfd8bab3f447b119e87042@54.147.31.250:26656,b18bbe1f3d8576f4b73d9b18976e71c65e839149@34.226.134.117:26656,1500161dd491b67fb1ac81868952be49e2509c9f@52.78.36.216:26656,dd4a3f1750af5765266231b9d8ac764599921736@3.36.224.80:26656,8ea4f592ad6cc38d7532aff418d1fb97052463af@34.240.245.39:26656,e772e1fb8c3492a9570a377a5eafdb1dc53cd778@54.194.245.5:26656,6726b826df45ac8e9afb4bdb2469c7771bd797f1@52.209.21.164:26656" \ - --node tcp://127.0.0.1:{{.Ports.BackendRPC}} \ + --grpc_server tcp://127.0.0.1:{{.Ports.BackendHttp}} \ + --p2p.seeds "e019e16d4e376723f3adc58eb1761809fea9bee0@35.234.150.253:26656,7f3049e88ac7f820fd86d9120506aaec0dc54b27@34.89.75.187:26656,1f5aff3b4f3193404423c3dd1797ce60cd9fea43@34.142.43.240:26656,2d5484feef4257e56ece025633a6ea132d8cadca@35.246.99.203:26656,17e9efcbd173e81a31579310c502e8cdd8b8ff2e@35.197.233.249:26656,72a83490309f9f63fdca3a0bef16c290e5cbb09c@35.246.95.65:26656,00677b1b2c6282fb060b7bb6e9cc7d2d05cdd599@34.105.180.11:26656,721dd4cebfc4b78760c7ee5d7b1b44d29a0aa854@34.147.169.102:26656,4760b3fc04648522a0bcb2d96a10aadee141ee89@34.89.55.74:26656" \ --bor_rpc_url http://127.0.0.1:8172 \ - --eth_rpc_url http://127.0.0.1:8116 \ - --rest-server - + --eth_rpc_url http://127.0.0.1:8116 {{end}} \ No newline at end of file diff --git a/build/templates/backend/scripts/polygon_heimdall.sh b/build/templates/backend/scripts/polygon_heimdall.sh index 27d4ee2460..c267c1bbed 100644 --- a/build/templates/backend/scripts/polygon_heimdall.sh +++ b/build/templates/backend/scripts/polygon_heimdall.sh @@ -8,29 +8,22 @@ INSTALL_DIR={{.Env.BackendInstallPath}}/{{.Coin.Alias}} DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend HEIMDALL_BIN=$INSTALL_DIR/heimdalld -HOME_DIR=$DATA_DIR/heimdalld +HOME_DIR=$DATA_DIR CONFIG_DIR=$HOME_DIR/config if [ ! -d "$CONFIG_DIR" ]; then # init chain - $HEIMDALL_BIN init --home $HOME_DIR - - # overwrite genesis file - cp $INSTALL_DIR/genesis.json $CONFIG_DIR/genesis.json + $HEIMDALL_BIN init $(hostname -s) --home $HOME_DIR --chain-id heimdallv2-137 fi # --bor_rpc_url: backend-polygon-bor ports.backend_http # --eth_rpc_url: backend-ethereum ports.backend_http $HEIMDALL_BIN start \ --home $HOME_DIR \ - --chain=mainnet \ --rpc.laddr tcp://127.0.0.1:{{.Ports.BackendRPC}} \ --p2p.laddr tcp://0.0.0.0:{{.Ports.BackendP2P}} \ - --laddr tcp://127.0.0.1:{{.Ports.BackendHttp}} \ - --p2p.seeds "f4f605d60b8ffaaf15240564e58a81103510631c@159.203.9.164:26656,4fb1bc820088764a564d4f66bba1963d47d82329@44.232.55.71:26656,2eadba4be3ce47ac8db0a3538cb923b57b41c927@35.199.4.13:26656,3b23b20017a6f348d329c102ddc0088f0a10a444@35.221.13.28:26656,25f5f65a09c56e9f1d2d90618aa70cd358aa68da@35.230.116.151:26656,4cd60c1d76e44b05f7dfd8bab3f447b119e87042@54.147.31.250:26656,b18bbe1f3d8576f4b73d9b18976e71c65e839149@34.226.134.117:26656,1500161dd491b67fb1ac81868952be49e2509c9f@52.78.36.216:26656,dd4a3f1750af5765266231b9d8ac764599921736@3.36.224.80:26656,8ea4f592ad6cc38d7532aff418d1fb97052463af@34.240.245.39:26656,e772e1fb8c3492a9570a377a5eafdb1dc53cd778@54.194.245.5:26656,6726b826df45ac8e9afb4bdb2469c7771bd797f1@52.209.21.164:26656" \ - --node tcp://127.0.0.1:{{.Ports.BackendRPC}} \ + --grpc_server tcp://127.0.0.1:{{.Ports.BackendHttp}} \ + --p2p.seeds "e019e16d4e376723f3adc58eb1761809fea9bee0@35.234.150.253:26656,7f3049e88ac7f820fd86d9120506aaec0dc54b27@34.89.75.187:26656,1f5aff3b4f3193404423c3dd1797ce60cd9fea43@34.142.43.240:26656,2d5484feef4257e56ece025633a6ea132d8cadca@35.246.99.203:26656,17e9efcbd173e81a31579310c502e8cdd8b8ff2e@35.197.233.249:26656,72a83490309f9f63fdca3a0bef16c290e5cbb09c@35.246.95.65:26656,00677b1b2c6282fb060b7bb6e9cc7d2d05cdd599@34.105.180.11:26656,721dd4cebfc4b78760c7ee5d7b1b44d29a0aa854@34.147.169.102:26656,4760b3fc04648522a0bcb2d96a10aadee141ee89@34.89.55.74:26656" \ --bor_rpc_url http://127.0.0.1:8170 \ - --eth_rpc_url http://127.0.0.1:8136 \ - --rest-server - + --eth_rpc_url http://127.0.0.1:8136 {{end}} \ No newline at end of file diff --git a/configs/coins/polygon_heimdall.json b/configs/coins/polygon_heimdall.json index a7444c8882..7c05497569 100644 --- a/configs/coins/polygon_heimdall.json +++ b/configs/coins/polygon_heimdall.json @@ -16,16 +16,15 @@ "package_name": "backend-polygon-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.6.0", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.6.0.tar.gz", + "version": "0.2.16", + "binary_url": "https://github.com/0xPolygon/heimdall-v2/releases/download/v0.2.16/heimdall-v0.2.16-amd64.deb", "verification_type": "sha256", - "verification_source": "8cf23d79724d29b38565ea0ca6574efb62ab6b929450ed38ae2ef785679adc39", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.6.0.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "1682bade3065065a4b660a162e06c843b4a3079af829cec300a05e9577c9389b", + "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/heimdalld > backend/heimdalld && chmod +x backend/heimdalld && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.6.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -37,4 +36,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/polygon_heimdall_archive.json b/configs/coins/polygon_heimdall_archive.json index ce2c39df75..96826703db 100644 --- a/configs/coins/polygon_heimdall_archive.json +++ b/configs/coins/polygon_heimdall_archive.json @@ -16,16 +16,15 @@ "package_name": "backend-polygon-archive-heimdall", "package_revision": "satoshilabs-1", "system_user": "polygon", - "version": "1.6.0", - "binary_url": "https://github.com/maticnetwork/heimdall/archive/refs/tags/v1.6.0.tar.gz", + "version": "0.2.16", + "binary_url": "https://github.com/0xPolygon/heimdall-v2/releases/download/v0.2.16/heimdall-v0.2.16-amd64.deb", "verification_type": "sha256", - "verification_source": "8cf23d79724d29b38565ea0ca6574efb62ab6b929450ed38ae2ef785679adc39", - "extract_command": "mkdir backend/source && tar -C backend/source --strip 1 -xf v1.6.0.tar.gz && cd backend/source && make build && mv build/heimdalld ../ && rm -rf ../source && echo", + "verification_source": "1682bade3065065a4b660a162e06c843b4a3079af829cec300a05e9577c9389b", + "extract_command": "mkdir -p backend && dpkg --fsys-tarfile ${ARCHIVE} | tar -xO ./usr/bin/heimdalld > backend/heimdalld && chmod +x backend/heimdalld && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/polygon_archive_heimdall_exec.sh 2>&1 >> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", "exec_script": "polygon_archive_heimdall.sh", "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://raw.githubusercontent.com/maticnetwork/heimdall/v1.6.0/builder/files/genesis-mainnet-v1.json -O {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/genesis.json", "service_type": "simple", "service_additional_params_template": "", "protect_memory": true, @@ -37,4 +36,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} From c2ded90100d8e0fede5be391e5741e996627f7dc Mon Sep 17 00:00:00 2001 From: Tadeas Kmenta Date: Mon, 11 Aug 2025 12:54:53 +0200 Subject: [PATCH 497/530] Update Flux Daemon to v8.0.0 (#1306) * update flux addnodes * fluxd v8.0.0 --- configs/coins/flux.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/flux.json b/configs/coins/flux.json index 5752d3a5f1..ec64a22dab 100644 --- a/configs/coins/flux.json +++ b/configs/coins/flux.json @@ -22,10 +22,10 @@ "package_name": "backend-flux", "package_revision": "satoshilabs-1", "system_user": "flux", - "version": "7.2.0", - "binary_url": "https://github.com/RunOnFlux/fluxd/releases/download/v7.2.0/Flux-amd64-v7.2.0.tar.gz", + "version": "8.0.0", + "binary_url": "https://github.com/RunOnFlux/fluxd/releases/download/v8.0.0/Flux-amd64-v8.0.0.tar.gz", "verification_type": "sha256", - "verification_source": "aac3a9581fb8e8f3215ddd3de9721fdb6e9d90ef65d3fa73a495d7451dd480ef", + "verification_source": "c9e579fb39f78d2c15190bf8350745344efa4eff5563bba4221d6f20d536848a", "extract_command": "tar -C backend -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/fluxd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", From e1fb89cb9000dc75604c0d687cb8341e12f5fb29 Mon Sep 17 00:00:00 2001 From: etimofeeva Date: Thu, 14 Aug 2025 22:03:11 +0200 Subject: [PATCH 498/530] feat: implemented eth_getTransactionCount with pending tag --- bchain/coins/eth/ethrpc.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index b96515b923..6ebadab720 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -1170,9 +1170,17 @@ func (b *EthereumRPC) EthereumTypeGetBalance(addrDesc bchain.AddressDescriptor) // EthereumTypeGetNonce returns current balance of an address func (b *EthereumRPC) EthereumTypeGetNonce(addrDesc bchain.AddressDescriptor) (uint64, error) { - ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) - defer cancel() - return b.Client.NonceAt(ctx, addrDesc, nil) + result, err := b.callRpcStringResult("eth_getTransactionCount", addrDesc, "pending") + if err != nil { + return 0, err + } + + nonce, err := hexutil.DecodeUint64(result) + if err != nil { + return 0, err + } + + return nonce, nil } // GetChainParser returns ethereum BlockChainParser From 54c133953e6cfea61af8cb2d7208d3ce6f17eb15 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 4 Aug 2025 19:10:54 +0200 Subject: [PATCH 499/530] Fix initialization of ethereum rpc --- bchain/coins/eth/ethrpc.go | 54 +++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index b96515b923..25cf0ee42b 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -111,30 +111,6 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification s.Timeout = time.Duration(c.RPCTimeout) * time.Second s.PushHandler = pushHandler - if s.ChainConfig.AlternativeEstimateFee == "1inch" { - if s.alternativeFeeProvider, err = NewOneInchFeesProvider(s, s.ChainConfig.AlternativeEstimateFeeParams); err != nil { - glog.Error("New1InchFeesProvider error ", err, " Reverting to default estimateFee functionality") - // disable AlternativeEstimateFee logic - s.alternativeFeeProvider = nil - } - } else if s.ChainConfig.AlternativeEstimateFee == "infura" { - if s.alternativeFeeProvider, err = NewInfuraFeesProvider(s, s.ChainConfig.AlternativeEstimateFeeParams); err != nil { - glog.Error("NewInfuraFeesProvider error ", err, " Reverting to default estimateFee functionality") - // disable AlternativeEstimateFee logic - s.alternativeFeeProvider = nil - } - } - if s.alternativeFeeProvider != nil { - glog.Info("Using alternative fee provider ", s.ChainConfig.AlternativeEstimateFee) - } - - network := c.Network - if network == "" { - network = c.CoinShortcut - } - - s.alternativeSendTxProvider = NewAlternativeSendTxProvider(network, c.RPCTimeout, c.MempoolTxTimeoutHours) - return s, nil } @@ -195,6 +171,15 @@ func (b *EthereumRPC) Initialize() error { return err } + b.initAlternativeFeeProvider() + + network := b.Network + if network == "" { + network = b.ChainConfig.CoinShortcut + } + + b.alternativeSendTxProvider = NewAlternativeSendTxProvider(network, b.ChainConfig.RPCTimeout, b.ChainConfig.MempoolTxTimeoutHours) + glog.Info("rpc: block chain ", b.Network) return nil @@ -359,6 +344,27 @@ func (b *EthereumRPC) subscribe(f func() (bchain.EVMClientSubscription, error)) return nil } +func (b *EthereumRPC) initAlternativeFeeProvider() { + var err error + if b.ChainConfig.AlternativeEstimateFee == "1inch" { + if b.alternativeFeeProvider, err = NewOneInchFeesProvider(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil { + glog.Error("New1InchFeesProvider error ", err, " Reverting to default estimateFee functionality") + // disable AlternativeEstimateFee logic + b.alternativeFeeProvider = nil + } + } else if b.ChainConfig.AlternativeEstimateFee == "infura" { + if b.alternativeFeeProvider, err = NewInfuraFeesProvider(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil { + glog.Error("NewInfuraFeesProvider error ", err, " Reverting to default estimateFee functionality") + // disable AlternativeEstimateFee logic + b.alternativeFeeProvider = nil + } + } + if b.alternativeFeeProvider != nil { + glog.Info("Using alternative fee provider ", b.ChainConfig.AlternativeEstimateFee) + } + +} + func (b *EthereumRPC) closeRPC() { if b.newBlockSubscription != nil { b.newBlockSubscription.Unsubscribe() From 541e30dbaa82de295bdc6dc83575c52207ac83f5 Mon Sep 17 00:00:00 2001 From: etimofeeva Date: Thu, 31 Jul 2025 11:38:03 +0200 Subject: [PATCH 500/530] 2.4.2 zebra update --- build/templates/backend/config/zcash.conf | 1 - configs/coins/zcash.json | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build/templates/backend/config/zcash.conf b/build/templates/backend/config/zcash.conf index 25103a7841..edd7e6c1da 100644 --- a/build/templates/backend/config/zcash.conf +++ b/build/templates/backend/config/zcash.conf @@ -8,7 +8,6 @@ tx_cost_limit = 80000000 [metrics] [mining] -debug_like_zcashd = true internal_miner = false [network] diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index 9ddb165822..cb093ccb16 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,10 +22,10 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "2.3.0", - "docker_image": "zfnd/zebra:2.3.0", + "version": "2.4.2", + "docker_image": "zfnd/zebra:2.4.2", "verification_type": "docker", - "verification_source": "70835d84cc6dceeda160707a35c611b11e616acb3627c99862e92bb5f0789ab4", + "verification_source": "3c0a6d7677eec638870a7346f0d0b9205cee761aad35e8990970cd674cd30ae8", "extract_command": "mkdir backend/bin && docker cp extract:/usr/local/bin/zebrad backend/bin/zebrad", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/zebrad --config {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/zcash.conf start", From 0e92dd124b9e8bafee9a2808cd1e58dd5ac8f9fd Mon Sep 17 00:00:00 2001 From: etimofeeva Date: Thu, 14 Aug 2025 21:11:24 +0200 Subject: [PATCH 501/530] feat: add alternative RPC provider support for sendTransaction --- bchain/coins/blockchain.go | 4 ++-- bchain/coins/btc/bitcoinrpc.go | 2 +- bchain/coins/dcr/decredrpc.go | 2 +- bchain/coins/eth/ethrpc.go | 7 +++++-- bchain/coins/nuls/nulsrpc.go | 2 +- bchain/types.go | 2 +- blockbook-api.ts | 2 ++ server/public.go | 4 ++-- server/public_ethereumtype_test.go | 11 +++++++++++ server/socketio.go | 2 +- server/websocket.go | 7 ++++--- server/ws_types.go | 3 ++- tests/dbtestdata/fakechain.go | 2 +- 13 files changed, 34 insertions(+), 16 deletions(-) diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index d004896f52..38e98bea2a 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -302,9 +302,9 @@ func (c *blockChainWithMetrics) LongTermFeeRate() (v *bchain.LongTermFeeRate, er return c.b.LongTermFeeRate() } -func (c *blockChainWithMetrics) SendRawTransaction(tx string) (v string, err error) { +func (c *blockChainWithMetrics) SendRawTransaction(tx string, disableAlternativeRPC bool) (v string, err error) { defer func(s time.Time) { c.observeRPCLatency("SendRawTransaction", s, err) }(time.Now()) - return c.b.SendRawTransaction(tx) + return c.b.SendRawTransaction(tx, disableAlternativeRPC) } func (c *blockChainWithMetrics) GetMempoolEntry(txid string) (v *bchain.MempoolEntry, err error) { diff --git a/bchain/coins/btc/bitcoinrpc.go b/bchain/coins/btc/bitcoinrpc.go index 91bdee9793..f6daead229 100644 --- a/bchain/coins/btc/bitcoinrpc.go +++ b/bchain/coins/btc/bitcoinrpc.go @@ -889,7 +889,7 @@ func (b *BitcoinRPC) LongTermFeeRate() (*bchain.LongTermFeeRate, error) { } // SendRawTransaction sends raw transaction -func (b *BitcoinRPC) SendRawTransaction(tx string) (string, error) { +func (b *BitcoinRPC) SendRawTransaction(tx string, disableAlternativeRPC bool) (string, error) { glog.V(1).Info("rpc: sendrawtransaction") res := ResSendRawTransaction{} diff --git a/bchain/coins/dcr/decredrpc.go b/bchain/coins/dcr/decredrpc.go index 07cc459849..31c5810f81 100644 --- a/bchain/coins/dcr/decredrpc.go +++ b/bchain/coins/dcr/decredrpc.go @@ -791,7 +791,7 @@ func (d *DecredRPC) EstimateFee(blocks int) (big.Int, error) { return r, nil } -func (d *DecredRPC) SendRawTransaction(tx string) (string, error) { +func (d *DecredRPC) SendRawTransaction(tx string, disableAlternativeRPC bool) (string, error) { sendRawTxRequest := &GenericCmd{ ID: 1, Method: "sendrawtransaction", diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 25cf0ee42b..9cbf570748 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -1122,12 +1122,15 @@ func (b *EthereumRPC) EthereumTypeGetEip1559Fees() (*bchain.Eip1559Fees, error) } // SendRawTransaction sends raw transaction -func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) { +func (b *EthereumRPC) SendRawTransaction(hex string, disableAlternativeRPC bool) (string, error) { var txid string var retErr error - if b.alternativeSendTxProvider != nil { + if !disableAlternativeRPC && b.alternativeSendTxProvider != nil { txid, retErr = b.alternativeSendTxProvider.SendRawTransaction(hex) + if retErr == nil { + return txid, nil + } if b.alternativeSendTxProvider.UseOnlyAlternativeProvider() { return txid, retErr } diff --git a/bchain/coins/nuls/nulsrpc.go b/bchain/coins/nuls/nulsrpc.go index 001cb6dfd9..3c73bed8bc 100644 --- a/bchain/coins/nuls/nulsrpc.go +++ b/bchain/coins/nuls/nulsrpc.go @@ -471,7 +471,7 @@ func (n *NulsRPC) EstimateFee(blocks int) (big.Int, error) { return *big.NewInt(100000), nil } -func (n *NulsRPC) SendRawTransaction(tx string) (string, error) { +func (n *NulsRPC) SendRawTransaction(tx string, alternativeRPC bool) (string, error) { broadcast := CmdTxBroadcast{} req := struct { TxHex string `json:"txHex"` diff --git a/bchain/types.go b/bchain/types.go index e6c2d12606..8e214ae1b1 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -331,7 +331,7 @@ type BlockChain interface { EstimateSmartFee(blocks int, conservative bool) (big.Int, error) EstimateFee(blocks int) (big.Int, error) LongTermFeeRate() (*LongTermFeeRate, error) - SendRawTransaction(tx string) (string, error) + SendRawTransaction(tx string, disableAlternativeRPC bool) (string, error) GetMempoolEntry(txid string) (*MempoolEntry, error) GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error) // parser diff --git a/blockbook-api.ts b/blockbook-api.ts index a327e8d104..56ec8ae73c 100644 --- a/blockbook-api.ts +++ b/blockbook-api.ts @@ -754,6 +754,8 @@ export interface WsLongTermFeeRateRes { export interface WsSendTransactionReq { /** Hex-encoded transaction data to broadcast. */ hex: string; + /** Use alternative RPC method to broadcast transaction. */ + disableAlternativeRPC?: boolean; } export interface WsSubscribeAddressesReq { /** List of addresses to subscribe for updates (e.g., new transactions). */ diff --git a/server/public.go b/server/public.go index 84ba710b5a..82650d9b50 100644 --- a/server/public.go +++ b/server/public.go @@ -1059,7 +1059,7 @@ func (s *PublicServer) explorerSendTx(w http.ResponseWriter, r *http.Request) (t } hex := r.FormValue("hex") if len(hex) > 0 { - res, err := s.chain.SendRawTransaction(hex) + res, err := s.chain.SendRawTransaction(hex, false) if err != nil { data.SendTxHex = hex data.Error = &api.APIError{Text: err.Error(), Public: true} @@ -1509,7 +1509,7 @@ func (s *PublicServer) apiSendTx(r *http.Request, apiVersion int) (interface{}, } } if len(hex) > 0 { - res.Result, err = s.chain.SendRawTransaction(hex) + res.Result, err = s.chain.SendRawTransaction(hex, false) if err != nil { return nil, api.NewAPIError(err.Error(), true) } diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index f16658a6af..f63f8558f0 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -153,6 +153,17 @@ var websocketTestsEthereumType = []websocketTest{ }, want: `{"id":"1","data":{"data":"0x4567abcd"}}`, }, + { + name: "websocket sendTransaction hex format", + req: websocketReq{ + Method: "sendTransaction", + Params: WsSendTransactionReq{ + Hex: "123456", + DisableAlternativeRPC: true, + }, + }, + want: `{"id":"2","data":{"result":"9876"}}`, + }, } func initEthereumTypeDB(d *db.RocksDB) error { diff --git a/server/socketio.go b/server/socketio.go index 9e8dab7f66..c606eb1ac9 100644 --- a/server/socketio.go +++ b/server/socketio.go @@ -641,7 +641,7 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai } func (s *SocketIoServer) sendTransaction(tx string) (res resultSendTransaction, err error) { - txid, err := s.chain.SendRawTransaction(tx) + txid, err := s.chain.SendRawTransaction(tx, false) if err != nil { return res, err } diff --git a/server/websocket.go b/server/websocket.go index cdaa340c12..9a6d09cba3 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -376,10 +376,11 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *WsRe r := WsSendTransactionReq{} err = json.Unmarshal(req.Params, &r) if err == nil { - rv, err = s.sendTransaction(r.Hex) + rv, err = s.sendTransaction(r.Hex, r.DisableAlternativeRPC) } return }, + "getMempoolFilters": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) { r := WsMempoolFiltersReq{} err = json.Unmarshal(req.Params, &r) @@ -751,8 +752,8 @@ func (s *WebsocketServer) longTermFeeRate() (res interface{}, err error) { }, nil } -func (s *WebsocketServer) sendTransaction(tx string) (res resultSendTransaction, err error) { - txid, err := s.chain.SendRawTransaction(tx) +func (s *WebsocketServer) sendTransaction(tx string, disableAlternativeRPC bool) (res resultSendTransaction, err error) { + txid, err := s.chain.SendRawTransaction(tx, disableAlternativeRPC) if err != nil { return res, err } diff --git a/server/ws_types.go b/server/ws_types.go index 5add2356c0..3f17f6cea7 100644 --- a/server/ws_types.go +++ b/server/ws_types.go @@ -141,7 +141,8 @@ type WsLongTermFeeRateRes struct { // WsSendTransactionReq is used to broadcast a transaction to the network. type WsSendTransactionReq struct { - Hex string `json:"hex" ts_doc:"Hex-encoded transaction data to broadcast."` + Hex string `json:"hex,omitempty" ts_doc:"Hex-encoded transaction data to broadcast (string format)."` + DisableAlternativeRPC bool `json:"disableAlternativeRpc" ts_doc:"Use alternative RPC method to broadcast transaction."` } // WsSubscribeAddressesReq is used to subscribe to updates on a list of addresses. diff --git a/tests/dbtestdata/fakechain.go b/tests/dbtestdata/fakechain.go index 044c26838e..6f0e22e830 100644 --- a/tests/dbtestdata/fakechain.go +++ b/tests/dbtestdata/fakechain.go @@ -201,7 +201,7 @@ func (c *fakeBlockChain) EstimateFee(blocks int) (v big.Int, err error) { return } -func (c *fakeBlockChain) SendRawTransaction(tx string) (v string, err error) { +func (c *fakeBlockChain) SendRawTransaction(tx string, disableAlternativeRPC bool) (v string, err error) { if tx == "123456" { return "9876", nil } From 188d06ed318cc9c3f391ebaa3931c25251424475 Mon Sep 17 00:00:00 2001 From: etimofeeva Date: Thu, 21 Aug 2025 10:04:51 +0200 Subject: [PATCH 502/530] feat: added alternative provider to eth_estimateGas, eth_call, eth_getTransactionCount --- bchain/coins/eth/contract.go | 19 +++++++++++--- bchain/coins/eth/ethrpc.go | 49 +++++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go index 08149c085b..9e95160044 100644 --- a/bchain/coins/eth/contract.go +++ b/bchain/coins/eth/contract.go @@ -275,9 +275,6 @@ func contractGetTransfersFromTx(tx *bchain.RpcTransaction) (bchain.TokenTransfer // EthereumTypeRpcCall calls eth_call with given data and to address func (b *EthereumRPC) EthereumTypeRpcCall(data, to, from string) (string, error) { - ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) - defer cancel() - var r string args := map[string]interface{}{ "data": data, "to": to, @@ -285,6 +282,22 @@ func (b *EthereumRPC) EthereumTypeRpcCall(data, to, from string) (string, error) if from != "" { args["from"] = from } + + if b.alternativeSendTxProvider != nil { + result, err := b.alternativeSendTxProvider.callHttpStringResult( + b.alternativeSendTxProvider.urls[0], + "eth_call", + args, + "latest", + ) + if err == nil { + return result, nil + } + } + + ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) + defer cancel() + var r string err := b.RPC.CallContext(ctx, &r, "eth_call", args, "latest") if err != nil { return "", err diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 6ebadab720..32dacc988c 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -990,6 +990,20 @@ func (b *EthereumRPC) EstimateFee(blocks int) (big.Int, error) { // EstimateSmartFee returns fee estimation func (b *EthereumRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) { + if b.alternativeSendTxProvider != nil { + result, err := b.alternativeSendTxProvider.callHttpStringResult( + b.alternativeSendTxProvider.urls[0], + "eth_gasPrice", + ) + if err == nil { + if strings.HasPrefix(result, "0x") { + gasPrice, err := hexutil.DecodeBig(result) + if err == nil { + return *gasPrice, nil + } + } + } + } ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) defer cancel() var r big.Int @@ -1035,6 +1049,17 @@ func (b *EthereumRPC) EthereumTypeEstimateGas(params map[string]interface{}) (ui if s, ok := GetStringFromMap("gasPrice", params); ok && len(s) > 0 { msg.GasPrice, _ = hexutil.DecodeBig(s) } + + if b.alternativeSendTxProvider != nil { + result, err := b.alternativeSendTxProvider.callHttpStringResult( + b.alternativeSendTxProvider.urls[0], + "eth_estimateGas", + params, + ) + if err == nil { + return hexutil.DecodeUint64(result) + } + } return b.Client.EstimateGas(ctx, msg) } @@ -1170,9 +1195,27 @@ func (b *EthereumRPC) EthereumTypeGetBalance(addrDesc bchain.AddressDescriptor) // EthereumTypeGetNonce returns current balance of an address func (b *EthereumRPC) EthereumTypeGetNonce(addrDesc bchain.AddressDescriptor) (uint64, error) { - result, err := b.callRpcStringResult("eth_getTransactionCount", addrDesc, "pending") - if err != nil { - return 0, err + + var result string + var err error + + if b.alternativeSendTxProvider != nil { + result, err = b.alternativeSendTxProvider.callHttpStringResult( + b.alternativeSendTxProvider.urls[0], + "eth_getTransactionCount", + addrDesc, + "pending", + ) + if err != nil { + glog.Errorf("Alternative provider failed for eth_getTransactionCount: %v, falling back to primary RPC", err) + } + } + + if result == "" { + result, err = b.callRpcStringResult("eth_getTransactionCount", addrDesc, "pending") + if err != nil { + return 0, err + } } nonce, err := hexutil.DecodeUint64(result) From ecf873693ad3e5d41049cfbcdb220c3f2b2c938c Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 1 Sep 2025 13:08:35 +0200 Subject: [PATCH 503/530] Fix initialization of ethereum rpc --- bchain/coins/eth/ethrpc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 9cbf570748..4056f29dfb 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -173,7 +173,7 @@ func (b *EthereumRPC) Initialize() error { b.initAlternativeFeeProvider() - network := b.Network + network := b.ChainConfig.Network if network == "" { network = b.ChainConfig.CoinShortcut } From a1f7bacbc16c5d9d28d8e861d685665e62cc8dd9 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 1 Sep 2025 14:47:26 +0200 Subject: [PATCH 504/530] Add init alternative providers for EVM chains --- bchain/coins/arbitrum/arbitrumrpc.go | 2 ++ bchain/coins/avalanche/avalancherpc.go | 2 ++ bchain/coins/base/baserpc.go | 2 ++ bchain/coins/bsc/bscrpc.go | 2 ++ bchain/coins/eth/ethrpc.go | 14 +++++++++----- bchain/coins/optimism/optimismrpc.go | 2 ++ bchain/coins/polygon/polygonrpc.go | 2 ++ 7 files changed, 21 insertions(+), 5 deletions(-) diff --git a/bchain/coins/arbitrum/arbitrumrpc.go b/bchain/coins/arbitrum/arbitrumrpc.go index e8c2535438..d862e4b8af 100644 --- a/bchain/coins/arbitrum/arbitrumrpc.go +++ b/bchain/coins/arbitrum/arbitrumrpc.go @@ -71,6 +71,8 @@ func (b *ArbitrumRPC) Initialize() error { return errors.Errorf("Unknown network id %v", id) } + b.InitAlternativeProviders() + glog.Info("rpc: block chain ", b.Network) return nil diff --git a/bchain/coins/avalanche/avalancherpc.go b/bchain/coins/avalanche/avalancherpc.go index 0692174ed3..916c4c2f45 100644 --- a/bchain/coins/avalanche/avalancherpc.go +++ b/bchain/coins/avalanche/avalancherpc.go @@ -98,6 +98,8 @@ func (b *AvalancheRPC) Initialize() error { return errors.Errorf("Unknown network id %v", id) } + b.InitAlternativeProviders() + glog.Info("rpc: block chain ", b.Network) return nil diff --git a/bchain/coins/base/baserpc.go b/bchain/coins/base/baserpc.go index 116f82efa6..f0d0192118 100644 --- a/bchain/coins/base/baserpc.go +++ b/bchain/coins/base/baserpc.go @@ -67,6 +67,8 @@ func (b *BaseRPC) Initialize() error { return errors.Errorf("Unknown network id %v", id) } + b.InitAlternativeProviders() + glog.Info("rpc: block chain ", b.Network) return nil diff --git a/bchain/coins/bsc/bscrpc.go b/bchain/coins/bsc/bscrpc.go index 96fb648144..2439e57cf5 100644 --- a/bchain/coins/bsc/bscrpc.go +++ b/bchain/coins/bsc/bscrpc.go @@ -76,6 +76,8 @@ func (b *BNBSmartChainRPC) Initialize() error { return errors.Errorf("Unknown network id %v", id) } + b.InitAlternativeProviders() + glog.Info("rpc: block chain ", b.Network) return nil diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index be0482843b..c9c235b63c 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -171,18 +171,22 @@ func (b *EthereumRPC) Initialize() error { return err } + b.InitAlternativeProviders() + + glog.Info("rpc: block chain ", b.Network) + + return nil +} + +// InitAlternativeProviders initializes alternative providers +func (b *EthereumRPC) InitAlternativeProviders() { b.initAlternativeFeeProvider() network := b.ChainConfig.Network if network == "" { network = b.ChainConfig.CoinShortcut } - b.alternativeSendTxProvider = NewAlternativeSendTxProvider(network, b.ChainConfig.RPCTimeout, b.ChainConfig.MempoolTxTimeoutHours) - - glog.Info("rpc: block chain ", b.Network) - - return nil } // CreateMempool creates mempool if not already created, however does not initialize it diff --git a/bchain/coins/optimism/optimismrpc.go b/bchain/coins/optimism/optimismrpc.go index b28ae9efc5..3149bf6aae 100644 --- a/bchain/coins/optimism/optimismrpc.go +++ b/bchain/coins/optimism/optimismrpc.go @@ -67,6 +67,8 @@ func (b *OptimismRPC) Initialize() error { return errors.Errorf("Unknown network id %v", id) } + b.InitAlternativeProviders() + glog.Info("rpc: block chain ", b.Network) return nil diff --git a/bchain/coins/polygon/polygonrpc.go b/bchain/coins/polygon/polygonrpc.go index d218caf4a6..8ef914143b 100644 --- a/bchain/coins/polygon/polygonrpc.go +++ b/bchain/coins/polygon/polygonrpc.go @@ -67,6 +67,8 @@ func (b *PolygonRPC) Initialize() error { return errors.Errorf("Unknown network id %v", id) } + b.InitAlternativeProviders() + glog.Info("rpc: block chain ", b.Network) return nil From db2d8cd2481eff8dd3d7f49301875994a9377c0a Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 1 Sep 2025 20:03:15 +0200 Subject: [PATCH 505/530] Add AddrContractsCache to speed up indexing --- bchain/coins/eth/ethrpc.go | 2 +- db/rocksdb.go | 40 ++++++++++++++++++---------- db/rocksdb_ethereumtype.go | 54 +++++++++++++++++++++++++++++++++++--- 3 files changed, 78 insertions(+), 18 deletions(-) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index c9c235b63c..c796335744 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -504,7 +504,7 @@ func (b *EthereumRPC) getBestHeader() (bchain.EVMHeader, error) { // UpdateBestHeader keeps track of the latest block header confirmed on chain func (b *EthereumRPC) UpdateBestHeader(h bchain.EVMHeader) { - glog.V(2).Info("rpc: new block header ", h.Number()) + glog.V(2).Info("rpc: new block header ", h.Number().Uint64()) b.bestHeaderLock.Lock() b.bestHeader = h b.bestHeaderTime = time.Now() diff --git a/db/rocksdb.go b/db/rocksdb.go index f13bf29cb5..9fa6517f09 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -57,21 +57,25 @@ const ( addressBalanceDetailUTXOIndexed = 2 ) +const addrContractsCacheMinSize = 300_000 // limit for caching address contracts in memory to speed up indexing + // RocksDB handle type RocksDB struct { - path string - db *grocksdb.DB - wo *grocksdb.WriteOptions - ro *grocksdb.ReadOptions - cfh []*grocksdb.ColumnFamilyHandle - chainParser bchain.BlockChainParser - is *common.InternalState - metrics *common.Metrics - cache *grocksdb.Cache - maxOpenFiles int - cbs connectBlockStats - extendedIndex bool - connectBlockMux sync.Mutex + path string + db *grocksdb.DB + wo *grocksdb.WriteOptions + ro *grocksdb.ReadOptions + cfh []*grocksdb.ColumnFamilyHandle + chainParser bchain.BlockChainParser + is *common.InternalState + metrics *common.Metrics + cache *grocksdb.Cache + maxOpenFiles int + cbs connectBlockStats + extendedIndex bool + connectBlockMux sync.Mutex + addrContractsCacheMux sync.Mutex + addrContractsCache map[string]*unpackedAddrContracts } const ( @@ -150,7 +154,11 @@ func NewRocksDB(path string, cacheSize, maxOpenFiles int, parser bchain.BlockCha } wo := grocksdb.NewDefaultWriteOptions() ro := grocksdb.NewDefaultReadOptions() - return &RocksDB{path, db, wo, ro, cfh, parser, nil, metrics, c, maxOpenFiles, connectBlockStats{}, extendedIndex, sync.Mutex{}}, nil + r := &RocksDB{path, db, wo, ro, cfh, parser, nil, metrics, c, maxOpenFiles, connectBlockStats{}, extendedIndex, sync.Mutex{}, sync.Mutex{}, make(map[string]*unpackedAddrContracts)} + if chainType == bchain.ChainEthereumType { + go r.periodicStoreAddrContractsCache() + } + return r, nil } func (d *RocksDB) closeDB() error { @@ -165,6 +173,10 @@ func (d *RocksDB) closeDB() error { // Close releases the RocksDB environment opened in NewRocksDB. func (d *RocksDB) Close() error { if d.db != nil { + // store cached address contracts + if d.chainParser.GetChainType() == bchain.ChainEthereumType { + d.storeAddrContractsCache() + } // store the internal state of the app if d.is != nil && d.is.DbState == common.DbStateOpen { d.is.DbState = common.DbStateClosed diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go index bb2798e0fe..7d59825ee8 100644 --- a/db/rocksdb_ethereumtype.go +++ b/db/rocksdb_ethereumtype.go @@ -7,6 +7,7 @@ import ( "os" "sort" "sync" + "time" vlq "github.com/bsm/go-vlq" "github.com/golang/glog" @@ -1660,6 +1661,12 @@ func (s *unpackedMultiTokenValues) upsert(m bchain.MultiTokenValue, index int32, // getUnpackedAddrDescContracts returns partially unpacked AddrContracts for given addrDesc func (d *RocksDB) getUnpackedAddrDescContracts(addrDesc bchain.AddressDescriptor) (*unpackedAddrContracts, error) { + d.addrContractsCacheMux.Lock() + rv, found := d.addrContractsCache[string(addrDesc)] + d.addrContractsCacheMux.Unlock() + if found && rv != nil { + return rv, nil + } val, err := d.db.GetCF(d.ro, d.cfh[cfAddressContracts], addrDesc) if err != nil { return nil, err @@ -1669,7 +1676,13 @@ func (d *RocksDB) getUnpackedAddrDescContracts(addrDesc bchain.AddressDescriptor if len(buf) == 0 { return nil, nil } - return partiallyUnpackAddrContracts(buf) + rv, err = partiallyUnpackAddrContracts(buf) + if err == nil && rv != nil && len(buf) > addrContractsCacheMinSize { + d.addrContractsCacheMux.Lock() + d.addrContractsCache[string(addrDesc)] = rv + d.addrContractsCacheMux.Unlock() + } + return rv, err } // to speed up import of blocks, the unpacking of big ints is deferred to time when they are needed @@ -1797,9 +1810,44 @@ func (d *RocksDB) storeUnpackedAddressContracts(wb *grocksdb.WriteBatch, acm map if acs == nil || (acs.NonContractTxs == 0 && acs.InternalTxs == 0 && len(acs.Contracts) == 0) { wb.DeleteCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc)) } else { - buf := packUnpackedAddrContracts(acs) - wb.PutCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc), buf) + // do not store large address contracts found in cache + if _, found := d.addrContractsCache[addrDesc]; !found { + buf := packUnpackedAddrContracts(acs) + wb.PutCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc), buf) + } } } return nil } + +func (d *RocksDB) writeContractsCache() { + wb := grocksdb.NewWriteBatch() + defer wb.Destroy() + d.addrContractsCacheMux.Lock() + for addrDesc, acs := range d.addrContractsCache { + buf := packUnpackedAddrContracts(acs) + wb.PutCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc), buf) + } + d.addrContractsCacheMux.Unlock() + if err := d.WriteBatch(wb); err != nil { + glog.Error("writeContractsCache: failed to store addrContractsCache: ", err) + } +} + +func (d *RocksDB) storeAddrContractsCache() { + start := time.Now() + if len(d.addrContractsCache) > 0 { + d.writeContractsCache() + } + glog.Info("storeAddrContractsCache: store ", len(d.addrContractsCache), " entries in ", time.Since(start)) +} + +func (d *RocksDB) periodicStoreAddrContractsCache() { + period := time.Duration(5) * time.Minute + timer := time.NewTimer(period) + for { + <-timer.C + timer.Reset(period) + d.storeAddrContractsCache() + } +} From e6ecaff230289ac6185ce5aa099d090fd341d233 Mon Sep 17 00:00:00 2001 From: etimofeeva Date: Thu, 28 Aug 2025 18:28:43 +0200 Subject: [PATCH 506/530] added test to test-websocket.html --- static/test-websocket.html | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/static/test-websocket.html b/static/test-websocket.html index 438b5f6f4c..de9b588176 100644 --- a/static/test-websocket.html +++ b/static/test-websocket.html @@ -304,10 +304,14 @@ function sendTransaction() { var hex = document.getElementById('sendTransactionHex').value.trim(); + var disableAlternativeRPC = document.getElementById('sendTransactionDisableAlternativeRPC').value.trim(); const method = 'sendTransaction'; const params = { hex, }; + if (disableAlternativeRPC === 'true') { + params.disableAlternativeRpc = true; + } send(method, params, function (result) { document.getElementById('sendTransactionResult').innerText = JSON.stringify( result, @@ -929,12 +933,23 @@

Blockbook Websocket Test Page

/>
- +
+ + +
From 29b7d66811ea5672c00793290af16902bc28fe39 Mon Sep 17 00:00:00 2001 From: etimofeeva Date: Tue, 2 Sep 2025 18:29:23 +0200 Subject: [PATCH 507/530] fix: improved error handling and added better logging for readability fixed error handling added more error handling added logs for debugging parsing error fixed address descriptor rollback to error handling and additional debugging feat: added logging as error logging in order to better stand out --- bchain/coins/eth/ethrpc.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index c796335744..a2ac87bed4 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -1208,9 +1208,9 @@ func (b *EthereumRPC) EthereumTypeGetBalance(addrDesc bchain.AddressDescriptor) // EthereumTypeGetNonce returns current balance of an address func (b *EthereumRPC) EthereumTypeGetNonce(addrDesc bchain.AddressDescriptor) (uint64, error) { - var result string var err error + var usedAlternative bool if b.alternativeSendTxProvider != nil { result, err = b.alternativeSendTxProvider.callHttpStringResult( @@ -1219,20 +1219,24 @@ func (b *EthereumRPC) EthereumTypeGetNonce(addrDesc bchain.AddressDescriptor) (u addrDesc, "pending", ) - if err != nil { + if err == nil && result != "" { + usedAlternative = true + } else { glog.Errorf("Alternative provider failed for eth_getTransactionCount: %v, falling back to primary RPC", err) } } - if result == "" { + if !usedAlternative { result, err = b.callRpcStringResult("eth_getTransactionCount", addrDesc, "pending") if err != nil { + glog.Errorf("Primary RPC failed for eth_getTransactionCount: %v", err) return 0, err } } nonce, err := hexutil.DecodeUint64(result) if err != nil { + glog.Errorf("Failed to parse nonce result '%s': %v", result, err) return 0, err } From d4e9f0f8625ededa83fc5f3179f67000673987bc Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 8 Sep 2025 12:35:46 +0200 Subject: [PATCH 508/530] Fix EthereumTypeGetNonce --- bchain/coins/eth/ethrpc.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index a2ac87bed4..eaef4e7af4 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -1212,11 +1212,13 @@ func (b *EthereumRPC) EthereumTypeGetNonce(addrDesc bchain.AddressDescriptor) (u var err error var usedAlternative bool + ethAddress := ethcommon.BytesToAddress(addrDesc) + if b.alternativeSendTxProvider != nil { result, err = b.alternativeSendTxProvider.callHttpStringResult( b.alternativeSendTxProvider.urls[0], "eth_getTransactionCount", - addrDesc, + ethAddress, "pending", ) if err == nil && result != "" { @@ -1227,7 +1229,7 @@ func (b *EthereumRPC) EthereumTypeGetNonce(addrDesc bchain.AddressDescriptor) (u } if !usedAlternative { - result, err = b.callRpcStringResult("eth_getTransactionCount", addrDesc, "pending") + result, err = b.callRpcStringResult("eth_getTransactionCount", ethAddress, "pending") if err != nil { glog.Errorf("Primary RPC failed for eth_getTransactionCount: %v", err) return 0, err From 9939b92ef3d16a89eee010d2178810a752bb43af Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Sun, 17 Aug 2025 12:31:18 +0200 Subject: [PATCH 509/530] Add support for Ethereum Testnet Hoodi --- README.md | 2 +- bchain/coins/blockchain.go | 2 + bchain/coins/eth/ethrpc.go | 5 ++ configs/coins/ethereum_testnet_hoodi.json | 71 +++++++++++++++++ .../coins/ethereum_testnet_hoodi_archive.json | 77 +++++++++++++++++++ ...ereum_testnet_hoodi_archive_consensus.json | 52 +++++++++++++ .../ethereum_testnet_hoodi_consensus.json | 52 +++++++++++++ docs/ports.md | 6 +- 8 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 configs/coins/ethereum_testnet_hoodi.json create mode 100644 configs/coins/ethereum_testnet_hoodi_archive.json create mode 100644 configs/coins/ethereum_testnet_hoodi_archive_consensus.json create mode 100644 configs/coins/ethereum_testnet_hoodi_consensus.json diff --git a/README.md b/README.md index 3cc41cb14f..d5933f115a 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ the rest of coins were implemented by the community. Testnets for some coins are also supported, for example: -- Bitcoin Testnet, Bitcoin Cash Testnet, ZCash Testnet, Ethereum Testnets (Sepolia, Holesky) +- Bitcoin Testnet, Bitcoin Cash Testnet, ZCash Testnet, Ethereum Testnets (Sepolia, Hoodi) List of all implemented coins is in [the registry of ports](/docs/ports.md). diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 38e98bea2a..65b65d41a4 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -81,6 +81,8 @@ func init() { BlockChainFactories["Ethereum Testnet Sepolia Archive"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Holesky"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Holesky Archive"] = eth.NewEthereumRPC + BlockChainFactories["Ethereum Testnet Hoodi"] = eth.NewEthereumRPC + BlockChainFactories["Ethereum Testnet Hoodi Archive"] = eth.NewEthereumRPC BlockChainFactories["Bcash"] = bch.NewBCashRPC BlockChainFactories["Bcash Testnet"] = bch.NewBCashRPC BlockChainFactories["Bgold"] = btg.NewBGoldRPC diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index eaef4e7af4..9e738a7f92 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -34,6 +34,8 @@ const ( TestNetSepolia Network = 11155111 // TestNetHolesky is Holesky test network TestNetHolesky Network = 17000 + // TestNetHoodi is Hoodi test network + TestNetHoodi Network = 560048 ) // Configuration represents json config file @@ -162,6 +164,9 @@ func (b *EthereumRPC) Initialize() error { case TestNetHolesky: b.Testnet = true b.Network = "holesky" + case TestNetHoodi: + b.Testnet = true + b.Network = "hoodi" default: return errors.Errorf("Unknown network id %v", id) } diff --git a/configs/coins/ethereum_testnet_hoodi.json b/configs/coins/ethereum_testnet_hoodi.json new file mode 100644 index 0000000000..07ec6ab210 --- /dev/null +++ b/configs/coins/ethereum_testnet_hoodi.json @@ -0,0 +1,71 @@ +{ + "coin": { + "name": "Ethereum Testnet Hoodi", + "shortcut": "tHOD", + "label": "Ethereum Hoodi", + "alias": "ethereum_testnet_hoodi" + }, + "ports": { + "backend_rpc": 18006, + "backend_message_queue": 0, + "backend_p2p": 48306, + "backend_http": 18106, + "backend_authrpc": 18506, + "blockbook_internal": 19006, + "blockbook_public": 19106 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-ethereum-testnet-hoodi", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "3.0.11", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", + "verification_type": "sha256", + "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", + "extract_command": "tar -C backend --strip-components=1 -xf", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain hoodi --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", + "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" + } + } + }, + "blockbook": { + "package_name": "blockbook-ethereum-testnet-hoodi", + "system_user": "blockbook-ethereum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 3000, + "additional_params": { + "consensusNodeVersion": "http://localhost:17506/eth/v1/node/version", + "eip1559Fees": true, + "mempoolTxTimeoutHours": 12, + "queryBackendOnMempoolResync": false + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_hoodi_archive.json b/configs/coins/ethereum_testnet_hoodi_archive.json new file mode 100644 index 0000000000..627d0a93bb --- /dev/null +++ b/configs/coins/ethereum_testnet_hoodi_archive.json @@ -0,0 +1,77 @@ +{ + "coin": { + "name": "Ethereum Testnet Hoodi Archive", + "shortcut": "tHOD", + "label": "Ethereum Hoodi", + "alias": "ethereum_testnet_hoodi_archive" + }, + "ports": { + "backend_rpc": 18026, + "backend_message_queue": 0, + "backend_p2p": 48326, + "backend_http": 18126, + "backend_torrent": 18126, + "backend_authrpc": 18526, + "blockbook_internal": 19026, + "blockbook_public": 19126 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-ethereum-testnet-hoodi-archive", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "3.0.11", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", + "verification_type": "sha256", + "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", + "extract_command": "tar -C backend --strip-components=1 -xf", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain hoodi --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", + "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" + } + } + }, + "blockbook": { + "package_name": "blockbook-ethereum-testnet-hoodi-archive", + "system_user": "blockbook-ethereum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "-workers=16", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 3000, + "additional_params": { + "consensusNodeVersion": "http://localhost:17526/eth/v1/node/version", + "address_aliases": true, + "eip1559Fees": true, + "mempoolTxTimeoutHours": 12, + "processInternalTransactions": true, + "queryBackendOnMempoolResync": false, + "fiat_rates-disabled": "coingecko", + "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", + "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_hoodi_archive_consensus.json b/configs/coins/ethereum_testnet_hoodi_archive_consensus.json new file mode 100644 index 0000000000..ea3c871dc4 --- /dev/null +++ b/configs/coins/ethereum_testnet_hoodi_archive_consensus.json @@ -0,0 +1,52 @@ +{ + "coin": { + "name": "Ethereum Testnet Hoodi Archive", + "shortcut": "tHOD", + "label": "Ethereum Hoodi", + "alias": "ethereum_testnet_hoodi_archive_consensus", + "execution_alias": "ethereum_testnet_hoodi_archive" + }, + "ports": { + "backend_rpc": 18026, + "backend_message_queue": 0, + "backend_p2p": 48326, + "backend_http": 18126, + "backend_authrpc": 18526, + "blockbook_internal": 19026, + "blockbook_public": 19126 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-ethereum-testnet-hoodi-archive-consensus", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "6.0.4", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", + "verification_type": "sha256", + "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", + "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --hoodi --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17526 --rpc-port=17527 --monitoring-port=17528 --p2p-tcp-port=13626 --p2p-udp-port=12626 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_hoodi_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "wget https://github.com/eth-clients/hoodi/raw/main/metadata/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", + "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/configs/coins/ethereum_testnet_hoodi_consensus.json b/configs/coins/ethereum_testnet_hoodi_consensus.json new file mode 100644 index 0000000000..a9e547e641 --- /dev/null +++ b/configs/coins/ethereum_testnet_hoodi_consensus.json @@ -0,0 +1,52 @@ +{ + "coin": { + "name": "Ethereum Testnet Hoodi", + "shortcut": "tHOD", + "label": "Ethereum Hoodi", + "alias": "ethereum_testnet_hoodi_consensus", + "execution_alias": "ethereum_testnet_hoodi" + }, + "ports": { + "backend_rpc": 18006, + "backend_message_queue": 0, + "backend_p2p": 48306, + "backend_http": 18106, + "backend_authrpc": 18506, + "blockbook_internal": 19006, + "blockbook_public": 19106 + }, + "ipc": { + "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_timeout": 25 + }, + "backend": { + "package_name": "backend-ethereum-testnet-hoodi-consensus", + "package_revision": "satoshilabs-1", + "system_user": "ethereum", + "version": "6.0.4", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", + "verification_type": "sha256", + "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", + "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", + "exclude_files": [], + "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --hoodi --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17506 --rpc-port=17507 --monitoring-port=17508 --p2p-tcp-port=13506 --p2p-udp-port=12506 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_hoodi/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", + "postinst_script_template": "wget https://github.com/eth-clients/holesky/raw/main/metadata/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", + "service_type": "simple", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "", + "client_config_file": "", + "platforms": { + "arm64": { + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", + "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" + } + } + }, + "meta": { + "package_maintainer": "IT", + "package_maintainer_email": "it@satoshilabs.com" + } +} diff --git a/docs/ports.md b/docs/ports.md index b99743c669..18bed15cab 100644 --- a/docs/ports.md +++ b/docs/ports.md @@ -61,7 +61,6 @@ | Arbitrum Nova Archive | 9308 | 9208 | 8308 | 38408 p2p | | Base | 9309 | 9209 | 8309 | 38409 p2p, 8209 http, 8409 authrpc | | Base Archive | 9311 | 9211 | 8211 | 38411 p2p, 8311 http, 8411 authrpc | -| Ethereum Testnet Holesky | 19116 | 19016 | 18016 | 18116 http, 18516 authrpc, 48316 p2p | | Bitcoin Signet | 19120 | 19020 | 18020 | 48320 | | Bitcoin Regtest | 19121 | 19021 | 18021 | 48321 | | Bitcoin Testnet4 | 19129 | 19029 | 18029 | 48329 | @@ -71,7 +70,6 @@ | Dash Testnet | 19133 | 19033 | 18033 | 48333 | | Litecoin Testnet | 19134 | 19034 | 18034 | 48334 | | Bitcoin Gold Testnet | 19135 | 19035 | 18035 | 48335 | -| Ethereum Testnet Holesky Archive | 19136 | 19036 | 18036 | 18136 http, 18136 torrent, 18536 authrpc, 48336 p2p | | Dogecoin Testnet | 19138 | 19038 | 18038 | 48338 | | Vertcoin Testnet | 19140 | 19040 | 18040 | 48340 | | Monacoin Testnet | 19141 | 19041 | 18041 | 48341 | @@ -83,6 +81,10 @@ | Koto Testnet | 19151 | 19051 | 18051 | 48351 | | Decred Testnet | 19161 | 19061 | 18061 | 48361 | | Flo Testnet | 19166 | 19066 | 18066 | 48366 | +| Ethereum Testnet Holesky | 19116 | 19016 | 18016 | 18116 http, 18516 authrpc, 48316 p2p | +| Ethereum Testnet Holesky Archive | 19136 | 19036 | 18036 | 18136 http, 18136 torrent, 18536 authrpc, 48336 p2p | +| Ethereum Testnet Hoodi | 19106 | 19006 | 18006 | 18106 http, 18506 authrpc, 48306 p2p | +| Ethereum Testnet Hoodi Archive | 19126 | 19026 | 18026 | 18126 http, 18126 torrent, 18526 authrpc, 48326 p2p | | Ethereum Testnet Sepolia | 19176 | 19076 | 18076 | 18176 http, 18576 authrpc, 48376 p2p | | Ethereum Testnet Sepolia Archive | 19186 | 19086 | 18086 | 18186 http, 18186 torrent, 18586 authrpc, 48386 p2p | | Qtum Testnet | 19188 | 19088 | 18088 | 48388 | From b4fa97abc541442fb9d98a3b7f7a56c832f44ba3 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 9 Sep 2025 14:19:15 +0200 Subject: [PATCH 510/530] Stop using alternative provider for eth_call and eth_gasPrice --- bchain/coins/eth/contract.go | 12 ------------ bchain/coins/eth/ethrpc.go | 14 -------------- 2 files changed, 26 deletions(-) diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go index 9e95160044..682f4c2cd7 100644 --- a/bchain/coins/eth/contract.go +++ b/bchain/coins/eth/contract.go @@ -283,18 +283,6 @@ func (b *EthereumRPC) EthereumTypeRpcCall(data, to, from string) (string, error) args["from"] = from } - if b.alternativeSendTxProvider != nil { - result, err := b.alternativeSendTxProvider.callHttpStringResult( - b.alternativeSendTxProvider.urls[0], - "eth_call", - args, - "latest", - ) - if err == nil { - return result, nil - } - } - ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) defer cancel() var r string diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 9e738a7f92..c054f41526 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -1005,20 +1005,6 @@ func (b *EthereumRPC) EstimateFee(blocks int) (big.Int, error) { // EstimateSmartFee returns fee estimation func (b *EthereumRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) { - if b.alternativeSendTxProvider != nil { - result, err := b.alternativeSendTxProvider.callHttpStringResult( - b.alternativeSendTxProvider.urls[0], - "eth_gasPrice", - ) - if err == nil { - if strings.HasPrefix(result, "0x") { - gasPrice, err := hexutil.DecodeBig(result) - if err == nil { - return *gasPrice, nil - } - } - } - } ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) defer cancel() var r big.Int From ebe0b46779e5b2a2bf8606bd9889afd0d0a8666c Mon Sep 17 00:00:00 2001 From: f7b Date: Wed, 17 Sep 2025 14:11:33 +0200 Subject: [PATCH 511/530] eth (+testnets) 3.0.11 -> 3.0.17 --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_holesky.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_archive.json | 10 +++++----- configs/coins/ethereum_testnet_hoodi.json | 10 +++++----- configs/coins/ethereum_testnet_hoodi_archive.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 8 files changed, 40 insertions(+), 40 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 76cfa2c25c..fbb77ce881 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.11", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", + "version": "3.0.17", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", + "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", - "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", + "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index acc78e048c..90fce052da 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.11", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", + "version": "3.0.17", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", + "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", - "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", + "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" } } }, diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index df848c452a..70669d85e2 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.11", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", + "version": "3.0.17", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", + "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", - "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", + "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 07d36ed745..2a165b69fa 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.11", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", + "version": "3.0.17", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", + "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", - "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", + "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" } } }, diff --git a/configs/coins/ethereum_testnet_hoodi.json b/configs/coins/ethereum_testnet_hoodi.json index 07ec6ab210..535f4e558b 100644 --- a/configs/coins/ethereum_testnet_hoodi.json +++ b/configs/coins/ethereum_testnet_hoodi.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-hoodi", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.11", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", + "version": "3.0.17", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", + "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain hoodi --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", - "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", + "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" } } }, diff --git a/configs/coins/ethereum_testnet_hoodi_archive.json b/configs/coins/ethereum_testnet_hoodi_archive.json index 627d0a93bb..4770f8edbc 100644 --- a/configs/coins/ethereum_testnet_hoodi_archive.json +++ b/configs/coins/ethereum_testnet_hoodi_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-hoodi-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.11", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", + "version": "3.0.17", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", + "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain hoodi --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", - "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", + "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index af93256c10..bcf034066c 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.11", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", + "version": "3.0.17", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", + "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", - "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", + "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 9440bf053e..a40c697471 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.11", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_amd64.tar.gz", + "version": "3.0.17", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "f046e1e0ffbb460b156dea52023f0fa84efe536edb8d6eb42094b398d710615c", + "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.11/erigon_v3.0.11_linux_arm64.tar.gz", - "verification_source": "c8c3c660187a2848bb8af0a1a65dd4548d8fd9bb46d1e6d2f5eb60854436e6c9" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", + "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" } } }, From 6670f2242a01609d4458fbfd2e710fd5d015a546 Mon Sep 17 00:00:00 2001 From: Emerson Date: Wed, 17 Sep 2025 11:21:23 -0500 Subject: [PATCH 512/530] Resolve Arbitrum, Base, and Zcash build errors (#1325) * Add Docker socket mount to deb-% target for Docker cp extraction This fixes builds that are using Docker in their extract_command: Zcash, Base (+ archive), Base Op-Node (+ archive), Arbitrum (+ archive), Arbitrum Nova (+ archive) * Fix basename error in Makefile template when BinaryURL is empty --------- Co-authored-by: Blake Emerson --- Makefile | 2 +- build/templates/backend/Makefile | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9c01550463..d384f37990 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ deb-blockbook-%: .deb-image docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh blockbook $* $(ARGS) deb-%: .deb-image - docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh all $* $(ARGS) + docker run -t --rm -e PACKAGER=$(PACKAGER) -v /var/run/docker.sock:/var/run/docker.sock -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh all $* $(ARGS) deb-blockbook-all: clean-deb $(addprefix deb-blockbook-, $(TARGETS)) diff --git a/build/templates/backend/Makefile b/build/templates/backend/Makefile index de5440aa8f..570444583f 100644 --- a/build/templates/backend/Makefile +++ b/build/templates/backend/Makefile @@ -1,5 +1,9 @@ {{define "main" -}} +{{- if ne .Backend.BinaryURL "" }} ARCHIVE := $(shell basename {{.Backend.BinaryURL}}) +{{- else }} +ARCHIVE := +{{- end }} all: mkdir backend @@ -34,6 +38,8 @@ all: clean: rm -rf backend +{{- if ne .Backend.BinaryURL "" }} rm -f ${ARCHIVE} +{{- end }} rm -f checksum {{end}} From 4b09caeec948dfa969879591f0dd9925a5990f43 Mon Sep 17 00:00:00 2001 From: Blake Emerson Date: Wed, 3 Sep 2025 21:58:37 -0500 Subject: [PATCH 513/530] Zcash: Zebra 2.5.0 --- configs/coins/zcash.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index cb093ccb16..77fa5aa386 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,10 +22,10 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "2.4.2", - "docker_image": "zfnd/zebra:2.4.2", + "version": "2.5.0", + "docker_image": "zfnd/zebra:2.5.0", "verification_type": "docker", - "verification_source": "3c0a6d7677eec638870a7346f0d0b9205cee761aad35e8990970cd674cd30ae8", + "verification_source": "c57e04a969b630fb5bddea77c4d1246552bb288c70a724d37dcb34f75a21456c", "extract_command": "mkdir backend/bin && docker cp extract:/usr/local/bin/zebrad backend/bin/zebrad", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/zebrad --config {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/zcash.conf start", From 7bd643efa08ee485ae6deb051f965d0a75ea4670 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 25 Sep 2025 11:19:44 +0200 Subject: [PATCH 514/530] Rename ETH contract 0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb to Fluid --- configs/contract-fix/ethereum.json | 36 +++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/configs/contract-fix/ethereum.json b/configs/contract-fix/ethereum.json index 1580329a18..1856a35d44 100644 --- a/configs/contract-fix/ethereum.json +++ b/configs/contract-fix/ethereum.json @@ -1 +1,35 @@ -[{"standard":"ERC20","contract":"0xC19B6A4Ac7C7Cc24459F08984Bbd09664af17bD1","name":"Sensorium","symbol":"SENSO","decimals":0,"createdInBlock":11098997},{"standard":"ERC20","contract":"0xd5F7838F5C461fefF7FE49ea5ebaF7728bB0ADfa","name":"mETH","symbol":"mETH","decimals":18,"createdInBlock":18290587},{"standard":"ERC20","contract":"0xE6829d9a7eE3040e1276Fa75293Bde931859e8fA","name":"cmETH","symbol":"cmETH","decimals":18,"createdInBlock":20439180}] +[ + { + "standard": "ERC20", + "contract": "0xC19B6A4Ac7C7Cc24459F08984Bbd09664af17bD1", + "name": "Sensorium", + "symbol": "SENSO", + "decimals": 0, + "createdInBlock": 11098997 + }, + { + "standard": "ERC20", + "contract": "0xd5F7838F5C461fefF7FE49ea5ebaF7728bB0ADfa", + "name": "mETH", + "symbol": "mETH", + "decimals": 18, + "createdInBlock": 18290587 + }, + { + "standard": "ERC20", + "contract": "0xE6829d9a7eE3040e1276Fa75293Bde931859e8fA", + "name": "cmETH", + "symbol": "cmETH", + "decimals": 18, + "createdInBlock": 20439180 + }, + { + "type": "ERC20", + "standard": "ERC20", + "contract": "0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb", + "name": "Fluid", + "symbol": "FLUID", + "decimals": 18, + "createdInBlock": 12183236 + } +] From 2569d6f97020b14d306f0b834436533f8a7902a0 Mon Sep 17 00:00:00 2001 From: etimofeeva Date: Thu, 11 Sep 2025 15:21:06 +0200 Subject: [PATCH 515/530] return for low even lower fees than 1 sat/vb --- bchain/coins/btc/mempoolspaceblock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bchain/coins/btc/mempoolspaceblock.go b/bchain/coins/btc/mempoolspaceblock.go index 61a9dcada9..1f7d4226d5 100644 --- a/bchain/coins/btc/mempoolspaceblock.go +++ b/bchain/coins/btc/mempoolspaceblock.go @@ -159,7 +159,7 @@ func (p *mempoolSpaceBlockFeeProvider) processData(data *[]mempoolSpaceBlockFeeR } } - if fee < 1 { + if fee <= 0 { glog.Warningf("Skipping block at index %d due to invalid fee: %f", i, fee) continue } From cba50bf4a36b2114bcd9d1d29f58c44eafa787cc Mon Sep 17 00:00:00 2001 From: f7b Date: Mon, 22 Sep 2025 10:08:22 +0200 Subject: [PATCH 516/530] eth (+testnets) 3.0.17 -> 3.1.0 --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_holesky.json | 10 +++++----- configs/coins/ethereum_testnet_holesky_archive.json | 10 +++++----- configs/coins/ethereum_testnet_hoodi.json | 12 ++++++------ configs/coins/ethereum_testnet_hoodi_archive.json | 12 ++++++------ configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 8 files changed, 42 insertions(+), 42 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index fbb77ce881..0591aa3607 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.17", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", + "version": "3.1.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", + "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", - "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", + "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 90fce052da..9de1d05de7 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.17", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", + "version": "3.1.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", + "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", - "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", + "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" } } }, diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json index 70669d85e2..1a573caaa7 100644 --- a/configs/coins/ethereum_testnet_holesky.json +++ b/configs/coins/ethereum_testnet_holesky.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-holesky", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.17", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", + "version": "3.1.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", + "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", - "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", + "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" } } }, diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json index 2a165b69fa..1df3a3e4c2 100644 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ b/configs/coins/ethereum_testnet_holesky_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-holesky-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.17", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", + "version": "3.1.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", + "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", - "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", + "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" } } }, diff --git a/configs/coins/ethereum_testnet_hoodi.json b/configs/coins/ethereum_testnet_hoodi.json index 535f4e558b..a85f3e6784 100644 --- a/configs/coins/ethereum_testnet_hoodi.json +++ b/configs/coins/ethereum_testnet_hoodi.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-hoodi", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.17", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", + "version": "3.1.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", + "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain hoodi --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", - "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", + "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" } } }, @@ -68,4 +68,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_hoodi_archive.json b/configs/coins/ethereum_testnet_hoodi_archive.json index 4770f8edbc..fa8830f58c 100644 --- a/configs/coins/ethereum_testnet_hoodi_archive.json +++ b/configs/coins/ethereum_testnet_hoodi_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-hoodi-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.17", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", + "version": "3.1.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", + "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain hoodi --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", - "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", + "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" } } }, @@ -74,4 +74,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index bcf034066c..cc5ca2379b 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.17", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", + "version": "3.1.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", + "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", - "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", + "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index a40c697471..f03f231c40 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.0.17", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_amd64.tar.gz", + "version": "3.1.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "e1951d4fd5da38b092ffdbecbee35a6c456a736b6e4135b7d43764780f7b40ce", + "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.0.17/erigon_v3.0.17_linux_arm64.tar.gz", - "verification_source": "2607722abd7936df21b4ef9e42c4a13e883b525d272d63581a36df4fab117d64" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", + "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" } } }, From 9f18d57585855a97124d88f6beab49704e0cbff9 Mon Sep 17 00:00:00 2001 From: etimofeeva Date: Fri, 26 Sep 2025 08:38:34 +0200 Subject: [PATCH 517/530] added basic detail to stakingPools --- api/worker.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/api/worker.go b/api/worker.go index d261590e53..135417a609 100644 --- a/api/worker.go +++ b/api/worker.go @@ -1201,10 +1201,12 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto d.totalResults = -1 } // if staking pool enabled, fetch the staking pool details - if details >= AccountDetailsTokenBalances { - d.stakingPools, err = w.getStakingPoolsData(addrDesc) - if err != nil { - return nil, nil, err + if details >= AccountDetailsBasic { + if len(w.chain.EthereumTypeGetSupportedStakingPools()) > 0 { + d.stakingPools, err = w.getStakingPoolsData(addrDesc) + if err != nil { + return nil, nil, err + } } } return ba, &d, nil From 8727e9cd91e703e9e2fabb1339aeceb6353722d9 Mon Sep 17 00:00:00 2001 From: careworry Date: Mon, 28 Apr 2025 19:43:35 +0800 Subject: [PATCH 518/530] refactor: use slices.Clone Signed-off-by: careworry --- common/internalstate.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/internalstate.go b/common/internalstate.go index 2122c1784d..5fb5273809 100644 --- a/common/internalstate.go +++ b/common/internalstate.go @@ -2,6 +2,7 @@ package common import ( "encoding/json" + "slices" "sort" "sync" "sync/atomic" @@ -202,7 +203,7 @@ func (is *InternalState) GetDBColumnStatValues(c int) (int64, int64, int64) { func (is *InternalState) GetAllDBColumnStats() []InternalStateColumn { is.mux.Lock() defer is.mux.Unlock() - return append(is.DbColumns[:0:0], is.DbColumns...) + return slices.Clone(is.DbColumns) } // DBSizeTotal sums the computed sizes of all columns From 39daa172c324ddad9a995468674df4e683cc497a Mon Sep 17 00:00:00 2001 From: wmypku Date: Tue, 26 Aug 2025 16:50:03 +0800 Subject: [PATCH 519/530] refactor: use the built-in max/min to simplify the code Signed-off-by: wmypku --- bchain/baseparser.go | 5 +---- bchain/coins/eth/dataparser.go | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/bchain/baseparser.go b/bchain/baseparser.go index f1278cc34d..9a7dcbea91 100644 --- a/bchain/baseparser.go +++ b/bchain/baseparser.go @@ -47,10 +47,7 @@ func (p *BaseParser) AmountToBigInt(n common.JSONNumber) (big.Int, error) { var r big.Int s := string(n) i := strings.IndexByte(s, '.') - d := p.AmountDecimalPoint - if d > len(zeros) { - d = len(zeros) - } + d := min(p.AmountDecimalPoint, len(zeros)) if i == -1 { s = s + zeros[:d] } else { diff --git a/bchain/coins/eth/dataparser.go b/bchain/coins/eth/dataparser.go index 8182692658..1d06fdda80 100644 --- a/bchain/coins/eth/dataparser.go +++ b/bchain/coins/eth/dataparser.go @@ -51,10 +51,7 @@ func parseSimpleStringProperty(data string) string { // allow string properties as UTF-8 data b, err := hex.DecodeString(data) if err == nil { - i := bytes.Index(b, []byte{0}) - if i > 32 { - i = 32 - } + i := min(bytes.Index(b, []byte{0}), 32) if i > 0 { b = b[:i] } From f2042e1c41dd92991f5dd249ae7da0abb587f3d5 Mon Sep 17 00:00:00 2001 From: JoHnY Date: Wed, 15 Oct 2025 14:55:52 +0200 Subject: [PATCH 520/530] =?UTF-8?q?btc=20(+testnets)=2029.0=20=E2=86=92=20?= =?UTF-8?q?29.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/coins/bitcoin.json | 10 +++++----- configs/coins/bitcoin_regtest.json | 10 +++++----- configs/coins/bitcoin_signet.json | 10 +++++----- configs/coins/bitcoin_testnet.json | 10 +++++----- configs/coins/bitcoin_testnet4.json | 10 +++++----- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/configs/coins/bitcoin.json b/configs/coins/bitcoin.json index 2665f6f627..02e10bb198 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": "29.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-x86_64-linux-gnu.tar.gz", + "version": "29.2", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.2/bitcoin-29.2-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "a681e4f6ce524c338a105f214613605bac6c33d58c31dc5135bbc02bc458bb6c", + "verification_source": "1fd58d0ae94b8a9e21bbaeab7d53395a44976e82bd5492b0a894826c135f9009", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -43,8 +43,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-aarch64-linux-gnu.tar.gz", - "verification_source": "7922ac99363dd28f79e57ef7098581fd48ebd1119b412b07e73b1fd19fd0443f" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.2/bitcoin-29.2-aarch64-linux-gnu.tar.gz", + "verification_source": "f88f72a3c5bf526581aae573be8c1f62133eaecfe3d34646c9ffca7b79dfdc7a" } } }, diff --git a/configs/coins/bitcoin_regtest.json b/configs/coins/bitcoin_regtest.json index 88ceed7e9a..a14a85a66d 100644 --- a/configs/coins/bitcoin_regtest.json +++ b/configs/coins/bitcoin_regtest.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-regtest", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "29.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-x86_64-linux-gnu.tar.gz", + "version": "29.2", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.2/bitcoin-29.2-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "a681e4f6ce524c338a105f214613605bac6c33d58c31dc5135bbc02bc458bb6c", + "verification_source": "1fd58d0ae94b8a9e21bbaeab7d53395a44976e82bd5492b0a894826c135f9009", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-aarch64-linux-gnu.tar.gz", - "verification_source": "7922ac99363dd28f79e57ef7098581fd48ebd1119b412b07e73b1fd19fd0443f" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.2/bitcoin-29.2-aarch64-linux-gnu.tar.gz", + "verification_source": "f88f72a3c5bf526581aae573be8c1f62133eaecfe3d34646c9ffca7b79dfdc7a" } } }, diff --git a/configs/coins/bitcoin_signet.json b/configs/coins/bitcoin_signet.json index c88cccdc66..63f5562a45 100644 --- a/configs/coins/bitcoin_signet.json +++ b/configs/coins/bitcoin_signet.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-signet", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "29.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-x86_64-linux-gnu.tar.gz", + "version": "29.2", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.2/bitcoin-29.2-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "a681e4f6ce524c338a105f214613605bac6c33d58c31dc5135bbc02bc458bb6c", + "verification_source": "1fd58d0ae94b8a9e21bbaeab7d53395a44976e82bd5492b0a894826c135f9009", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-aarch64-linux-gnu.tar.gz", - "verification_source": "7922ac99363dd28f79e57ef7098581fd48ebd1119b412b07e73b1fd19fd0443f" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.2/bitcoin-29.2-aarch64-linux-gnu.tar.gz", + "verification_source": "f88f72a3c5bf526581aae573be8c1f62133eaecfe3d34646c9ffca7b79dfdc7a" } } }, diff --git a/configs/coins/bitcoin_testnet.json b/configs/coins/bitcoin_testnet.json index dbf955bd41..f3db91e270 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": "29.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-x86_64-linux-gnu.tar.gz", + "version": "29.2", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.2/bitcoin-29.2-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "a681e4f6ce524c338a105f214613605bac6c33d58c31dc5135bbc02bc458bb6c", + "verification_source": "1fd58d0ae94b8a9e21bbaeab7d53395a44976e82bd5492b0a894826c135f9009", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-aarch64-linux-gnu.tar.gz", - "verification_source": "7922ac99363dd28f79e57ef7098581fd48ebd1119b412b07e73b1fd19fd0443f" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.2/bitcoin-29.2-aarch64-linux-gnu.tar.gz", + "verification_source": "f88f72a3c5bf526581aae573be8c1f62133eaecfe3d34646c9ffca7b79dfdc7a" } } }, diff --git a/configs/coins/bitcoin_testnet4.json b/configs/coins/bitcoin_testnet4.json index 235aaa6385..4478c3e135 100644 --- a/configs/coins/bitcoin_testnet4.json +++ b/configs/coins/bitcoin_testnet4.json @@ -22,10 +22,10 @@ "package_name": "backend-bitcoin-testnet4", "package_revision": "satoshilabs-1", "system_user": "bitcoin", - "version": "29.0", - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-x86_64-linux-gnu.tar.gz", + "version": "29.2", + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.2/bitcoin-29.2-x86_64-linux-gnu.tar.gz", "verification_type": "sha256", - "verification_source": "a681e4f6ce524c338a105f214613605bac6c33d58c31dc5135bbc02bc458bb6c", + "verification_source": "1fd58d0ae94b8a9e21bbaeab7d53395a44976e82bd5492b0a894826c135f9009", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": ["bin/bitcoin-qt"], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", @@ -42,8 +42,8 @@ }, "platforms": { "arm64": { - "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.0/bitcoin-29.0-aarch64-linux-gnu.tar.gz", - "verification_source": "7922ac99363dd28f79e57ef7098581fd48ebd1119b412b07e73b1fd19fd0443f" + "binary_url": "https://bitcoincore.org/bin/bitcoin-core-29.2/bitcoin-29.2-aarch64-linux-gnu.tar.gz", + "verification_source": "f88f72a3c5bf526581aae573be8c1f62133eaecfe3d34646c9ffca7b79dfdc7a" } } }, From 34a6f9bba92e6646d77688c221b5136bfacb385b Mon Sep 17 00:00:00 2001 From: f7b Date: Mon, 6 Oct 2025 10:47:48 +0200 Subject: [PATCH 521/530] eth (+testnets) 3.1.0 -> 3.2.0 --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_hoodi.json | 10 +++++----- configs/coins/ethereum_testnet_hoodi_archive.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 0591aa3607..8a5a6972b9 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", + "version": "3.2.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", + "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", - "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", + "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 9de1d05de7..20ae00d7c5 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", + "version": "3.2.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", + "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", - "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", + "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" } } }, diff --git a/configs/coins/ethereum_testnet_hoodi.json b/configs/coins/ethereum_testnet_hoodi.json index a85f3e6784..101b792ee3 100644 --- a/configs/coins/ethereum_testnet_hoodi.json +++ b/configs/coins/ethereum_testnet_hoodi.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-hoodi", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", + "version": "3.2.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", + "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain hoodi --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", - "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", + "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" } } }, diff --git a/configs/coins/ethereum_testnet_hoodi_archive.json b/configs/coins/ethereum_testnet_hoodi_archive.json index fa8830f58c..11c86db121 100644 --- a/configs/coins/ethereum_testnet_hoodi_archive.json +++ b/configs/coins/ethereum_testnet_hoodi_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-hoodi-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", + "version": "3.2.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", + "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain hoodi --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", - "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", + "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index cc5ca2379b..4766db8237 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", + "version": "3.2.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", + "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", - "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", + "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index f03f231c40..9a2df2c7fa 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", + "version": "3.2.0", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", + "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", - "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", + "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" } } }, From 4ce39bcdb0dbf9d6dfdea8b94b245bbf4c610a46 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 16 Oct 2025 11:47:33 +0200 Subject: [PATCH 522/530] Escape html in name and symbol shown in explorer --- server/html_templates.go | 7 ++++--- server/html_templates_test.go | 7 +++++++ server/public.go | 5 +++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/server/html_templates.go b/server/html_templates.go index 470134c1ac..b9c9d62e3e 100644 --- a/server/html_templates.go +++ b/server/html_templates.go @@ -3,6 +3,7 @@ package server import ( "encoding/json" "fmt" + "html" "html/template" "math/big" "net/http" @@ -274,7 +275,7 @@ func appendAmountSpan(rv *strings.Builder, class, amount, shortcut, txDate strin } if shortcut != "" { rv.WriteString(" ") - rv.WriteString(shortcut) + rv.WriteString(html.EscapeString(shortcut)) } rv.WriteString("
") } @@ -317,7 +318,7 @@ func appendAmountSpanBitcoinType(rv *strings.Builder, class, amount, shortcut, t rv.WriteString("") if shortcut != "" { rv.WriteString(" ") - rv.WriteString(shortcut) + rv.WriteString(html.EscapeString(shortcut)) } rv.WriteString("") } @@ -331,7 +332,7 @@ func appendAmountWrapperSpan(rv *strings.Builder, primary, symbol, classes strin rv.WriteString(`" cc="`) rv.WriteString(primary) rv.WriteString(" ") - rv.WriteString(symbol) + rv.WriteString(html.EscapeString(symbol)) rv.WriteString(`">`) } diff --git a/server/html_templates_test.go b/server/html_templates_test.go index 4a98d0aabd..eca70f3652 100644 --- a/server/html_templates_test.go +++ b/server/html_templates_test.go @@ -160,6 +160,13 @@ func Test_appendAmountSpan(t *testing.T) { txDate: "2022-03-14", want: `-43141.29 EUR`, }, + { + name: "prim-amt 1.23456789 BTC", + class: "prim-amt", + amount: "1.23456789", + shortcut: "alert(1)", + want: `1.23456789 <javascript>alert(1)</javascript>`, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/server/public.go b/server/public.go index 82650d9b50..e54a307c71 100644 --- a/server/public.go +++ b/server/public.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "html" "html/template" "io" "math/big" @@ -704,11 +705,11 @@ func addressAliasSpan(a string, td *TemplateData) template.HTML { rv.WriteString(a) } else { rv.WriteString(``) - rv.WriteString(alias.Alias) + rv.WriteString(html.EscapeString(alias.Alias)) } rv.WriteString("") return template.HTML(rv.String()) From 9557fa22380be480deeea835484a8bd8ee0763ff Mon Sep 17 00:00:00 2001 From: TheTrunk Date: Wed, 22 Oct 2025 17:29:20 +0800 Subject: [PATCH 523/530] fluxd v9.0.0 --- configs/coins/flux.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/flux.json b/configs/coins/flux.json index ec64a22dab..3bafdcf074 100644 --- a/configs/coins/flux.json +++ b/configs/coins/flux.json @@ -22,10 +22,10 @@ "package_name": "backend-flux", "package_revision": "satoshilabs-1", "system_user": "flux", - "version": "8.0.0", - "binary_url": "https://github.com/RunOnFlux/fluxd/releases/download/v8.0.0/Flux-amd64-v8.0.0.tar.gz", + "version": "9.0.0", + "binary_url": "https://github.com/RunOnFlux/fluxd/releases/download/v9.0.0/Flux-amd64-v9.0.0.tar.gz", "verification_type": "sha256", - "verification_source": "c9e579fb39f78d2c15190bf8350745344efa4eff5563bba4221d6f20d536848a", + "verification_source": "3d37ad5c769195c9ce6d6d0ee613eb9852de0cb9ee3779c9da7d9f9e51cd285e", "extract_command": "tar -C backend -xf", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/fluxd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", From 65334fb82595af30305455f2413e6b06ec6ae160 Mon Sep 17 00:00:00 2001 From: justanwar <42809091+justanwar@users.noreply.github.com> Date: Mon, 20 Oct 2025 18:52:21 +0800 Subject: [PATCH 524/530] Update Firo daemon 0.14.15.0 (mandatory) --- configs/coins/firo.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/firo.json b/configs/coins/firo.json index 41d0ebe335..26458b3ed4 100644 --- a/configs/coins/firo.json +++ b/configs/coins/firo.json @@ -22,10 +22,10 @@ "package_name": "backend-firo", "package_revision": "satoshilabs-1", "system_user": "firo", - "version": "0.14.14.1", - "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.14.1/firo-0.14.14.1-linux64.tar.gz", + "version": "0.14.15.0", + "binary_url": "https://github.com/firoorg/firo/releases/download/v0.14.15.0/firo-0.14.15.0-linux64.tar.gz", "verification_type": "sha256", - "verification_source": "059da5f978bbda1615fdbd1a16a0e854c4c6a15480b2d50de4a8911f6f5b4636", + "verification_source": "6a601e7c1aa0af4aee3b28a7fbd365a1d749d2203e8d042bcccf9f950072ecd9", "extract_command": "tar -C backend --strip 1 -xf", "exclude_files": [ "bin/firo-qt", From b28f0eaab4009a2e89b8c749a34e0fafd7e6441d Mon Sep 17 00:00:00 2001 From: f7b Date: Mon, 20 Oct 2025 10:23:11 +0200 Subject: [PATCH 525/530] eth (+testnets) 3.2.0 -> 3.2.1 --- configs/coins/ethereum.json | 10 +++++----- configs/coins/ethereum_archive.json | 10 +++++----- configs/coins/ethereum_testnet_hoodi.json | 10 +++++----- configs/coins/ethereum_testnet_hoodi_archive.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia.json | 10 +++++----- configs/coins/ethereum_testnet_sepolia_archive.json | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index 8a5a6972b9..ef0f93d283 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.2.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", + "version": "3.2.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", + "verification_source": "8b5444988667721f2b2ef1ab3098139c31f722492992939c110813408c39dc7c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", - "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_arm64.tar.gz", + "verification_source": "19a91709dc3ddbe947c4f81e70cb1de49044954e21f441e9ea46b3696f21b57f" } } }, diff --git a/configs/coins/ethereum_archive.json b/configs/coins/ethereum_archive.json index 20ae00d7c5..abe1fc4733 100644 --- a/configs/coins/ethereum_archive.json +++ b/configs/coins/ethereum_archive.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.2.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", + "version": "3.2.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", + "verification_source": "8b5444988667721f2b2ef1ab3098139c31f722492992939c110813408c39dc7c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain mainnet --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", - "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_arm64.tar.gz", + "verification_source": "19a91709dc3ddbe947c4f81e70cb1de49044954e21f441e9ea46b3696f21b57f" } } }, diff --git a/configs/coins/ethereum_testnet_hoodi.json b/configs/coins/ethereum_testnet_hoodi.json index 101b792ee3..21bbee5ab7 100644 --- a/configs/coins/ethereum_testnet_hoodi.json +++ b/configs/coins/ethereum_testnet_hoodi.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-hoodi", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.2.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", + "version": "3.2.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", + "verification_source": "8b5444988667721f2b2ef1ab3098139c31f722492992939c110813408c39dc7c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain hoodi --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", - "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_arm64.tar.gz", + "verification_source": "19a91709dc3ddbe947c4f81e70cb1de49044954e21f441e9ea46b3696f21b57f" } } }, diff --git a/configs/coins/ethereum_testnet_hoodi_archive.json b/configs/coins/ethereum_testnet_hoodi_archive.json index 11c86db121..b6c0f5a2de 100644 --- a/configs/coins/ethereum_testnet_hoodi_archive.json +++ b/configs/coins/ethereum_testnet_hoodi_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-hoodi-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.2.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", + "version": "3.2.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", + "verification_source": "8b5444988667721f2b2ef1ab3098139c31f722492992939c110813408c39dc7c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain hoodi --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", - "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_arm64.tar.gz", + "verification_source": "19a91709dc3ddbe947c4f81e70cb1de49044954e21f441e9ea46b3696f21b57f" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia.json b/configs/coins/ethereum_testnet_sepolia.json index 4766db8237..18511edbec 100644 --- a/configs/coins/ethereum_testnet_sepolia.json +++ b/configs/coins/ethereum_testnet_sepolia.json @@ -22,10 +22,10 @@ "package_name": "backend-ethereum-testnet-sepolia", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.2.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", + "version": "3.2.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", + "verification_source": "8b5444988667721f2b2ef1ab3098139c31f722492992939c110813408c39dc7c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -39,8 +39,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", - "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_arm64.tar.gz", + "verification_source": "19a91709dc3ddbe947c4f81e70cb1de49044954e21f441e9ea46b3696f21b57f" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_archive.json b/configs/coins/ethereum_testnet_sepolia_archive.json index 9a2df2c7fa..809f3f6ce5 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive.json +++ b/configs/coins/ethereum_testnet_sepolia_archive.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "3.2.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_amd64.tar.gz", + "version": "3.2.1", + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_amd64.tar.gz", "verification_type": "sha256", - "verification_source": "ffbc4724d262b439157531ca33fc83b50c00fb8ed32e95f16323c37240a7287e", + "verification_source": "8b5444988667721f2b2ef1ab3098139c31f722492992939c110813408c39dc7c", "extract_command": "tar -C backend --strip-components=1 -xf", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain sepolia --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.0/erigon_v3.2.0_linux_arm64.tar.gz", - "verification_source": "9d4d7da69924f449ef0be40575e92bf0949724ea666db05eb34a33f065ceec67" + "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.2.1/erigon_v3.2.1_linux_arm64.tar.gz", + "verification_source": "19a91709dc3ddbe947c4f81e70cb1de49044954e21f441e9ea46b3696f21b57f" } } }, From c5fca2ae14f247cdadf37f56e18b6c8f078d64af Mon Sep 17 00:00:00 2001 From: f7b Date: Mon, 29 Sep 2025 12:16:56 +0200 Subject: [PATCH 526/530] prysm (+testnets) 6.0.4 -> 6.1.0, holesky decommisioned --- configs/coins/ethereum_archive_consensus.json | 10 +-- configs/coins/ethereum_consensus.json | 10 +-- configs/coins/ethereum_testnet_holesky.json | 71 ----------------- .../ethereum_testnet_holesky_archive.json | 76 ------------------- ...eum_testnet_holesky_archive_consensus.json | 52 ------------- .../ethereum_testnet_holesky_consensus.json | 52 ------------- ...ereum_testnet_hoodi_archive_consensus.json | 12 +-- .../ethereum_testnet_hoodi_consensus.json | 12 +-- ...eum_testnet_sepolia_archive_consensus.json | 10 +-- .../ethereum_testnet_sepolia_consensus.json | 10 +-- 10 files changed, 32 insertions(+), 283 deletions(-) delete mode 100644 configs/coins/ethereum_testnet_holesky.json delete mode 100644 configs/coins/ethereum_testnet_holesky_archive.json delete mode 100644 configs/coins/ethereum_testnet_holesky_archive_consensus.json delete mode 100644 configs/coins/ethereum_testnet_holesky_consensus.json diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index d3b1e20ef5..9c7c14ab76 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.4", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", + "version": "6.1.0", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", + "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", - "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", + "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" } } }, diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 79fc973e06..378d02c114 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.4", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", + "version": "6.1.0", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", + "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", - "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", + "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" } } }, diff --git a/configs/coins/ethereum_testnet_holesky.json b/configs/coins/ethereum_testnet_holesky.json deleted file mode 100644 index 1a573caaa7..0000000000 --- a/configs/coins/ethereum_testnet_holesky.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "coin": { - "name": "Ethereum Testnet Holesky", - "shortcut": "tHOL", - "label": "Ethereum Holesky", - "alias": "ethereum_testnet_holesky" - }, - "ports": { - "backend_rpc": 18016, - "backend_message_queue": 0, - "backend_p2p": 48316, - "backend_http": 18116, - "backend_authrpc": 18516, - "blockbook_internal": 19016, - "blockbook_public": 19116 - }, - "ipc": { - "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", - "rpc_timeout": 25 - }, - "backend": { - "package_name": "backend-ethereum-testnet-holesky", - "package_revision": "satoshilabs-1", - "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", - "verification_type": "sha256", - "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", - "extract_command": "tar -C backend --strip-components=1 -xf", - "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode full --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", - "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "", - "service_type": "simple", - "service_additional_params_template": "", - "protect_memory": true, - "mainnet": false, - "server_config_file": "", - "client_config_file": "", - "platforms": { - "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", - "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" - } - } - }, - "blockbook": { - "package_name": "blockbook-ethereum-testnet-holesky", - "system_user": "blockbook-ethereum", - "internal_binding_template": ":{{.Ports.BlockbookInternal}}", - "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "", - "additional_params": "", - "block_chain": { - "parse": true, - "mempool_workers": 8, - "mempool_sub_workers": 2, - "block_addresses_to_keep": 3000, - "additional_params": { - "consensusNodeVersion": "http://localhost:17516/eth/v1/node/version", - "eip1559Fees": true, - "mempoolTxTimeoutHours": 12, - "queryBackendOnMempoolResync": false - } - } - }, - "meta": { - "package_maintainer": "IT", - "package_maintainer_email": "it@satoshilabs.com" - } -} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_holesky_archive.json b/configs/coins/ethereum_testnet_holesky_archive.json deleted file mode 100644 index 1df3a3e4c2..0000000000 --- a/configs/coins/ethereum_testnet_holesky_archive.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "coin": { - "name": "Ethereum Testnet Holesky Archive", - "shortcut": "tHOL", - "label": "Ethereum Holesky", - "alias": "ethereum_testnet_holesky_archive" - }, - "ports": { - "backend_rpc": 18036, - "backend_message_queue": 0, - "backend_p2p": 48336, - "backend_http": 18136, - "backend_torrent": 18136, - "backend_authrpc": 18536, - "blockbook_internal": 19036, - "blockbook_public": 19136 - }, - "ipc": { - "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", - "rpc_timeout": 25 - }, - "backend": { - "package_name": "backend-ethereum-testnet-holesky-archive", - "package_revision": "satoshilabs-1", - "system_user": "ethereum", - "version": "3.1.0", - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_amd64.tar.gz", - "verification_type": "sha256", - "verification_source": "45a9c4594b754750c4e3631b0c86b9da5b61f91b0241d824a9eed61aed040154", - "extract_command": "tar -C backend --strip-components=1 -xf", - "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/erigon --chain holesky --snap.keepblocks --db.size.limit 15TB --db.pagesize 16KB --prune.mode archive --externalcl --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/erigon --port {{.Ports.BackendP2P}} --ws --ws.port {{.Ports.BackendRPC}} --http --http.port {{.Ports.BackendRPC}} --http.addr 127.0.0.1 --http.corsdomain \"*\" --http.vhosts \"*\" --http.api \"eth,net,web3,debug,txpool\" --authrpc.port {{.Ports.BackendAuthRpc}} --private.api.addr \"\" --torrent.port {{.Ports.BackendHttp}} --log.dir.path {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log.dir.prefix {{.Coin.Alias}}'", - "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "", - "service_type": "simple", - "service_additional_params_template": "", - "protect_memory": true, - "mainnet": false, - "server_config_file": "", - "client_config_file": "", - "platforms": { - "arm64": { - "binary_url": "https://github.com/erigontech/erigon/releases/download/v3.1.0/erigon_v3.1.0_linux_arm64.tar.gz", - "verification_source": "a613c211888784e5fb3ed1376cd47984d08980b6d7eb6c590af628592ca87311" - } - } - }, - "blockbook": { - "package_name": "blockbook-ethereum-testnet-holesky-archive", - "system_user": "blockbook-ethereum", - "internal_binding_template": ":{{.Ports.BlockbookInternal}}", - "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "", - "additional_params": "-workers=16", - "block_chain": { - "parse": true, - "mempool_workers": 8, - "mempool_sub_workers": 2, - "block_addresses_to_keep": 3000, - "additional_params": { - "consensusNodeVersion": "http://localhost:17536/eth/v1/node/version", - "address_aliases": true, - "mempoolTxTimeoutHours": 12, - "processInternalTransactions": true, - "queryBackendOnMempoolResync": false, - "fiat_rates-disabled": "coingecko", - "fiat_rates_params": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}", - "fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/" - } - } - }, - "meta": { - "package_maintainer": "IT", - "package_maintainer_email": "it@satoshilabs.com" - } -} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_holesky_archive_consensus.json b/configs/coins/ethereum_testnet_holesky_archive_consensus.json deleted file mode 100644 index 5d92537bbd..0000000000 --- a/configs/coins/ethereum_testnet_holesky_archive_consensus.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "coin": { - "name": "Ethereum Testnet Holesky Archive", - "shortcut": "tHOL", - "label": "Ethereum Holesky", - "alias": "ethereum_testnet_holesky_archive_consensus", - "execution_alias": "ethereum_testnet_holesky_archive" - }, - "ports": { - "backend_rpc": 18036, - "backend_message_queue": 0, - "backend_p2p": 48336, - "backend_http": 18136, - "backend_authrpc": 18536, - "blockbook_internal": 19036, - "blockbook_public": 19136 - }, - "ipc": { - "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", - "rpc_timeout": 25 - }, - "backend": { - "package_name": "backend-ethereum-testnet-holesky-archive-consensus", - "package_revision": "satoshilabs-1", - "system_user": "ethereum", - "version": "6.0.4", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", - "verification_type": "sha256", - "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", - "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", - "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17536 --rpc-port=17537 --monitoring-port=17538 --p2p-tcp-port=13636 --p2p-udp-port=12636 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", - "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://github.com/eth-clients/holesky/raw/main/metadata/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", - "service_type": "simple", - "service_additional_params_template": "", - "protect_memory": true, - "mainnet": false, - "server_config_file": "", - "client_config_file": "", - "platforms": { - "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", - "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" - } - } - }, - "meta": { - "package_maintainer": "IT", - "package_maintainer_email": "it@satoshilabs.com" - } -} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_holesky_consensus.json b/configs/coins/ethereum_testnet_holesky_consensus.json deleted file mode 100644 index 36ea18def5..0000000000 --- a/configs/coins/ethereum_testnet_holesky_consensus.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "coin": { - "name": "Ethereum Testnet Holesky", - "shortcut": "tHOL", - "label": "Ethereum Holesky", - "alias": "ethereum_testnet_holesky_consensus", - "execution_alias": "ethereum_testnet_holesky" - }, - "ports": { - "backend_rpc": 18016, - "backend_message_queue": 0, - "backend_p2p": 48316, - "backend_http": 18116, - "backend_authrpc": 18516, - "blockbook_internal": 19016, - "blockbook_public": 19116 - }, - "ipc": { - "rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}", - "rpc_timeout": 25 - }, - "backend": { - "package_name": "backend-ethereum-testnet-holesky-consensus", - "package_revision": "satoshilabs-1", - "system_user": "ethereum", - "version": "6.0.4", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", - "verification_type": "sha256", - "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", - "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", - "exclude_files": [], - "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --holesky --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17516 --rpc-port=17517 --monitoring-port=17518 --p2p-tcp-port=13516 --p2p-udp-port=12516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_holesky/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", - "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log", - "postinst_script_template": "wget https://github.com/eth-clients/holesky/raw/main/metadata/genesis.ssz -O {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz", - "service_type": "simple", - "service_additional_params_template": "", - "protect_memory": true, - "mainnet": false, - "server_config_file": "", - "client_config_file": "", - "platforms": { - "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", - "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" - } - } - }, - "meta": { - "package_maintainer": "IT", - "package_maintainer_email": "it@satoshilabs.com" - } -} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_hoodi_archive_consensus.json b/configs/coins/ethereum_testnet_hoodi_archive_consensus.json index ea3c871dc4..2530ac6e94 100644 --- a/configs/coins/ethereum_testnet_hoodi_archive_consensus.json +++ b/configs/coins/ethereum_testnet_hoodi_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-hoodi-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.4", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", + "version": "6.1.0", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", + "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --hoodi --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17526 --rpc-port=17527 --monitoring-port=17528 --p2p-tcp-port=13626 --p2p-udp-port=12626 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_hoodi_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", - "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", + "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" } } }, @@ -49,4 +49,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_hoodi_consensus.json b/configs/coins/ethereum_testnet_hoodi_consensus.json index a9e547e641..5d0f5307b9 100644 --- a/configs/coins/ethereum_testnet_hoodi_consensus.json +++ b/configs/coins/ethereum_testnet_hoodi_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-hoodi-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.4", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", + "version": "6.1.0", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", + "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --hoodi --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17506 --rpc-port=17507 --monitoring-port=17508 --p2p-tcp-port=13506 --p2p-udp-port=12506 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_hoodi/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", - "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", + "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" } } }, @@ -49,4 +49,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} +} \ No newline at end of file diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json index 57f28b4114..5409b869f7 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.4", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", + "version": "6.1.0", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", + "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", - "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", + "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" } } }, diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json index 54de2069b3..ca2ba72e2a 100644 --- a/configs/coins/ethereum_testnet_sepolia_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.0.4", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-amd64", + "version": "6.1.0", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", "verification_type": "sha256", - "verification_source": "5be75a5b5bb8654420eaba215f1138236395fe7fc6182329079c28dc5217258e", + "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.0.4/beacon-chain-v6.0.4-linux-arm64", - "verification_source": "24b0fd2efe77f77f7c690e73d408ea42e4de355472d386f6d8da19c216afad44" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", + "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" } } }, From 674271fec772a6ccd24b835032e40523e3085b36 Mon Sep 17 00:00:00 2001 From: AlexanderPavlenko Date: Wed, 15 Oct 2025 16:48:21 +0400 Subject: [PATCH 527/530] prysm (+testnets) 6.1.0 -> 6.1.2 --- configs/coins/ethereum_archive_consensus.json | 12 ++++++------ configs/coins/ethereum_consensus.json | 12 ++++++------ .../ethereum_testnet_hoodi_archive_consensus.json | 12 ++++++------ configs/coins/ethereum_testnet_hoodi_consensus.json | 12 ++++++------ .../ethereum_testnet_sepolia_archive_consensus.json | 12 ++++++------ .../coins/ethereum_testnet_sepolia_consensus.json | 12 ++++++------ 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/configs/coins/ethereum_archive_consensus.json b/configs/coins/ethereum_archive_consensus.json index 9c7c14ab76..e5f1f154a1 100644 --- a/configs/coins/ethereum_archive_consensus.json +++ b/configs/coins/ethereum_archive_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.1.0", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", + "version": "6.1.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", + "verification_source": "45d34c817db22e34ae12ebe733d281db76a349e3be439952f9e1dd50f10bc2b1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7516 --rpc-port=7517 --monitoring-port=7518 --p2p-tcp-port=3516 --p2p-udp-port=2516 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_archive/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", - "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-arm64", + "verification_source": "2651f1407bb842e7f03dc00ba58990ee3345865cb5d474a3f76a968db5e57c02" } } }, @@ -45,4 +45,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_consensus.json b/configs/coins/ethereum_consensus.json index 378d02c114..4288d87db7 100644 --- a/configs/coins/ethereum_consensus.json +++ b/configs/coins/ethereum_consensus.json @@ -19,10 +19,10 @@ "package_name": "backend-ethereum-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.1.0", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", + "version": "6.1.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", + "verification_source": "45d34c817db22e34ae12ebe733d281db76a349e3be439952f9e1dd50f10bc2b1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --mainnet --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=7536 --rpc-port=7537 --monitoring-port=7538 --p2p-tcp-port=3536 --p2p-udp-port=2536 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum/backend/erigon/jwt.hex 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -36,8 +36,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", - "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-arm64", + "verification_source": "2651f1407bb842e7f03dc00ba58990ee3345865cb5d474a3f76a968db5e57c02" } } }, @@ -45,4 +45,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_hoodi_archive_consensus.json b/configs/coins/ethereum_testnet_hoodi_archive_consensus.json index 2530ac6e94..8864249adc 100644 --- a/configs/coins/ethereum_testnet_hoodi_archive_consensus.json +++ b/configs/coins/ethereum_testnet_hoodi_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-hoodi-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.1.0", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", + "version": "6.1.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", + "verification_source": "45d34c817db22e34ae12ebe733d281db76a349e3be439952f9e1dd50f10bc2b1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --hoodi --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17526 --rpc-port=17527 --monitoring-port=17528 --p2p-tcp-port=13626 --p2p-udp-port=12626 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_hoodi_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", - "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-arm64", + "verification_source": "2651f1407bb842e7f03dc00ba58990ee3345865cb5d474a3f76a968db5e57c02" } } }, @@ -49,4 +49,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_hoodi_consensus.json b/configs/coins/ethereum_testnet_hoodi_consensus.json index 5d0f5307b9..1c50970658 100644 --- a/configs/coins/ethereum_testnet_hoodi_consensus.json +++ b/configs/coins/ethereum_testnet_hoodi_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-hoodi-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.1.0", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", + "version": "6.1.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", + "verification_source": "45d34c817db22e34ae12ebe733d281db76a349e3be439952f9e1dd50f10bc2b1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --hoodi --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17506 --rpc-port=17507 --monitoring-port=17508 --p2p-tcp-port=13506 --p2p-udp-port=12506 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_hoodi/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", - "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-arm64", + "verification_source": "2651f1407bb842e7f03dc00ba58990ee3345865cb5d474a3f76a968db5e57c02" } } }, @@ -49,4 +49,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json index 5409b869f7..3455cc1fd6 100644 --- a/configs/coins/ethereum_testnet_sepolia_archive_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_archive_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-archive-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.1.0", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", + "version": "6.1.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", + "verification_source": "45d34c817db22e34ae12ebe733d281db76a349e3be439952f9e1dd50f10bc2b1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17586 --rpc-port=17587 --monitoring-port=17548 --p2p-tcp-port=13676 --p2p-udp-port=12676 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia_archive/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", - "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-arm64", + "verification_source": "2651f1407bb842e7f03dc00ba58990ee3345865cb5d474a3f76a968db5e57c02" } } }, @@ -49,4 +49,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} diff --git a/configs/coins/ethereum_testnet_sepolia_consensus.json b/configs/coins/ethereum_testnet_sepolia_consensus.json index ca2ba72e2a..b26f323e3c 100644 --- a/configs/coins/ethereum_testnet_sepolia_consensus.json +++ b/configs/coins/ethereum_testnet_sepolia_consensus.json @@ -23,10 +23,10 @@ "package_name": "backend-ethereum-testnet-sepolia-consensus", "package_revision": "satoshilabs-1", "system_user": "ethereum", - "version": "6.1.0", - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-amd64", + "version": "6.1.2", + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-amd64", "verification_type": "sha256", - "verification_source": "c5c46ecfe4cc45acdcf087edf5108c529ad2b3fc5f17d407b426a10e595fca3f", + "verification_source": "45d34c817db22e34ae12ebe733d281db76a349e3be439952f9e1dd50f10bc2b1", "extract_command": "mv ${ARCHIVE} backend/beacon-chain && chmod +x backend/beacon-chain && echo", "exclude_files": [], "exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/beacon-chain --sepolia --accept-terms-of-use --execution-endpoint=http://localhost:{{.Ports.BackendAuthRpc}} --grpc-gateway-port=17576 --rpc-port=17577 --monitoring-port=17578 --p2p-tcp-port=13576 --p2p-udp-port=12576 --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --jwt-secret={{.Env.BackendDataPath}}/ethereum_testnet_sepolia/backend/erigon/jwt.hex --genesis-state={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/genesis.ssz 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'", @@ -40,8 +40,8 @@ "client_config_file": "", "platforms": { "arm64": { - "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.0/beacon-chain-v6.1.0-linux-arm64", - "verification_source": "9bafddd4a2bdfc0ebf9f7531b9ed22df590e486ecbd1507ac866d8bfb3ca6896" + "binary_url": "https://github.com/OffchainLabs/prysm/releases/download/v6.1.2/beacon-chain-v6.1.2-linux-arm64", + "verification_source": "2651f1407bb842e7f03dc00ba58990ee3345865cb5d474a3f76a968db5e57c02" } } }, @@ -49,4 +49,4 @@ "package_maintainer": "IT", "package_maintainer_email": "it@satoshilabs.com" } -} \ No newline at end of file +} From d6a15b69f6725874d39b2bc005bd415dbb80cf85 Mon Sep 17 00:00:00 2001 From: Blake Emerson Date: Tue, 18 Nov 2025 15:45:41 -0600 Subject: [PATCH 528/530] Zcash: Upgrade to zebra v3.0.0 --- configs/coins/zcash.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/coins/zcash.json b/configs/coins/zcash.json index 77fa5aa386..d2ec7ed719 100644 --- a/configs/coins/zcash.json +++ b/configs/coins/zcash.json @@ -22,10 +22,10 @@ "package_name": "backend-zcash", "package_revision": "satoshilabs-1", "system_user": "zcash", - "version": "2.5.0", - "docker_image": "zfnd/zebra:2.5.0", + "version": "3.0.0", + "docker_image": "zfnd/zebra:3.0.0", "verification_type": "docker", - "verification_source": "c57e04a969b630fb5bddea77c4d1246552bb288c70a724d37dcb34f75a21456c", + "verification_source": "ec082c6c3fb26b1cbb4aa0f044406dc0cfbc8ce5f3c3e5ff5f9886d832becac9", "extract_command": "mkdir backend/bin && docker cp extract:/usr/local/bin/zebrad backend/bin/zebrad", "exclude_files": [], "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/zebrad --config {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/zcash.conf start", From 964662d578825e90e9608ace0f4ac91771c2d6b1 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Mon, 24 Nov 2025 02:24:39 +0100 Subject: [PATCH 529/530] Upgrade golang to 1.25 and dependencies, fix Avalanche sync --- bchain/coins/avalanche/types.go | 123 ++------------------------ build/docker/bin/Dockerfile | 2 +- go.mod | 54 ++++++------ go.sum | 150 +++++++++++++++++--------------- 4 files changed, 114 insertions(+), 215 deletions(-) diff --git a/bchain/coins/avalanche/types.go b/bchain/coins/avalanche/types.go index 69c7a88727..c07ebab244 100644 --- a/bchain/coins/avalanche/types.go +++ b/bchain/coins/avalanche/types.go @@ -3,33 +3,16 @@ package avalanche import ( "encoding/json" "errors" - "io" "math/big" - "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/crypto/sha3" ) -var hasherPool = sync.Pool{ - New: func() interface{} { return sha3.NewLegacyKeccak256() }, -} - -func rlpHash(x interface{}) (h common.Hash) { - sha := hasherPool.Get().(crypto.KeccakState) - defer hasherPool.Put(sha) - sha.Reset() - _ = rlp.Encode(sha, x) - _, _ = sha.Read(h[:]) - return h -} - // Header represents a block header in the Avalanche blockchain. type Header struct { + RpcHash common.Hash `json:"hash" gencodec:"required"` ParentHash common.Hash `json:"parentHash" gencodec:"required"` UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` Coinbase common.Address `json:"miner" gencodec:"required"` @@ -128,6 +111,7 @@ func (h Header) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (h *Header) UnmarshalJSON(input []byte) error { type Header struct { + RpcHash *common.Hash `json:"hash"` ParentHash *common.Hash `json:"parentHash" gencodec:"required"` UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` Coinbase *common.Address `json:"miner" gencodec:"required"` @@ -155,6 +139,10 @@ func (h *Header) UnmarshalJSON(input []byte) error { if err := json.Unmarshal(input, &dec); err != nil { return err } + if dec.RpcHash == nil { + return errors.New("missing required field 'hash' for Header") + } + h.RpcHash = *dec.RpcHash if dec.ParentHash == nil { return errors.New("missing required field 'parentHash' for Header") } @@ -238,102 +226,7 @@ func (h *Header) UnmarshalJSON(input []byte) error { return nil } -func (obj *Header) EncodeRLP(_w io.Writer) error { - w := rlp.NewEncoderBuffer(_w) - _tmp0 := w.List() - w.WriteBytes(obj.ParentHash[:]) - w.WriteBytes(obj.UncleHash[:]) - w.WriteBytes(obj.Coinbase[:]) - w.WriteBytes(obj.Root[:]) - w.WriteBytes(obj.TxHash[:]) - w.WriteBytes(obj.ReceiptHash[:]) - w.WriteBytes(obj.Bloom[:]) - if obj.Difficulty == nil { - _, _ = w.Write(rlp.EmptyString) - } else { - if obj.Difficulty.Sign() == -1 { - return rlp.ErrNegativeBigInt - } - w.WriteBigInt(obj.Difficulty) - } - if obj.Number == nil { - _, _ = w.Write(rlp.EmptyString) - } else { - if obj.Number.Sign() == -1 { - return rlp.ErrNegativeBigInt - } - w.WriteBigInt(obj.Number) - } - w.WriteUint64(obj.GasLimit) - w.WriteUint64(obj.GasUsed) - w.WriteUint64(obj.Time) - w.WriteBytes(obj.Extra) - w.WriteBytes(obj.MixDigest[:]) - w.WriteBytes(obj.Nonce[:]) - w.WriteBytes(obj.ExtDataHash[:]) - _tmp1 := obj.BaseFee != nil - _tmp2 := obj.ExtDataGasUsed != nil - _tmp3 := obj.BlockGasCost != nil - _tmp4 := obj.BlobGasUsed != nil - _tmp5 := obj.ExcessBlobGas != nil - _tmp6 := obj.ParentBeaconRoot != nil - if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 { - if obj.BaseFee == nil { - _, _ = w.Write(rlp.EmptyString) - } else { - if obj.BaseFee.Sign() == -1 { - return rlp.ErrNegativeBigInt - } - w.WriteBigInt(obj.BaseFee) - } - } - if _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 { - if obj.ExtDataGasUsed == nil { - _, _ = w.Write(rlp.EmptyString) - } else { - if obj.ExtDataGasUsed.Sign() == -1 { - return rlp.ErrNegativeBigInt - } - w.WriteBigInt(obj.ExtDataGasUsed) - } - } - if _tmp3 || _tmp4 || _tmp5 || _tmp6 { - if obj.BlockGasCost == nil { - _, _ = w.Write(rlp.EmptyString) - } else { - if obj.BlockGasCost.Sign() == -1 { - return rlp.ErrNegativeBigInt - } - w.WriteBigInt(obj.BlockGasCost) - } - } - if _tmp4 || _tmp5 || _tmp6 { - if obj.BlobGasUsed == nil { - _, _ = w.Write([]byte{0x80}) - } else { - w.WriteUint64((*obj.BlobGasUsed)) - } - } - if _tmp5 || _tmp6 { - if obj.ExcessBlobGas == nil { - _, _ = w.Write([]byte{0x80}) - } else { - w.WriteUint64((*obj.ExcessBlobGas)) - } - } - if _tmp6 { - if obj.ParentBeaconRoot == nil { - _, _ = w.Write([]byte{0x80}) - } else { - w.WriteBytes(obj.ParentBeaconRoot[:]) - } - } - w.ListEnd(_tmp0) - return w.Flush() -} - -// Hash returns the block hash of the header, which is simply the keccak256 hash of its -// RLP encoding. +// Hash returns the block hash of the header func (h *Header) Hash() common.Hash { - return rlpHash(h) + return h.RpcHash } diff --git a/build/docker/bin/Dockerfile b/build/docker/bin/Dockerfile index df4670a3fc..07e4254dae 100644 --- a/build/docker/bin/Dockerfile +++ b/build/docker/bin/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update && \ libzstd-dev liblz4-dev graphviz && \ apt-get clean ARG GOLANG_VERSION -ENV GOLANG_VERSION=go1.23.7 +ENV GOLANG_VERSION=go1.25.4 ENV ROCKSDB_VERSION=v9.10.0 ENV GOPATH=/go ENV PATH=$PATH:$GOPATH/bin diff --git a/go.mod b/go.mod index 1958e8d74f..0b44d03671 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module github.com/trezor/blockbook -go 1.23.0 +go 1.25.0 require ( - github.com/ava-labs/avalanchego v1.12.1 + github.com/ava-labs/avalanchego v1.14.0 github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e github.com/deckarep/golang-set v1.8.0 github.com/decred/dcrd/chaincfg/chainhash v1.0.2 @@ -13,7 +13,7 @@ require ( github.com/decred/dcrd/dcrutil/v3 v3.0.0 github.com/decred/dcrd/hdkeychain/v3 v3.0.0 github.com/decred/dcrd/txscript/v3 v3.0.0 - github.com/ethereum/go-ethereum v1.15.5 + github.com/ethereum/go-ethereum v1.16.7 github.com/golang/glog v1.2.1 github.com/gorilla/websocket v1.5.0 github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 @@ -25,67 +25,63 @@ require ( github.com/pebbe/zmq4 v1.2.1 github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29 github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e - github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/client_golang v1.23.2 github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a github.com/tkrajina/typescriptify-golang-structs v0.1.11 - golang.org/x/crypto v0.32.0 - google.golang.org/protobuf v1.34.2 + golang.org/x/crypto v0.43.0 + google.golang.org/protobuf v1.36.10 ) require ( github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29 // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/aead/siphash v1.0.1 // indirect github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.17.0 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/consensys/bavard v0.1.22 // indirect - github.com/consensys/gnark-crypto v0.14.0 // indirect + github.com/consensys/gnark-crypto v0.18.1 // indirect + github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect github.com/crate-crypto/go-kzg-4844 v1.1.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/dchest/blake256 v1.0.0 // indirect - github.com/dchest/siphash v1.2.1 // indirect + github.com/dchest/siphash v1.2.3 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/decred/base58 v1.0.3 // indirect - github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect + github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect github.com/decred/dcrd/crypto/ripemd160 v1.0.1 // indirect github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/decred/dcrd/wire v1.4.0 // indirect github.com/decred/slog v1.1.0 // indirect github.com/ethereum/c-kzg-4844 v1.0.0 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect github.com/ethereum/go-verkle v0.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/gorilla/rpc v1.2.0 // indirect github.com/holiman/uint256 v1.3.2 // indirect github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.10.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.3 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/stretchr/testify v1.10.0 // indirect - github.com/supranational/blst v0.3.14 // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect + github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect + github.com/tklauser/go-sysconf v0.3.15 // indirect + github.com/tklauser/numcpus v0.10.0 // indirect github.com/tkrajina/go-reflector v0.5.5 // indirect - github.com/yusufpapurcu/wmi v1.2.2 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.29.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.37.0 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - rsc.io/tmplfunc v0.0.3 // indirect ) // replace github.com/martinboehm/btcutil => ../btcutil diff --git a/go.sum b/go.sum index 9e55e196a1..c7e0c7d0d0 100644 --- a/go.sum +++ b/go.sum @@ -6,18 +6,21 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29 h1:B11BryeZQ1LrAzzM0lCpblwleB7SyxPfvN2AsNbyvQc= github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29/go.mod h1:+39XiGr9m9TPY49sG4XIH5CVaRxHGFWT0U4MOY6dy3o= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= -github.com/ava-labs/avalanchego v1.12.1 h1:NL04K5+gciC2XqGZbDcIu0nuVApEddzc6YyujRBv+u8= -github.com/ava-labs/avalanchego v1.12.1/go.mod h1:xnVvN86jhxndxfS8e0U7v/0woyfx9BhX/feld7XDjDE= +github.com/ava-labs/avalanchego v1.14.0 h1:0j314N1fEwstKSymvyhvvxi8Hr752xc6MQvjq6kGIJY= +github.com/ava-labs/avalanchego v1.14.0/go.mod h1:7sYTcQknONY5x5qzS+GrN+UtyB8kX7Q5ClHhGj1DgXg= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.17.0 h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI= -github.com/bits-and-blooms/bitset v1.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e h1:D64GF/Xr5zSUnM3q1Jylzo4sK7szhP/ON+nb2DB5XJA= github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= @@ -41,28 +44,32 @@ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZe github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/consensys/bavard v0.1.22 h1:Uw2CGvbXSZWhqK59X0VG/zOjpTFuOMcPLStrp1ihI0A= -github.com/consensys/bavard v0.1.22/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs= -github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E= -github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= -github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/consensys/gnark-crypto v0.18.1 h1:RyLV6UhPRoYYzaFnPQA4qK3DyuDgkTgskDdoGqFt3fI= +github.com/consensys/gnark-crypto v0.18.1/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg= +github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4= github.com/crate-crypto/go-kzg-4844 v1.1.0/go.mod h1:JolLjpSff1tCCJKaJx4psrlEdlXuJEC996PL3tTAFks= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/blake256 v1.0.0 h1:6gUgI5MHdz9g0TdrgKqXsoDX+Zjxmm1Sc6OsoGru50I= github.com/dchest/blake256 v1.0.0/go.mod h1:xXNWCE1jsAP8DAjP+rKw2MbeqLczjI3TRx2VK+9OEYY= github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= +github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= +github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= @@ -73,8 +80,9 @@ github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyL github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60= github.com/decred/dcrd/chaincfg/v3 v3.0.0 h1:+TFbu7ZmvBwM+SZz5mrj6cun9ts/6DAL5sqnsaFBHGQ= github.com/decred/dcrd/chaincfg/v3 v3.0.0/go.mod h1:EspyubQ7D2w6tjP7rBGDIE7OTbuMgBjR2F2kZFnh31A= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= +github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/crypto/ripemd160 v1.0.1 h1:TjRL4LfftzTjXzaufov96iDAkbY2R3aTvH2YMYa1IOc= github.com/decred/dcrd/crypto/ripemd160 v1.0.1/go.mod h1:F0H8cjIuWTRoixr/LM3REB8obcWkmYx0gbxpQWR8RPg= github.com/decred/dcrd/dcrec v1.0.0 h1:W+z6Es+Rai3MXYVoPAxYr5U1DGis0Co33scJ6uH2J6o= @@ -83,8 +91,8 @@ github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1 h1:V6eqU1crZzuoFT4KG2LhaU5xDSdkHu github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1/go.mod h1:d0H8xGMWbiIQP7gN3v2rByWUcuZPm9YsgmnfoxgbINc= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 h1:sgNeV1VRMDzs6rzyPpxyM0jp317hnwiq58Filgag2xw= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/decred/dcrd/dcrjson/v3 v3.0.1 h1:b9cpplNJG+nutE2jS8K/BtSGIJihEQHhFjFAsvJF/iI= github.com/decred/dcrd/dcrjson/v3 v3.0.1/go.mod h1:fnTHev/ABGp8IxFudDhjGi9ghLiXRff1qZz/wvq12Mg= github.com/decred/dcrd/dcrutil/v3 v3.0.0 h1:n6uQaTQynIhCY89XsoDk2WQqcUcnbD+zUM9rnZcIOZo= @@ -100,34 +108,36 @@ github.com/decred/slog v1.1.0 h1:uz5ZFfmaexj1rEDgZvzQ7wjGkoSPjw2LCh8K+K1VrW4= github.com/decred/slog v1.1.0/go.mod h1:kVXlGnt6DHy2fV5OjSeuvCJ0OmlmTF6LFpEPMu/fOY0= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= +github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= github.com/ethereum/go-ethereum v1.15.5 h1:Fo2TbBWC61lWVkFw9tsMoHCNX1ndpuaQBRJ8H6xLUPo= github.com/ethereum/go-ethereum v1.15.5/go.mod h1:1LG2LnMOx2yPRHR/S+xuipXH29vPr6BIH6GElD8N/fo= +github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ= +github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk= github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= -github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= -github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/getsentry/sentry-go v0.35.0 h1:+FJNlnjJsZMG3g0/rmmP7GiKjQoUF5EXfEtBwtPtkzY= +github.com/getsentry/sentry-go v0.35.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -136,6 +146,7 @@ github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpx github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= @@ -158,8 +169,8 @@ github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b/go.mod h1:63prj8cnj0t github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc h1:I1QApI4r4SG8Hh45H0yRjVnThWRn1oOwod76rrAe5KE= github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= -github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -187,17 +198,14 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= -github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= -github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= -github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= @@ -222,20 +230,21 @@ github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e h1:WrnL52yXO0jNpHC7 github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e/go.mod h1:y/B3gomTdd1s23RvcBij/X738fcTobeupT30EhV6nPE= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= -github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.3 h1:shd26MlnwTw5jksTDhC7rTQIteBxy+ZZDr3t7F2xN2Q= +github.com/prometheus/common v0.67.3/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -246,16 +255,18 @@ github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKl github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= +github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= +github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= +github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= +github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ= github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= github.com/tkrajina/typescriptify-golang-structs v0.1.11 h1:zEIVczF/iWgs4eTY7NQqbBe23OVlFVk9sWLX/FDYi4Q= @@ -264,38 +275,39 @@ github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= -github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= -github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= -golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4= +golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -313,5 +325,3 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= -rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= From d76f7c5137ddde8e5934b75b92082e213187907d Mon Sep 17 00:00:00 2001 From: etimofeeva Date: Mon, 29 Dec 2025 17:31:31 +0100 Subject: [PATCH 530/530] fix: adjusted zebrarpc for new version of zebrad backend --- bchain/coins/zec/zcashrpc.go | 58 +++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/bchain/coins/zec/zcashrpc.go b/bchain/coins/zec/zcashrpc.go index b3ee93f11e..68ba1481d6 100644 --- a/bchain/coins/zec/zcashrpc.go +++ b/bchain/coins/zec/zcashrpc.go @@ -5,6 +5,7 @@ import ( "encoding/json" "os/exec" "reflect" + "strings" "github.com/golang/glog" "github.com/juju/errors" @@ -121,12 +122,9 @@ func (z *ZCashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { bchain.BlockHeader Txs []bchain.Tx `json:"tx"` } - type rpcBlockTxids struct { - Txids []string `json:"tx"` - } type resGetBlockV1 struct { Error *bchain.RPCError `json:"error"` - Result rpcBlockTxids `json:"result"` + Result bchain.BlockInfo `json:"result"` } type resGetBlockV2 struct { Error *bchain.RPCError `json:"error"` @@ -148,6 +146,12 @@ func (z *ZCashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { req.Params.Verbosity = 2 err = z.Call(&req, &rawResponse) if err != nil { + // Check if it's a memory error and fall back + errStr := strings.ToLower(err.Error()) + if strings.Contains(errStr, "memory capacity exceeded") || strings.Contains(errStr, "response is too big") { + glog.Warningf("getblock verbosity=2 failed for block %v, falling back to individual tx fetches", hash) + return z.getBlockWithFallback(hash) + } return nil, errors.Annotatef(err, "hash %v", hash) } // hack for ZCash, where the field "valueZat" is used instead of "valueSat" @@ -157,9 +161,17 @@ func (z *ZCashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { return nil, errors.Annotatef(err, "hash %v", hash) } + // Check if verbosity=2 returned an RPC error if resV2.Error != nil { + // Check if error is memory-related (case-insensitive) + errorMsg := strings.ToLower(resV2.Error.Message) + if strings.Contains(errorMsg, "memory capacity exceeded") || strings.Contains(errorMsg, "response is too big") { + glog.Warningf("getblock verbosity=2 returned memory error for block %v, falling back to verbosity=1 + individual tx fetches", hash) + return z.getBlockWithFallback(hash) + } return nil, errors.Annotatef(resV2.Error, "hash %v", hash) } + block := &bchain.Block{ BlockHeader: resV2.Result.BlockHeader, Txs: resV2.Result.Txs, @@ -181,6 +193,44 @@ func (z *ZCashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { return block, nil } +// getBlockWithFallback fetches block using verbosity=1 and then fetches each transaction individually +func (z *ZCashRPC) getBlockWithFallback(hash string) (*bchain.Block, error) { + type resGetBlockV1 struct { + Error *bchain.RPCError `json:"error"` + Result bchain.BlockInfo `json:"result"` + } + + // Get block header and txids using verbosity=1 + resV1 := resGetBlockV1{} + req := btc.CmdGetBlock{Method: "getblock"} + req.Params.BlockHash = hash + req.Params.Verbosity = 1 + err := z.Call(&req, &resV1) + if err != nil { + return nil, errors.Annotatef(err, "hash %v", hash) + } + if resV1.Error != nil { + return nil, errors.Annotatef(resV1.Error, "hash %v", hash) + } + + // Create block with header from verbosity=1 response + block := &bchain.Block{ + BlockHeader: resV1.Result.BlockHeader, + Txs: make([]bchain.Tx, 0, len(resV1.Result.Txids)), + } + + // Fetch each transaction individually + for _, txid := range resV1.Result.Txids { + tx, err := z.GetTransaction(txid) + if err != nil { + return nil, errors.Annotatef(err, "failed to fetch tx %v for block %v", txid, hash) + } + block.Txs = append(block.Txs, *tx) + } + + return block, nil +} + // GetTransaction returns a transaction by the transaction ID func (z *ZCashRPC) GetTransaction(txid string) (*bchain.Tx, error) { r, err := z.getRawTransaction(txid)