Compare commits

...

227 Commits

Author SHA1 Message Date
Vivek Teega
bd1543d418
Merge branch 'trezor:master' into flo-upstream 2025-01-28 20:02:57 +05:30
kaladinlight
4a7fdb5095 Avalanche Etna Upgrade 2025-01-18 18:50:07 +01:00
Martin Boehm
70f34cedeb Use network specific ens suffix 2025-01-12 18:18:38 +01:00
Vivek Teegalapally
84be24ef7f Merge remote-tracking branch 'upstream/master' into flo-upstream 2025-01-09 02:14:38 +05:30
Tadeas Kmenta
b40948a781
update flux daemon to v7.2.0 (#1178)
Co-authored-by: Tadeas Kmenta <TheTrunk@users.noreply.github.com>
2025-01-07 21:15:37 +01:00
JoHnY
1334a16d3a zcash (+testnet) 6.0.0 → 6.1.0 2025-01-07 21:14:28 +01:00
JoHnY
40be3e7219 prysm 5.1.2 → 5.2.0 2025-01-07 21:14:02 +01:00
JoHnY
fab4dd78ca bch (+testnets) 27.1.0 → 28.0.1 2025-01-07 21:12:16 +01:00
JoHnY
8b05dbc9b9 polygon-bor 1.4.1 → 1.5.3 2024-12-16 08:51:31 +01:00
Martin Boehm
4c5c0bd32f Add option to disable sync of mempool transactions 2024-12-15 22:44:47 +01:00
JoHnY
d771291eca dash (+testnet) 20.1.0 → 22.0.0 2024-12-13 08:37:10 +00:00
Martin Boehm
bdc477050b Fix Arbitrum and Optimism fiat rates ids 2024-12-10 01:02:05 +01:00
Martin Boehm
22fe25b9cf Fix Arbitrum and Optimism fiat rates ids 2024-12-10 00:32:15 +01:00
Martin Boehm
6f4a6aa8e3 Remove Coingecko URL from configs to enable support of API key 2024-12-09 23:10:47 +01:00
Martin Boehm
144a369f92 Fix Ethereum SENSO contract decimals #1026 2024-12-09 21:40:00 +01:00
hishope
f2cd67e81f chore: fix some problematic function names
Signed-off-by: hishope <csqiye@126.com>
2024-12-09 21:05:30 +01:00
Martin Boehm
c3cdf9bca4 Fix usage of Network configuration parameter 2024-12-09 20:57:37 +01:00
Jiří Musil
a4f1730364
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 <martin.boehm@1mbsoftware.net>
2024-12-09 11:30:02 +01:00
JoHnY
e283ac8915 doge (+testnet) 1.14.7 → 1.14.9 2024-12-07 14:08:43 +01:00
Martin Boehm
46156d296f Fix refetch internal data 2024-12-07 14:07:39 +01:00
Martin Boehm
a55c69a8a1 EthereumType: admin interface to read and update contract info 2024-11-27 14:38:27 +01:00
Martin Boehm
19a902360e EthereumType: Remove fetching of contract details from sync 2024-11-27 14:38:27 +01:00
Martin Boehm
1fe4ee04f3 Migration from MATIC to POL 2024-11-27 14:37:41 +01:00
CodeFace
6eb3ba2201 bump Qtum 27.1 2024-11-25 21:38:20 +01:00
gruve-p
4ba04f119b
Groestlcoin: Bump to 28.0 (#1139) 2024-11-25 21:37:55 +01:00
grdddj
66b4ddbe01 chore: apply make style gofmt formatting 2024-11-25 21:37:17 +01:00
grdddj
04a5d8d95d chore: add make style target for gofmt formatting 2024-11-25 21:37:17 +01:00
kevin
d6aaa09e06
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
2024-11-25 21:36:07 +01:00
JoHnY
afe4749f6d eth (+testnets) 2.60.8 → 2.60.10 2024-11-25 21:35:18 +01:00
JoHnY
132bd77af7 ltc (+testnet) 0.21.3 → 0.21.4 2024-11-25 21:34:55 +01:00
wakiyamap
f373a73bea add tests for bitcoin testnet4 2024-11-25 21:34:29 +01:00
JoHnY
fe676b354d prysm 5.1.0 → 5.1.2 2024-10-29 17:12:07 +01:00
XK4MiLX
572d7e5075 Update Firo daemon 0.14.14.0 (mandatory) 2024-10-29 13:04:29 +01:00
JoHnY
c3cd444578 polygon-heimdall 1.0.5 → 1.0.10 2024-10-29 13:03:10 +01:00
Martin Boehm
d8c68f2b6b Add Bitcoin Testnet4 2024-10-15 10:23:52 +02:00
Martin Boehm
0cc953fcca btc (+testnet) 27.1 -> 28.0 2024-10-15 10:23:04 +02:00
Martin Boehm
76664cdbf3 Refactor ethCall method to rpcCall 2024-10-10 00:58:19 +02:00
JoHnY
059086cd3d zec (+testnet) 5.10.0 → 6.0.0 2024-10-09 14:11:41 +00:00
grdddj
1c70a269b0 chore: update and enrich API docs 2024-09-25 13:59:53 +02:00
grdddj
a2aa489b65 chore: add .editorconfig and style api.md 2024-09-25 13:59:53 +02:00
JoHnY
a93a011d8f eth (+testnets) 2.60.5 → 2.60.8 2024-09-25 13:58:34 +02:00
JoHnY
996fa2d88c polygon-bor 1.4.0 → 1.4.1 2024-09-25 13:58:04 +02:00
Martin Boehm
3cf7e6abe8 Add ethCall websocket request 2024-09-24 15:24:26 +02:00
JoHnY
a1434979fa polygon-bor 1.3.7 → 1.4.0 2024-09-24 06:48:51 +00:00
JoHnY
345ea17031 zcash 5.9.1 → 5.10.0 2024-09-02 15:30:44 +02:00
JoHnY
3cb815ba27 prysm 5.0.4 → 5.1.0 2024-08-30 11:53:09 +02:00
JoHnY
424055d689 polygon-bor 1.3.2 → 1.3.7 2024-08-12 10:55:23 +02:00
Martin Boehm
a0960c8e17 Add network parameter to getInfo ws request 2024-08-06 10:50:01 +02:00
Martin Boehm
c1f2e62c45 Show Nonce for ethereum type transactions in explorer 2024-07-30 14:46:38 +02:00
JoHnY
f6899a7712 eth (+testnets) 2.59.3 → 2.60.5 2024-07-30 09:19:23 +02:00
JoHnY
0afcb3a002 bch (+testnet) 27.0.0 → 27.1.0 2024-07-16 09:52:14 +02:00
kaladinlight
4b9a0a90bb extract snapshot url from metadata html 2024-07-12 00:10:31 +02:00
kaladinlight
7e5be4837b move go build into extract command 2024-07-12 00:10:31 +02:00
kaladinlight
3ce286f82d pull out exec command into script and add path flags 2024-07-12 00:10:31 +02:00
kaladinlight
80f05727c1 run op_node from backend for local file store 2024-07-12 00:10:31 +02:00
kaladinlight
2e3ac06ea0 update op-node config 2024-07-12 00:10:31 +02:00
kaladinlight
d814abae0e use eth.OpenRPC 2024-07-12 00:10:31 +02:00
kaladinlight
f0dd0e80b0 add optimism support 2024-07-12 00:10:31 +02:00
Martin Boehm
6f170e07b0 Delay loading 5minute and hourly tickers to avoid unnecessary calls to Coingecko 2024-07-11 17:06:49 +02:00
JoHnY
8d22752bec prysm 5.0.2 → 5.0.4 2024-07-11 17:05:30 +02:00
Sai Raj
45c778a14a
Merge pull request #1 from ranchimall/dev
Some checks failed
Create Release / build-and-release-flo-deb (push) Has been cancelled
Merge from trezor/blockbook
2024-06-27 19:49:31 -04:00
Sai Raj
2fe72ac150 Delete public_ethereumtype_test.go 2024-06-27 19:47:10 -04:00
Sai Raj
eeca7e72a3 Merge branch 'master' of https://github.com/trezor/blockbook into dev 2024-06-27 02:28:02 -04:00
Martin Boehm
8ac418b267 Use coin specific coingecko api key 2024-06-25 14:28:52 +02:00
JoHnY
6a7c4e96ed btc (+testnet) 27.0 → 27.1 2024-06-25 14:01:48 +02:00
XK4MiLX
d20379ada9 backend mandatory update 0.14.13.3 2024-06-25 13:47:30 +02:00
Martin Boehm
78b4ec1dc4 Increase polygon bor maxpeers and add bootnodes 2024-06-08 10:32:07 +02:00
Martin Boehm
35ab7a3966 Set unlimited size of ETH RPC message 2024-06-04 15:06:40 +02:00
JoHnY
2751a9b03f zcash (+testnet) 5.9.0 → 5.9.1 2024-05-29 12:48:09 +02:00
Martin Boehm
eaf8f4815c Decrease fiat rates download period to 60s for BTC 2024-05-15 16:19:40 +02:00
Martin Boehm
c0dbeeea9a Update typescript API types 2024-05-13 23:46:09 +02:00
Scotty0448
21780d439f Bump Ravencoin backend to 4.6.1.0 2024-05-13 22:34:35 +02:00
JoHnY
0d418deceb polygon-bor 1.3.1 → 1.3.2 2024-05-13 18:52:04 +02:00
JoHnY
f5cb71de7e bch (+testnet) 26.1.0 → 27.0.0 2024-05-07 10:03:52 +02:00
JoHnY
067a2da75d polygon-bor 1.2.8 → 1.3.1 2024-05-02 17:08:25 +02:00
gruve-p
c16143f6e1 Groestlcoin: Bump to 27.0 2024-04-24 16:24:57 +02:00
JoHnY
b0bec47364 btc (+testnet) 26.0 → 27.0 2024-04-23 14:26:28 +02:00
JoHnY
b5bbf8c376 zec (+testnet) 5.8.0 → 5.9.0 2024-04-23 12:41:46 +02:00
JoHnY
beaadf16cd Update dash GPG signing key 2024-04-22 16:02:19 +02:00
JoHnY
87652aa6ac dash (+testnet) 20.1.0 → 20.1.1 2024-04-22 16:02:19 +02:00
Martin Boehm
1b47447c77 Workaround of InitializeMempool issue in Holesky 2024-04-10 09:50:29 +02:00
JoHnY
9b5c79c878 polygon-heimdall 1.0.4 → 1.0.5 2024-04-09 11:15:35 +02:00
JoHnY
754169958e polygon-bor 1.2.7 → 1.2.8 2024-04-09 11:14:33 +02:00
JoHnY
ab5235b732 prysm 5.0.1 → 5.0.2 2024-04-09 11:13:47 +02:00
JoHnY
0277c2a8e6 litecoin (+testnet) 0.21.2.2 → 0.21.3 2024-04-02 10:51:17 +02:00
JoHnY
e9a08582ee eth (+testnets) 2.58.2 → 2.59.1 2024-03-28 14:27:26 +01:00
AdamSchinzel
3c57a0c010 Remove Goerli
Remove
2024-03-27 13:16:20 +01:00
Martin Boehm
b5cfbdfde5 Adjust mempool.space fee mappings 2024-03-15 23:23:19 +01:00
Martin Boehm
9a2fe4dbe4 Increase file limit for backend services 2024-03-13 11:10:08 +01:00
JoHnY
1df02d9df5 eth (+testnets) 2.58.1 → 2.58.2 2024-03-12 09:56:51 +01:00
JoHnY
60fb37da4f prysm 5.0.0 → 5.0.1 2024-03-11 10:25:16 +01:00
Miguilim
52561322f8
PIVX (+testnet) 5.5.0 → 5.6.1 (#1046)
* Update pivx.json

* Update pivx_testnet.json
2024-03-11 10:24:40 +01:00
Martin Boehm
847c40a29e Disable get balance history for contract addresses 2024-03-11 10:22:31 +01:00
Martin Boehm
d8cd7b4d0b Update histogram buckets for websocket metrics 2024-03-11 10:22:31 +01:00
Martin Boehm
2c84ed4eb2 Handle Coingecko Throttled error 2024-03-11 10:22:31 +01:00
Martin Boehm
913c0e5198 Bump Polygon Bor to v1.2.7, Heimdall to v1.0.4 2024-03-10 13:00:41 +01:00
JoHnY
4d60edc231 dash (+testnet) 20.0.4 → 20.1.0 2024-03-07 16:48:28 +01:00
JoHnY
f05159a7fa dogecoin (+testnet) 1.14.6 → 1.14.7 2024-03-07 14:19:12 +01:00
Martin Boehm
ca3a023493 Fix xpub addresses path for non standard descriptors 2024-03-05 14:27:09 +01:00
JoHnY
da34069d24 prysm 4.2.1 → 5.0.0 2024-02-26 14:29:47 +01:00
JoHnY
8802a73017 eth (+testnets) 2.58.0 → 2.58.1 2024-02-26 14:29:28 +01:00
XK4MiLX
b4249ab4bb update daemon to v7.1.0 2024-02-26 14:29:06 +01:00
AdamSchinzel
8780151664 Update type for gasPrice 2024-02-21 15:07:40 +01:00
Martin Boehm
8d78397969 Update ethereum_testnet_holesky_consensus.json
fixes --jwt-secret path
2024-02-19 21:49:12 +01:00
AlexanderPavlenko
a13feaccd9 Update ethereum_testnet_goerli_consensus.json
fixes --jwt-secret path
2024-02-19 21:47:09 +01:00
JoHnY
4e3ba20610 eth (+testnets) 2.57.2 → 2.58.0 2024-02-19 12:28:04 +01:00
Martin Boehm
5f47f3c489 Add mempool.space alternative blockchain fees provider 2024-02-13 20:32:15 +01:00
Martin Boehm
0ed95dca32 Upgrade go-ethereum dependency to v1.13.12 2024-02-13 20:32:15 +01:00
Martin Boehm
11ad8141ac Disable CI deploy of Goerli on config change 2024-02-11 21:13:28 +01:00
JoHnY
55860c001a prysm 4.2.0 → 4.2.1 2024-02-08 22:23:23 +01:00
Martin Boehm
1d95413f73 Staking pools - remove unused pendingRestakedReward 2024-01-31 23:45:43 +01:00
Martin Boehm
ac46385f49 Add support of staking pools 2024-01-31 23:45:43 +01:00
JoHnY
f03c625def eth (+testnets) 2.57.0 → 2.57.2 2024-01-30 21:36:17 +01:00
Martin Boehm
c2cb64856b Use Erigon for Goerli and Holesky non archive backends 2024-01-29 15:16:03 +01:00
Martin Boehm
3e1f456632 Bump Polygon Bor to v1.2.3 2024-01-28 16:41:20 +01:00
Martin Boehm
c43bcb25d0 Get current fiat rates: disable reading from DB for tokens without current rate 2024-01-23 00:50:04 +01:00
JoHnY
b10b790760 eth (+testnets) 2.56.2 → 2.57.0 2024-01-22 13:50:47 +01:00
JoHnY
a8fbfd2448 prysm 4.1.1 → 4.2.0 2024-01-17 17:36:35 +01:00
JoHnY
536a4701c0 ethereum (+testnets) 2.56.0 → 2.56.2 2024-01-17 16:34:46 +01:00
JoHnY
bfc3c7193b dash (+testnet) 20.0.3 → 20.0.4 2024-01-16 12:22:40 +00:00
Martin Boehm
23314e523c Update typescriptify library and typescript types 2024-01-14 15:02:05 +01:00
JoHnY
9ea7fb23df etc 1.12.17 → 1.12.18 2024-01-10 22:17:56 +01:00
JoHnY
12de050229 zcash (+testnet) 5.7.0 → 5.8.0 2024-01-08 22:20:38 +01:00
Martin Boehm
c2b2f10df4 Ethereum: fix ws.port after upgrade to Erigon 2.56.0 2024-01-07 01:14:39 +01:00
XK4MiLX
c1c44b19ca
PIVX: fix postinst_script_template (#1016) 2024-01-07 00:16:29 +01:00
JoHnY
aa13c065b1 dash (+testnet) 20.0.2 → 20.0.3 2024-01-07 00:15:09 +01:00
justanwar
7d2a4ef3db firo 0.14.13.0 -> 0.14.13.1 2024-01-07 00:14:52 +01:00
JoHnY
ca41c4a29e ethereum (+testnets) 2.55.1 → 2.56.0 2024-01-07 00:14:29 +01:00
Martin Boehm
24e08ec053 Add a custom node for Bitcoin 2023-12-26 11:04:45 +01:00
Martin Boehm
e464e16a33 Stop notifications of duplicate insertions into ethereum type mempool 2023-12-20 12:43:50 +01:00
romanornr
e7ee4a95d8 Refactor in initialization NewCoinGeckoDownloader 2023-12-19 16:04:24 +01:00
JoHnY
f6edc06630 btc (+testnet) 25.1 → 26.0 2023-12-19 10:40:16 +01:00
JoHnY
a24afcb7d5 dash (+testnet) 20.0.1 → 20.0.2 2023-12-18 22:23:56 +01:00
romanornr
6a1c8ad725 Viacoin 0.16.3 binary upgrade 2023-12-18 21:06:22 +01:00
gruve-p
33b885da68 Groestlcoin: Bump to 26.0 2023-12-18 21:05:12 +01:00
Martin Boehm
05737ec510 Set mempoolfullrbf=1 for Bitcoin 2023-12-11 13:42:46 +01:00
Martin Boehm
474d41c93f Bump Polygon Bor to v1.2.0 2023-12-10 14:42:28 +01:00
Martin Boehm
61bdb8a346 Fix internal admin styles 2023-12-08 18:30:21 +01:00
Martin Boehm
f28a747baa Upgrade go-ethereum dependency to v1.13.5 2023-12-08 09:45:19 +01:00
JoHnY
3d1a3ff550 etc 1.12.15 → 1.12.17 2023-12-08 09:23:23 +01:00
kevin
8c2a59aa0e
perf: add contract info cache for spam token/nft transactions (#994) 2023-12-08 09:22:41 +01:00
JoHnY
0c95abe025 eth(+testnets) 2.54.0 → 2.55.1 2023-12-07 11:31:25 +01:00
CodeFace
f4b340fc5d bump Qtum 24.1 2023-12-05 23:01:34 +01:00
XK4MiLX
22de7e55d0 update firo binary 0.14.13 2023-12-05 13:42:24 +01:00
TheTrunk
e67bb3176c update flux to v7.0.0 2023-12-05 13:41:57 +01:00
Martin Boehm
f4d06ab08d Load bootstrap css and js from local server 2023-12-03 23:00:03 +01:00
Martin Boehm
3c29b07c7c Disconnect ws connections exceeding limit of requests 2023-11-29 09:49:59 +01:00
Martin Boehm
08389b5e05 Bump polygon bor to v1.1.0 2023-11-24 16:55:15 +01:00
Martin Boehm
6992be6f58 Fix linting errors 2023-11-21 23:37:42 +01:00
Martin Boehm
2eebd7aed6 Bump erigon to 2.54.0 2023-11-21 15:54:22 +01:00
Marek Liska
b9fee439ed dash(+testnet): 19.3.0→20.0.1 2023-11-21 15:24:52 +01:00
Martin Boehm
8fa0612e48 Polygon Heimdall bump to 1.0.3 2023-11-15 18:15:58 +01:00
AdamSchinzel
8646c99dea Fix holesky shortcut 2023-11-14 11:33:29 +01:00
0xadams.eth
a2ab9da785
Add Holesky (#977) 2023-11-12 22:00:47 +01:00
Martin Boehm
a5bbb7d8ee Fix ethereum config files 2023-11-12 21:59:56 +01:00
0xadams.eth
8d4dd5d69d
Use Erigon for Ethereum Sepolia archive backend (#986) 2023-11-08 11:52:44 +01:00
JoHnY
9c04322971 etc 1.12.14 → 1.12.15 2023-11-08 11:48:27 +01:00
vertiond
56ba302766 vtc (+testnet) 22.1 -> 23.2 2023-11-08 11:47:44 +01:00
Martin Boehm
856c85e1e7 Add public interface block filter tests 2023-11-08 10:21:19 +01:00
grdddj
35dec9e9c4 Add tests for Ordinals filtering in mempool Golomb filters 2023-11-08 10:21:19 +01:00
grdddj
3b0e108f45 Add tests for Ordinals filtering in block Golomb filters 2023-11-08 10:21:18 +01:00
Martin Boehm
78f0be0f23 Set block and mempool filter params for regtest 2023-11-08 10:21:18 +01:00
Martin Boehm
80afffa7b0 Set block and mempool filter params for bitcoin and testnet 2023-11-08 10:21:18 +01:00
Martin Boehm
8ef09d124e Fix tests 2023-11-08 10:21:18 +01:00
Jiří Musil
7d0c424ad8 Ignore Ordinals in Golomb filters (#967) 2023-11-08 10:21:18 +01:00
Martin Boehm
a1a17b4331 Add getBlockFiltersBatch websocket method 2023-11-08 10:21:18 +01:00
Martin Boehm
4b6f19632d Enable websocket per message compression 2023-11-08 10:21:18 +01:00
Martin Boehm
83d411be4e Use golomb config in block sync, refactor 2023-11-08 10:21:18 +01:00
Martin Boehm
96dbc8c9dc Add configuration for block golomb filters 2023-11-08 10:21:18 +01:00
grdddj
911454f171 Implement Golomb block filters for each block
Contains a websocket method `getBlockFilter` and REST endpoint `block-filters`
2023-11-08 10:21:18 +01:00
JoHnY
bc8ce22ed0 prysm 4.0.8 → 4.1.1 2023-11-07 11:26:50 +01:00
JoHnY
61eeeed03e non-archive ethereum bump to erigon 2.53.2 2023-11-07 10:45:51 +01:00
Martin Boehm
3d93da0d64 Bump erigon to v2.53.2 2023-10-29 18:56:56 +01:00
Martin Boehm
1c71a696f2 Use Erigon for Ethereum mainnet archive backend 2023-10-29 18:39:43 +01:00
Martin Boehm
059c60d2a1 Use Erigon for Ethereum Goerli backend 2023-10-29 18:39:43 +01:00
Martin Boehm
137d6b2141 Bump polygon bor to 1.0.6, heimdall to 1.0.2 2023-10-24 22:24:35 +02:00
TheTrunk
6ccfa19cb5 update flux to v6.2.0 2023-10-24 22:20:52 +02:00
fanquake
5f53f3b614 btc (+testnet) 25.0 → 25.1 2023-10-24 22:20:20 +02:00
1818d14e70
Update README.md 2023-10-24 16:03:45 +05:30
bd1075ba8b
Update README.md 2023-10-23 19:39:23 +05:30
81462530fe
Update README.md 2023-10-23 19:34:18 +05:30
63cdae7747
Update README.md 2023-10-23 19:33:34 +05:30
d7ae525352
Update README.md 2023-10-23 18:52:12 +05:30
dbf4ee83fd
Update README.md 2023-10-23 18:34:16 +05:30
e11746c3c3
Update README.md 2023-10-23 18:33:30 +05:30
ef568ba31d
Update README.md 2023-10-23 18:28:28 +05:30
JoHnY
9c5ae9585f zec (+testnet) 5.6.1 → 5.7.0 2023-10-12 15:10:16 +00:00
AdamSchinzel
52f86d2ed3 Update README 2023-09-25 22:51:17 +02:00
AdamSchinzel
682b62fde8 Update README 2023-09-25 22:51:17 +02:00
JoHnY
c314a64ea8 etc 1.12.13 → 1.12.14 2023-09-25 18:47:40 +02:00
AdamSchinzel
d8c32533de Remove test data for Ropsten 2023-09-19 16:18:02 +02:00
AdamSchinzel
b91b36e86b Remove Ropsten 2023-09-19 16:18:02 +02:00
Vivek Teega
a586ce22e6
Update README.md 2023-09-06 20:31:45 +05:30
Vivek Teega
8859e21b02 Shortened tag name 2023-09-06 19:49:11 +05:30
JoHnY
c902332bc2 prysm 4.0.7 → 4.0.8 2023-09-06 15:43:29 +02:00
XK4MiLX
85e5b22d1c Fix missing postinst script for PIVX
- added service env
- added postinst_script_template
2023-09-06 15:43:13 +02:00
Vivek Teega
d4af0f5e14 Fix build workflow 2023-09-06 19:12:37 +05:30
AdamSchinzel
95ee9b5b64 Change symbol for Sepolia 2023-08-08 23:05:09 +02:00
JoHnY
915f4926d3 dash (+testnet) 19.2.0 → 19.3.0 2023-08-01 23:46:46 +02:00
Martin Boehm
6e51bdefd3 Fix bulk import of ethereum internal data 2023-08-01 23:09:51 +02:00
bbc9239a27 Setup actions 2023-07-26 14:05:50 +00:00
Vivek Teega
823d0f992d
Create build-release.yml 2023-07-26 19:23:18 +05:30
Martin Boehm
4f7d8f0956 polygon bor prysm 0.3.9 → 0.4.0 2023-07-21 21:20:09 +02:00
JoHnY
11ed72b58e prysm 4.0.6 → 4.0.7 2023-07-21 17:33:58 +02:00
JoHnY
04fa1df87a etc 1.12.11 → 1.12.13 2023-07-21 17:29:49 +02:00
Sai Raj
8e18ca0e32
fix: testnet flod cmd 2023-07-19 14:30:53 +05:30
sairajzero
e21decd769 fix: adding flo-testnet to BlockChainFactories 2023-07-18 19:54:16 +05:30
sairajzero
e1d6493a2e build hotfix: removing test file
- removing test files so that build doesn't fail due to test FAIL condition
2023-07-18 19:52:50 +05:30
grdddj
a139dd5d52 Fix typos/grammar in docs 2023-07-14 00:03:29 +02:00
grdddj
65cd73d081 Add gcc into shell.nix 2023-07-13 23:58:46 +02:00
Dante
6b022bb9c5
Fix PIVX parser after update v5.0 (#946) 2023-07-07 20:36:28 +02:00
Dante
083285f4d3
PIVX (+testnet) 4.0.0 → 5.5.0 (#945) 2023-07-07 20:36:06 +02:00
d5eeb47199
Update README.md 2023-07-05 18:34:18 +05:30
9fda269d8d
Update README.md 2023-07-05 16:22:07 +05:30
sairaj mote
23b13cc814 UI bug fix 2023-07-04 03:07:14 +05:30
sairajzero
9fa5ee3618 Adding query option 'confirmed' for address API
Adding query option confirmed=true for address details
- if confirmed is of boolean true, then only return confirmed txs
2023-07-04 00:53:11 +05:30
sairajzero
d1a0c9326b Allow CORS
- Set CORS headers to API requests
2023-07-03 23:25:40 +05:30
sairaj mote
e5c4c0dc65 UI fixes 2023-07-02 22:16:42 +05:30
sairaj mote
d90608d8eb Design changes 2023-07-02 22:06:29 +05:30
Martin Boehm
fcd88b3ab4 Tune up generation of the port registry 2023-07-02 10:45:41 +02:00
Martin Boehm
d1ffa7bb9e Fix internal data processing for Polygon 2023-07-01 23:00:32 +02:00
kevin
9f0286cef4
Add Polygon (#872) 2023-07-01 22:55:00 +02:00
sairaj mote
ac1da72ba8 UI changes 2023-07-02 01:00:11 +05:30
sairaj mote
f821c02dbc UI changes 2023-07-01 22:03:00 +05:30
sairajzero
3c83f1af21 Updating default index page (UI)
- Blocks page is now the Index page
- Status page (old index page) is now in /status
2023-07-01 17:40:14 +05:30
sairajzero
9003df740d Fix: floData not showing in API and UI
- floData is now in v1 (default) api
- v1 (default) API response data to match flosight as much as possible
- v2 API response with tx details now has coin-specific data field always
- Fixed: locktime not showing in API
- Fixed: display floData in UI (tx, block, address page) if not empty

Note: Due to these floData fixes, this version will only work for FLO blockchain
2023-06-30 03:55:03 +05:30
JoHnY
c30d2f79fe zcash (+testnet) 5.6.0 → 5.6.1 2023-06-26 21:00:17 +02:00
313 changed files with 9512 additions and 24264 deletions

10
.editorconfig Normal file
View File

@ -0,0 +1,10 @@
root = true
[*.md]
indent_style = space
indent_size = 4
trim_trailing_whitespace = false
max_line_length = 80
insert_final_newline = true
charset = utf-8
end_of_line = lf

View File

@ -0,0 +1,41 @@
name: Create Release
on:
push:
branches:
- flo
jobs:
build-and-release-flo-deb:
runs-on: self-hosted
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Build .deb File
run: |
make all-flo
mkdir -p release
mv ./build/*.deb ./release/
timeout-minutes: 130 # Adjust the timeout according to the estimated duration of "make all-flo"
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: FLO # Use the commit SHA as the tag name
release_name: Release FLO # Use the commit SHA as the release name
body: |
Release commit: ${{ github.sha }}
Commit message: ${{ github.event.head_commit.message }}
draft: false
prerelease: false
- name: Upload .deb File
uses: actions/upload-artifact@v2
with:
name: deb-file
path: ./release/*.deb

View File

@ -157,15 +157,4 @@ backend-deploy-and-test-zcash_testnet:
- configs/coins/zcash_testnet.json - configs/coins/zcash_testnet.json
tags: tags:
- blockbook - 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-goerli-archive:
stage: backend-deploy-and-test
only:
refs:
- master
changes:
- configs/coins/ethereum_testnet_goerli_archive.json
tags:
- blockbook
script: ./contrib/scripts/backend-deploy-and-test.sh ethereum_testnet_goerli_archive ethereum-testnet-goerli-archive ethereum=test ethereum_testnet_goerli_archive.log

View File

@ -1,9 +1,10 @@
BIN_IMAGE = blockbook-build BIN_IMAGE = blockbook-build
DEB_IMAGE = blockbook-build-deb DEB_IMAGE = blockbook-build-deb
PACKAGER = $(shell id -u):$(shell id -g) 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 '"') 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 NO_CACHE = false
TCMALLOC = TCMALLOC =
PORTABLE = 0 PORTABLE = 0
ARGS ?= ARGS ?=
@ -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)" docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" --network="host" $(BIN_IMAGE) make test-all ARGS="$(ARGS)"
deb-backend-%: .deb-image 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 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) 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 .deb-image: .bin-image
@if [ $$(build/tools/image_status.sh $(DEB_IMAGE):latest build/docker) != "ok" ]; then \ @if [ $$(build/tools/image_status.sh $(DEB_IMAGE):latest build/docker) != "ok" ]; then \
echo "Building image $(DEB_IMAGE)..."; \ 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 \ else \
echo "Image $(DEB_IMAGE) is up to date"; \ echo "Image $(DEB_IMAGE) is up to date"; \
fi fi
@ -79,3 +80,6 @@ clean-bin-image:
clean-deb-image: clean-deb-image:
- docker rmi $(DEB_IMAGE) - docker rmi $(DEB_IMAGE)
style:
find . -name "*.go" -exec gofmt -w {} \;

154
README.md
View File

@ -1,8 +1,154 @@
[![Go Report Card](https://goreportcard.com/badge/trezor/blockbook)](https://goreportcard.com/report/trezor/blockbook) [![Go Report Card](https://goreportcard.com/badge/trezor/blockbook)](https://goreportcard.com/report/trezor/blockbook)
[![FLO Release](https://github.com/ranchimall/blockbook/actions/workflows/build-release-flo-deb.yml/badge.svg?branch=flo)](https://github.com/ranchimall/blockbook/actions/workflows/build-release-flo-deb.yml)
# IMPORTANT Changes needed to migrate to blockbook from flosight
1. blockbook URL link
2. API URIs (path)
3. response object path for tx vin floID (for processes that need to find sender id from tx details)
4. The link for fetching details of transactions for every ADDRESS has changed slightly
```
- For point 3
We need to change
vin[x].addr
to
vin[x].addresses[0]
```
## Changes needed for UI in FLO applications
- OLD BLOCK LINK
- https://flosight.duckdns.org/block/48fb49a89317c0852eb894b0251ae3c830621d416b2481ecd1d541d0b01e5ab8
- https://flosight.duckdns.org/api/block/48fb49a89317c0852eb894b0251ae3c830621d416b2481ecd1d541d0b01e5ab8
- NEW BLOCK LINK
- https://blockbook.ranchimall.net/block/48fb49a89317c0852eb894b0251ae3c830621d416b2481ecd1d541d0b01e5ab8
- https://blockbook.ranchimall.net/api/block/48fb49a89317c0852eb894b0251ae3c830621d416b2481ecd1d541d0b01e5ab8
- OLD tx LINK
- https://flosight.duckdns.org/tx/ae7886df36832824e0e0f37cd003150cc7222e35bc7320d226f1561a598ce39a
- https://flosight.duckdns.org/api/tx/ae7886df36832824e0e0f37cd003150cc7222e35bc7320d226f1561a598ce39a
- NEW tx LINK
- https://blockbook.ranchimall.net/tx/ae7886df36832824e0e0f37cd003150cc7222e35bc7320d226f1561a598ce39a
- https://blockbook.ranchimall.net/api/tx/ae7886df36832824e0e0f37cd003150cc7222e35bc7320d226f1561a598ce39a
```
Make this change for sender address in tx API here
vin[x].addr
to
vin[x].addresses[0]
```
- OLD Address LINK
- https://flosight.duckdns.org/address/F9RgXE1WYxHwvejktpi6EnjnscEkpiWxvS
- https://flosight.duckdns.org/api/address/F9RgXE1WYxHwvejktpi6EnjnscEkpiWxvS
- NEW ADDRESS LINK
- https://blockbook.ranchimall.net/address/F9RgXE1WYxHwvejktpi6EnjnscEkpiWxvS
- https://blockbook.ranchimall.net/api/address/F9RgXE1WYxHwvejktpi6EnjnscEkpiWxvS
- OLD
- https://flosight.ranchimall.net/api/addrs/FBL45szT4jDQmViVirUxPeJjn1tkCDMxeT/txs
- https://flosight.ranchimall.net/api/addrs/FBL45szT4jDQmViVirUxPeJjn1tkCDMxeT/txs?from=0&to=359
- NEW
- https://blockbook.ranchimall.net/api/address/FBL45szT4jDQmViVirUxPeJjn1tkCDMxeT?details=basic
- https://blockbook.ranchimall.net/api/address/FBL45szT4jDQmViVirUxPeJjn1tkCDMxeT?details=txs
- with paging for address
- https://blockbook.ranchimall.net/api/address/FBL45szT4jDQmViVirUxPeJjn1tkCDMxeT?details=txs&pageSize=10&page=2
```javascript
//Flosight
var response = ajax("GET",`api/addrs/${addr}/txs?from=0&to=${nRequired}`);
//Blockbook
var response = ajax("GET",`api/address/${addr}&details=txs?pageSize=${nRequired}&page=1`);
```
- OLD LINK FOR DIRECT BALANCE QUERY
- https://flosight.duckdns.org/api/addr/FBL45szT4jDQmViVirUxPeJjn1tkCDMxeT/balance
- NO BLOCKBOOK EQUIVALENT
- THIS HAS TO BE FETCHED FROM THIS FORMAT
- https://blockbook.ranchimall.net/api/address/FBL45szT4jDQmViVirUxPeJjn1tkCDMxeT?details=basic
```javascript
// https://blockbook.ranchimall.net/api/address/FBL45szT4jDQmViVirUxPeJjn1tkCDMxeT?details=basic
// JSON data as a string
var jsonData = '{"addrStr":"FBL45szT4jDQmViVirUxPeJjn1tkCDMxeT","balance":0.1663,"balanceSat":16630000,"totalReceived":29.4465,"totalReceivedSat":2944650000,"totalSent":29.2802,"totalSentSat":2928020000,"unconfirmedBalance":0,"unconfirmedBalanceSat":0,"unconfirmedTxApperances":0,"txApperances":407}';
// Parse the JSON data into a JavaScript object
var dataObject = JSON.parse(jsonData);
// Extract the balance value
var balance = dataObject.balance;
// Output the balance
console.log(balance); // Output: 0.1663
```
- To broadcast tx in Blockbook
- https://blockbook.ranchimall.net/api/sendtx/hex_tx_data
```
//THIS IS FLOSIGHT CODE TO BROADCAST TRANSACTION -- NOT NEEDED IN BLOCKBOOK
function broadcastTx(signedTxHash) {
var http = new XMLHttpRequest();
var url = `${api_url}/api/tx/send`;
if (signedTxHash.length < 1) {
return false;
}
var params = `{"rawtx":"${signedTxHash}"}`;
var result;
http.open('POST', url, false);
//Send the proper header information along with the request
http.setRequestHeader('Content-type', 'application/json');
http.onreadystatechange = function () { //Call a function when the state changes.
if (http.readyState == 4 && http.status == 200) {
console.log(http.response);
var txid = JSON.parse(http.response).txid.result;
console.log("Transaction successful! txid : " + txid);
result = txid;
} else {
console.log(http.responseText);
result = false;
}
}
http.send(params);
return result;
}
```
# Websocket Connectivity issue with Nginx
- Nginx needs more directives to permit Websocket traffic to pass.
- `proxy_set_header Upgrade $http_upgrade;` AND `proxy_set_header Connection "upgrade";` must be set in location in nginx conf file
```
location / {
proxy_pass https://0.0.0.0:98765/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
```
# Blockbook # Blockbook
**Blockbook** is back-end service for Trezor wallet. Main features of **Blockbook** are: **Blockbook** is a back-end service for Trezor Suite. The main features of **Blockbook** are:
- index of addresses and address balances of the connected block chain - index of addresses and address balances of the connected block chain
- fast index search - fast index search
@ -35,7 +181,7 @@ the rest of coins were implemented by the community.
Testnets for some coins are also supported, for example: Testnets for some coins are also supported, for example:
- Bitcoin Testnet, Bitcoin Cash Testnet, ZCash Testnet, Ethereum Testnets (Goerli, Sepolia) - Bitcoin Testnet, Bitcoin Cash Testnet, ZCash Testnet, Ethereum Testnets (Sepolia, Holesky)
List of all implemented coins is in [the registry of ports](/docs/ports.md). List of all implemented coins is in [the registry of ports](/docs/ports.md).
@ -74,3 +220,7 @@ Blockbook stores data the key-value store RocksDB. Database format is described
## API ## API
Blockbook API is described [here](/docs/api.md). Blockbook API is described [here](/docs/api.md).
## Environment variables
List of environment variables that affect Blockbook's behavior is [here](/docs/env.md).

View File

@ -62,7 +62,7 @@ func (w *Worker) RefetchInternalDataRoutine() {
if block != nil { if block != nil {
blockSpecificData, _ = block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) 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) 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 // 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 // probably something to do with data preloaded to cache on the backend

View File

@ -208,9 +208,9 @@ type TokenTransfer struct {
From string `json:"from"` From string `json:"from"`
To string `json:"to"` To string `json:"to"`
Contract string `json:"contract"` Contract string `json:"contract"`
Name string `json:"name"` Name string `json:"name,omitempty"`
Symbol string `json:"symbol"` Symbol string `json:"symbol,omitempty"`
Decimals int `json:"decimals"` Decimals int `json:"decimals,omitempty"`
Value *Amount `json:"value,omitempty"` Value *Amount `json:"value,omitempty"`
MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"`
} }
@ -231,7 +231,11 @@ type EthereumSpecific struct {
Nonce uint64 `json:"nonce"` Nonce uint64 `json:"nonce"`
GasLimit *big.Int `json:"gasLimit"` GasLimit *big.Int `json:"gasLimit"`
GasUsed *big.Int `json:"gasUsed,omitempty"` GasUsed *big.Int `json:"gasUsed,omitempty"`
GasPrice *Amount `json:"gasPrice"` 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"` Data string `json:"data,omitempty"`
ParsedData *bchain.EthereumParsedInputData `json:"parsedData,omitempty"` ParsedData *bchain.EthereumParsedInputData `json:"parsedData,omitempty"`
InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"` InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"`
@ -316,6 +320,19 @@ type AddressFilter struct {
OnlyConfirmed bool OnlyConfirmed bool
} }
// 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 // Address holds information about address and its transactions
type Address struct { type Address struct {
Paging Paging
@ -342,6 +359,7 @@ type Address struct {
ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"` ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"`
Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty"` // deprecated Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty"` // deprecated
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"`
StakingPools []StakingPool `json:"stakingPools,omitempty"`
// helpers for explorer // helpers for explorer
Filter string `json:"-"` Filter string `json:"-"`
XPubAddresses map[string]struct{} `json:"-"` XPubAddresses map[string]struct{} `json:"-"`
@ -485,6 +503,7 @@ type BlockRaw struct {
// BlockbookInfo contains information about the running blockbook instance // BlockbookInfo contains information about the running blockbook instance
type BlockbookInfo struct { type BlockbookInfo struct {
Coin string `json:"coin"` Coin string `json:"coin"`
Network string `json:"network"`
Host string `json:"host"` Host string `json:"host"`
Version string `json:"version"` Version string `json:"version"`
GitCommit string `json:"gitCommit"` GitCommit string `json:"gitCommit"`
@ -504,6 +523,7 @@ type BlockbookInfo struct {
CurrentFiatRatesTime *time.Time `json:"currentFiatRatesTime,omitempty"` CurrentFiatRatesTime *time.Time `json:"currentFiatRatesTime,omitempty"`
HistoricalFiatRatesTime *time.Time `json:"historicalFiatRatesTime,omitempty"` HistoricalFiatRatesTime *time.Time `json:"historicalFiatRatesTime,omitempty"`
HistoricalTokenFiatRatesTime *time.Time `json:"historicalTokenFiatRatesTime,omitempty"` HistoricalTokenFiatRatesTime *time.Time `json:"historicalTokenFiatRatesTime,omitempty"`
SupportedStakingPools []string `json:"supportedStakingPools,omitempty"`
DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty"` DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty"`
DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty"` DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty"`
About string `json:"about"` About string `json:"about"`

View File

@ -2,7 +2,8 @@ package api
import ( import (
"math/big" "math/big"
"encoding/json"
"strconv"
"github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain"
) )
@ -21,9 +22,10 @@ type VinV1 struct {
ScriptSig ScriptSigV1 `json:"scriptSig"` ScriptSig ScriptSigV1 `json:"scriptSig"`
AddrDesc bchain.AddressDescriptor `json:"-"` AddrDesc bchain.AddressDescriptor `json:"-"`
Addresses []string `json:"addresses"` Addresses []string `json:"addresses"`
IsAddress bool `json:"-"` IsAddress bool `json:"isAddress"`
Value string `json:"value"` Value float64 `json:"value"`
ValueSat big.Int `json:"-"` ValueSat big.Int `json:"valueSat"`
Coinbase string `json:"coinbase,omitempty"`
} }
// ScriptPubKeyV1 is used for legacy api v1 // ScriptPubKeyV1 is used for legacy api v1
@ -32,14 +34,14 @@ type ScriptPubKeyV1 struct {
Asm string `json:"asm,omitempty"` Asm string `json:"asm,omitempty"`
AddrDesc bchain.AddressDescriptor `json:"-"` AddrDesc bchain.AddressDescriptor `json:"-"`
Addresses []string `json:"addresses"` Addresses []string `json:"addresses"`
IsAddress bool `json:"-"` IsAddress bool `json:"isAddress"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
} }
// VoutV1 is used for legacy api v1 // VoutV1 is used for legacy api v1
type VoutV1 struct { type VoutV1 struct {
Value string `json:"value"` Value float64 `json:"value"`
ValueSat big.Int `json:"-"` ValueSat big.Int `json:"valueSat"`
N int `json:"n"` N int `json:"n"`
ScriptPubKey ScriptPubKeyV1 `json:"scriptPubKey"` ScriptPubKey ScriptPubKeyV1 `json:"scriptPubKey"`
Spent bool `json:"spent"` Spent bool `json:"spent"`
@ -52,7 +54,7 @@ type VoutV1 struct {
type TxV1 struct { type TxV1 struct {
Txid string `json:"txid"` Txid string `json:"txid"`
Version int32 `json:"version,omitempty"` Version int32 `json:"version,omitempty"`
Locktime uint32 `json:"locktime,omitempty"` Locktime uint32 `json:"locktime"`
Vin []VinV1 `json:"vin"` Vin []VinV1 `json:"vin"`
Vout []VoutV1 `json:"vout"` Vout []VoutV1 `json:"vout"`
Blockhash string `json:"blockhash,omitempty"` Blockhash string `json:"blockhash,omitempty"`
@ -60,24 +62,29 @@ type TxV1 struct {
Confirmations uint32 `json:"confirmations"` Confirmations uint32 `json:"confirmations"`
Time int64 `json:"time,omitempty"` Time int64 `json:"time,omitempty"`
Blocktime int64 `json:"blocktime"` Blocktime int64 `json:"blocktime"`
ValueOut string `json:"valueOut"` ValueOut float64 `json:"valueOut"`
ValueOutSat big.Int `json:"-"` ValueOutSat big.Int `json:"valueOutSat"`
Size int `json:"size,omitempty"` Size int `json:"size,omitempty"`
ValueIn string `json:"valueIn"` ValueIn float64 `json:"valueIn"`
ValueInSat big.Int `json:"-"` ValueInSat big.Int `json:"valueInSat"`
Fees string `json:"fees"` Fees float64 `json:"fees"`
FeesSat big.Int `json:"-"` FeesSat big.Int `json:"feesSat"`
Hex string `json:"hex"` Hex string `json:"hex"`
FloData string `json:"floData,omitempty"`
} }
// AddressV1 is used for legacy api v1 // AddressV1 is used for legacy api v1
type AddressV1 struct { type AddressV1 struct {
Paging Paging
AddrStr string `json:"addrStr"` AddrStr string `json:"addrStr"`
Balance string `json:"balance"` Balance float64 `json:"balance"`
TotalReceived string `json:"totalReceived"` BalanceSat big.Int `json:"balanceSat"`
TotalSent string `json:"totalSent"` TotalReceived float64 `json:"totalReceived"`
UnconfirmedBalance string `json:"unconfirmedBalance"` TotalReceivedSat big.Int `json:"totalReceivedSat"`
TotalSent float64 `json:"totalSent"`
TotalSentSat big.Int `json:"totalSentSat"`
UnconfirmedBalance float64 `json:"unconfirmedBalance"`
UnconfirmedBalanceSat big.Int `json:"unconfirmedBalanceSat"`
UnconfirmedTxApperances int `json:"unconfirmedTxApperances"` UnconfirmedTxApperances int `json:"unconfirmedTxApperances"`
TxApperances int `json:"txApperances"` TxApperances int `json:"txApperances"`
Transactions []*TxV1 `json:"txs,omitempty"` Transactions []*TxV1 `json:"txs,omitempty"`
@ -88,7 +95,7 @@ type AddressV1 struct {
type AddressUtxoV1 struct { type AddressUtxoV1 struct {
Txid string `json:"txid"` Txid string `json:"txid"`
Vout uint32 `json:"vout"` Vout uint32 `json:"vout"`
Amount string `json:"amount"` Amount float64 `json:"amount"`
AmountSat big.Int `json:"satoshis"` AmountSat big.Int `json:"satoshis"`
Height int `json:"height,omitempty"` Height int `json:"height,omitempty"`
Confirmations int `json:"confirmations"` Confirmations int `json:"confirmations"`
@ -102,6 +109,15 @@ type BlockV1 struct {
Transactions []*TxV1 `json:"txs,omitempty"` Transactions []*TxV1 `json:"txs,omitempty"`
} }
type CoinSpecificDataV0 struct {
FloData string
}
func stringToFloat(f string) float64 {
s, _ := strconv.ParseFloat(f, 64)
return s
}
// TxToV1 converts Tx to TxV1 // TxToV1 converts Tx to TxV1
func (w *Worker) TxToV1(tx *Tx) *TxV1 { func (w *Worker) TxToV1(tx *Tx) *TxV1 {
d := w.chainParser.AmountDecimals() d := w.chainParser.AmountDecimals()
@ -119,9 +135,10 @@ func (w *Worker) TxToV1(tx *Tx) *TxV1 {
IsAddress: v.IsAddress, IsAddress: v.IsAddress,
Sequence: v.Sequence, Sequence: v.Sequence,
Txid: v.Txid, Txid: v.Txid,
Value: v.ValueSat.DecimalString(d), Value: stringToFloat(v.ValueSat.DecimalString(d)),
ValueSat: v.ValueSat.AsBigInt(), ValueSat: v.ValueSat.AsBigInt(),
Vout: v.Vout, Vout: v.Vout,
Coinbase: v.Coinbase,
} }
} }
voutV1 := make([]VoutV1, len(tx.Vout)) voutV1 := make([]VoutV1, len(tx.Vout))
@ -141,29 +158,39 @@ func (w *Worker) TxToV1(tx *Tx) *TxV1 {
SpentHeight: v.SpentHeight, SpentHeight: v.SpentHeight,
SpentIndex: v.SpentIndex, SpentIndex: v.SpentIndex,
SpentTxID: v.SpentTxID, SpentTxID: v.SpentTxID,
Value: v.ValueSat.DecimalString(d), Value: stringToFloat(v.ValueSat.DecimalString(d)),
ValueSat: v.ValueSat.AsBigInt(), ValueSat: v.ValueSat.AsBigInt(),
} }
} }
//floData
var coinData CoinSpecificDataV0
// Notice the dereferencing asterisk *
err := json.Unmarshal(tx.CoinSpecificData, &coinData)
if err != nil {
return nil
}
return &TxV1{ return &TxV1{
Blockhash: tx.Blockhash, Blockhash: tx.Blockhash,
Blockheight: tx.Blockheight, Blockheight: tx.Blockheight,
Blocktime: tx.Blocktime, Blocktime: tx.Blocktime,
Confirmations: tx.Confirmations, Confirmations: tx.Confirmations,
Fees: tx.FeesSat.DecimalString(d), Fees: stringToFloat(tx.FeesSat.DecimalString(d)),
FeesSat: tx.FeesSat.AsBigInt(), FeesSat: tx.FeesSat.AsBigInt(),
Hex: tx.Hex, Hex: tx.Hex,
Locktime: tx.Locktime, Locktime: tx.Locktime,
Size: tx.Size, Size: tx.Size,
Time: tx.Blocktime, Time: tx.Blocktime,
Txid: tx.Txid, Txid: tx.Txid,
ValueIn: tx.ValueInSat.DecimalString(d), ValueIn: stringToFloat(tx.ValueInSat.DecimalString(d)),
ValueInSat: tx.ValueInSat.AsBigInt(), ValueInSat: tx.ValueInSat.AsBigInt(),
ValueOut: tx.ValueOutSat.DecimalString(d), ValueOut: stringToFloat(tx.ValueOutSat.DecimalString(d)),
ValueOutSat: tx.ValueOutSat.AsBigInt(), ValueOutSat: tx.ValueOutSat.AsBigInt(),
Version: tx.Version, Version: tx.Version,
Vin: vinV1, Vin: vinV1,
Vout: voutV1, Vout: voutV1,
FloData: coinData.FloData, //(tx.CoinSpecificData).FloData
} }
} }
@ -180,14 +207,18 @@ func (w *Worker) AddressToV1(a *Address) *AddressV1 {
d := w.chainParser.AmountDecimals() d := w.chainParser.AmountDecimals()
return &AddressV1{ return &AddressV1{
AddrStr: a.AddrStr, AddrStr: a.AddrStr,
Balance: a.BalanceSat.DecimalString(d), Balance: stringToFloat(a.BalanceSat.DecimalString(d)),
BalanceSat: a.BalanceSat.AsBigInt(),
Paging: a.Paging, Paging: a.Paging,
TotalReceived: a.TotalReceivedSat.DecimalString(d), TotalReceived: stringToFloat(a.TotalReceivedSat.DecimalString(d)),
TotalSent: a.TotalSentSat.DecimalString(d), TotalReceivedSat: a.TotalReceivedSat.AsBigInt(),
TotalSent: stringToFloat(a.TotalSentSat.DecimalString(d)),
TotalSentSat: a.TotalSentSat.AsBigInt(),
Transactions: w.transactionsToV1(a.Transactions), Transactions: w.transactionsToV1(a.Transactions),
TxApperances: a.Txs, TxApperances: a.Txs,
Txids: a.Txids, Txids: a.Txids,
UnconfirmedBalance: a.UnconfirmedBalanceSat.DecimalString(d), UnconfirmedBalance: stringToFloat(a.UnconfirmedBalanceSat.DecimalString(d)),
UnconfirmedBalanceSat: a.UnconfirmedBalanceSat.AsBigInt(),
UnconfirmedTxApperances: a.UnconfirmedTxs, UnconfirmedTxApperances: a.UnconfirmedTxs,
} }
} }
@ -200,7 +231,7 @@ func (w *Worker) AddressUtxoToV1(au Utxos) []AddressUtxoV1 {
utxo := &au[i] utxo := &au[i]
v1[i] = AddressUtxoV1{ v1[i] = AddressUtxoV1{
AmountSat: utxo.AmountSat.AsBigInt(), AmountSat: utxo.AmountSat.AsBigInt(),
Amount: utxo.AmountSat.DecimalString(d), Amount: stringToFloat(utxo.AmountSat.DecimalString(d)),
Confirmations: utxo.Confirmations, Confirmations: utxo.Confirmations,
Height: utxo.Height, Height: utxo.Height,
Txid: utxo.Txid, Txid: utxo.Txid,

View File

@ -36,6 +36,9 @@ type Worker struct {
metrics *common.Metrics metrics *common.Metrics
} }
// contractInfoCache is a temporary cache of contract information for ethereum token transfers
type contractInfoCache = map[string]*bchain.ContractInfo
// NewWorker creates new api worker // 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, fiatRates *fiat.FiatRates) (*Worker, error) { func NewWorker(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState, fiatRates *fiat.FiatRates) (*Worker, error) {
w := &Worker{ w := &Worker{
@ -169,9 +172,18 @@ func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliases
} }
for a := range addresses { for a := range addresses {
if w.chainType == bchain.ChainEthereumType { if w.chainType == bchain.ChainEthereumType {
ci, err := w.db.GetContractInfoForAddress(a) addrDesc, err := w.chainParser.GetAddrDescFromAddress(a)
if err == nil && ci != nil && ci.Name != "" { if err != nil || addrDesc == nil {
aliases[a] = AddressAlias{Type: "Contract", Alias: ci.Name} 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) n := w.db.GetAddressAlias(a)
@ -187,7 +199,7 @@ func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliases
// GetTransaction reads transaction data from txid // GetTransaction reads transaction data from txid
func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) { func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) {
addresses := w.newAddressesMapForAliases() addresses := w.newAddressesMapForAliases()
tx, err := w.getTransaction(txid, spendingTxs, specificJSON, addresses) tx, err := w.getTransaction(txid, spendingTxs, /*specificJSON*/ true, addresses)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -195,6 +207,11 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool
return tx, nil 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 // getTransaction reads transaction data from txid
func (w *Worker) getTransaction(txid string, spendingTxs bool, specificJSON bool, addresses map[string]struct{}) (*Tx, error) { func (w *Worker) getTransaction(txid string, spendingTxs bool, specificJSON bool, addresses map[string]struct{}) (*Tx, error) {
bchainTx, height, err := w.txCache.GetTransaction(txid) bchainTx, height, err := w.txCache.GetTransaction(txid)
@ -426,18 +443,25 @@ func (w *Worker) getTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
// mempool txs do not have fees yet // mempool txs do not have fees yet
if ethTxData.GasUsed != nil { if ethTxData.GasUsed != nil {
feesSat.Mul(ethTxData.GasPrice, ethTxData.GasUsed) feesSat.Mul(ethTxData.GasPrice, ethTxData.GasUsed)
if ethTxData.L1Fee != nil {
feesSat.Add(&feesSat, ethTxData.L1Fee)
}
} }
if len(bchainTx.Vout) > 0 { if len(bchainTx.Vout) > 0 {
valOutSat = bchainTx.Vout[0].ValueSat valOutSat = bchainTx.Vout[0].ValueSat
} }
ethSpecific = &EthereumSpecific{ ethSpecific = &EthereumSpecific{
GasLimit: ethTxData.GasLimit, GasLimit: ethTxData.GasLimit,
GasPrice: (*Amount)(ethTxData.GasPrice), GasPrice: (*Amount)(ethTxData.GasPrice),
GasUsed: ethTxData.GasUsed, GasUsed: ethTxData.GasUsed,
Nonce: ethTxData.Nonce, L1Fee: ethTxData.L1Fee,
Status: ethTxData.Status, L1FeeScalar: ethTxData.L1FeeScalar,
Data: ethTxData.Data, L1GasPrice: (*Amount)(ethTxData.L1GasPrice),
ParsedData: parsedInputData, L1GasUsed: ethTxData.L1GasUsed,
Nonce: ethTxData.Nonce,
Status: ethTxData.Status,
Data: ethTxData.Data,
ParsedData: parsedInputData,
} }
if internalData != nil { if internalData != nil {
ethSpecific.Type = internalData.Type ethSpecific.Type = internalData.Type
@ -598,7 +622,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx,
return r, nil 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) cd, err := w.chainParser.GetAddrDescFromAddress(contract)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
@ -638,7 +662,7 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom
glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) 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 // fix contract name/symbol that was parsed as a string consisting of zeroes
blockchainContractInfo, err := w.chain.GetContractInfo(cd) blockchainContractInfo, err := w.chain.GetContractInfo(cd)
if err != nil { if err != nil {
@ -657,6 +681,10 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom
if blockchainContractInfo != nil { if blockchainContractInfo != nil {
contractInfo.Decimals = blockchainContractInfo.Decimals 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 { if err = w.db.StoreContractInfo(contractInfo); err != nil {
glog.Errorf("StoreContractInfo error %v, contract %v", err, cd) glog.Errorf("StoreContractInfo error %v, contract %v", err, cd)
} }
@ -666,39 +694,49 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom
} }
func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, addresses map[string]struct{}) []TokenTransfer { func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, addresses map[string]struct{}) []TokenTransfer {
sort.Sort(transfers)
tokens := make([]TokenTransfer, len(transfers)) tokens := make([]TokenTransfer, len(transfers))
for i := range transfers { if len(transfers) > 0 {
t := transfers[i] sort.Sort(transfers)
typeName := bchain.EthereumTokenTypeMap[t.Type] contractCache := make(contractInfoCache)
contractInfo, _, err := w.getContractInfo(t.Contract, typeName) for i := range transfers {
if err != nil { t := transfers[i]
glog.Errorf("getContractInfo error %v, contract %v", err, t.Contract) typeName := bchain.EthereumTokenTypeMap[t.Type]
continue var contractInfo *bchain.ContractInfo
} if info, ok := contractCache[t.Contract]; ok {
var value *Amount contractInfo = info
var values []MultiTokenValue } else {
if t.Type == bchain.MultiToken { info, _, err := w.GetContractInfo(t.Contract, typeName)
values = make([]MultiTokenValue, len(t.MultiTokenValues)) if err != nil {
for j := range values { glog.Errorf("getContractInfo error %v, contract %v", err, t.Contract)
values[j].Id = (*Amount)(&t.MultiTokenValues[j].Id) continue
values[j].Value = (*Amount)(&t.MultiTokenValues[j].Value) }
contractInfo = info
contractCache[t.Contract] = info
}
var value *Amount
var values []MultiTokenValue
if t.Type == bchain.MultiToken {
values = make([]MultiTokenValue, len(t.MultiTokenValues))
for j := range values {
values[j].Id = (*Amount)(&t.MultiTokenValues[j].Id)
values[j].Value = (*Amount)(&t.MultiTokenValues[j].Value)
}
} else {
value = (*Amount)(&t.Value)
}
aggregateAddress(addresses, t.From)
aggregateAddress(addresses, t.To)
tokens[i] = TokenTransfer{
Type: typeName,
Contract: t.Contract,
From: t.From,
To: t.To,
Value: value,
MultiTokenValues: values,
Decimals: contractInfo.Decimals,
Name: contractInfo.Name,
Symbol: contractInfo.Symbol,
} }
} else {
value = (*Amount)(&t.Value)
}
aggregateAddress(addresses, t.From)
aggregateAddress(addresses, t.To)
tokens[i] = TokenTransfer{
Type: typeName,
Contract: t.Contract,
From: t.From,
To: t.To,
Value: value,
MultiTokenValues: values,
Decimals: contractInfo.Decimals,
Name: contractInfo.Name,
Symbol: contractInfo.Symbol,
} }
} }
return tokens return tokens
@ -1045,6 +1083,7 @@ type ethereumTypeAddressData struct {
totalResults int totalResults int
tokensBaseValue float64 tokensBaseValue float64
tokensSecondaryValue float64 tokensSecondaryValue float64
stakingPools []StakingPool
} }
func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter, secondaryCoin string) (*db.AddrBalance, *ethereumTypeAddressData, error) { func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter, secondaryCoin string) (*db.AddrBalance, *ethereumTypeAddressData, error) {
@ -1103,10 +1142,16 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
d.tokens = d.tokens[:j] d.tokens = d.tokens[:j]
sort.Sort(d.tokens) sort.Sort(d.tokens)
} }
d.contractInfo, err = w.db.GetContractInfo(addrDesc, "") d.contractInfo, err = w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType)
if err != nil { if err != nil {
return nil, nil, err 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 { if filter.FromHeight == 0 && filter.ToHeight == 0 {
// compute total results for paging // compute total results for paging
if filter.Vout == AddressFilterVoutOff { if filter.Vout == AddressFilterVoutOff {
@ -1144,9 +1189,41 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
filter.Vout = AddressFilterVoutQueryNotNecessary filter.Vout = AddressFilterVoutQueryNotNecessary
d.totalResults = -1 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
}
}
return ba, &d, nil return ba, &d, nil
} }
func (w *Worker) getStakingPoolsData(addrDesc bchain.AddressDescriptor) ([]StakingPool, error) {
var pools []StakingPool
if len(w.chain.EthereumTypeGetSupportedStakingPools()) > 0 {
sp, err := w.chain.EthereumTypeGetStakingPoolsData(addrDesc)
if err != nil {
return nil, err
}
for i := range sp {
p := &sp[i]
pools = append(pools, StakingPool{
Contract: p.Contract,
Name: p.Name,
PendingBalance: (*Amount)(&p.PendingBalance),
PendingDepositedBalance: (*Amount)(&p.PendingDepositedBalance),
DepositedBalance: (*Amount)(&p.DepositedBalance),
WithdrawTotalAmount: (*Amount)(&p.WithdrawTotalAmount),
ClaimableAmount: (*Amount)(&p.ClaimableAmount),
RestakedReward: (*Amount)(&p.RestakedReward),
AutocompoundBalance: (*Amount)(&p.AutocompoundBalance),
})
}
}
return pools, nil
}
func (w *Worker) txFromTxid(txid string, bestHeight uint32, option AccountDetails, blockInfo *db.BlockInfo, addresses map[string]struct{}) (*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 tx *Tx
var err error var err error
@ -1159,7 +1236,7 @@ func (w *Worker) txFromTxid(txid string, bestHeight uint32, option AccountDetail
if ta == nil { if ta == nil {
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses") glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
// as fallback, get tx from backend // as fallback, get tx from backend
tx, err = w.getTransaction(txid, false, false, addresses) tx, err = w.getTransaction(txid, false, true, addresses)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "getTransaction %v", txid) return nil, errors.Annotatef(err, "getTransaction %v", txid)
} }
@ -1178,7 +1255,7 @@ func (w *Worker) txFromTxid(txid string, bestHeight uint32, option AccountDetail
tx = w.txFromTxAddress(txid, ta, blockInfo, bestHeight, addresses) tx = w.txFromTxAddress(txid, ta, blockInfo, bestHeight, addresses)
} }
} else { } else {
tx, err = w.getTransaction(txid, false, false, addresses) tx, err = w.getTransaction(txid, false, true, addresses)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "getTransaction %v", txid) return nil, errors.Annotatef(err, "getTransaction %v", txid)
} }
@ -1337,7 +1414,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
if option == AccountDetailsTxidHistory { if option == AccountDetailsTxidHistory {
txids = append(txids, txid) txids = append(txids, txid)
} else { } else {
tx, err := w.txFromTxid(txid, bestheight, option, nil, addresses) tx, err := w.txFromTxid(txid, bestheight, AccountDetailsTxHistory, nil, addresses)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1388,12 +1465,13 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
ContractInfo: ed.contractInfo, ContractInfo: ed.contractInfo,
Nonce: ed.nonce, Nonce: ed.nonce,
AddressAliases: w.getAddressAliases(addresses), AddressAliases: w.getAddressAliases(addresses),
StakingPools: ed.stakingPools,
} }
// keep address backward compatible, set deprecated Erc20Contract value if ERC20 token // 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.Type == bchain.ERC20TokenType {
r.Erc20Contract = ed.contractInfo r.Erc20Contract = ed.contractInfo
} }
glog.Info("GetAddress ", address, ", ", time.Since(start)) glog.Info("GetAddress-", option, " ", address, ", ", time.Since(start))
return r, nil return r, nil
} }
@ -1603,6 +1681,17 @@ func (w *Worker) GetBalanceHistory(address string, fromTimestamp, toTimestamp in
if err != nil { if err != nil {
return nil, err return nil, err
} }
// do not get balance history for contracts
if w.chainType == bchain.ChainEthereumType {
ci, err := w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType)
if err != nil {
return nil, err
}
if ci != nil {
glog.Info("GetBalanceHistory ", address, " is a contract, skipping")
return nil, NewAPIError("GetBalanceHistory for a contract not allowed", true)
}
}
fromUnix, fromHeight, toUnix, toHeight := w.balanceHistoryHeightsFromTo(fromTimestamp, toTimestamp) fromUnix, fromHeight, toUnix, toHeight := w.balanceHistoryHeightsFromTo(fromTimestamp, toTimestamp)
if fromHeight >= toHeight { if fromHeight >= toHeight {
return bhs, nil return bhs, nil
@ -1880,7 +1969,11 @@ func (w *Worker) GetCurrentFiatRates(currencies []string, token string) (*FiatTi
ticker := w.fiatRates.GetCurrentTicker(vsCurrency, token) ticker := w.fiatRates.GetCurrentTicker(vsCurrency, token)
var err error var err error
if ticker == nil { if ticker == nil {
ticker, err = w.db.FiatRatesFindLastTicker(vsCurrency, token) if token == "" {
// fallback - get last fiat rate from db if not in current ticker
// not for tokens, many tokens do not have fiat rates at all and it is very costly to do DB search for token without an exchange rate
ticker, err = w.db.FiatRatesFindLastTicker(vsCurrency, token)
}
if err != nil { if err != nil {
return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false) return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false)
} else if ticker == nil { } else if ticker == nil {
@ -2146,7 +2239,7 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
txi := 0 txi := 0
addresses := w.newAddressesMapForAliases() addresses := w.newAddressesMapForAliases()
for i := from; i < to; i++ { for i := from; i < to; i++ {
txs[txi], err = w.txFromTxid(bi.Txids[i], bestheight, AccountDetailsTxHistoryLight, dbi, addresses) txs[txi], err = w.txFromTxid(bi.Txids[i], bestheight, AccountDetailsTxHistory, dbi, addresses)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -2200,6 +2293,48 @@ func (w *Worker) GetBlockRaw(bid string) (*BlockRaw, error) {
return &BlockRaw{Hex: hex}, err return &BlockRaw{Hex: hex}, err
} }
// GetBlockFiltersBatch returns array of block filter data in the format ["height:hash:filter",...] if blocks greater than bestKnownBlockHash
func (w *Worker) GetBlockFiltersBatch(bestKnownBlockHash string, pageSize int) ([]string, error) {
if w.is.BlockGolombFilterP == 0 {
return nil, NewAPIError("Not supported", true)
}
if pageSize > 10000 {
return nil, NewAPIError("pageSize max 10000", true)
}
if pageSize <= 0 {
pageSize = 1000
}
bi, err := w.chain.GetBlockInfo(bestKnownBlockHash)
if err != nil {
return nil, err
}
bestHeight, _, err := w.db.GetBestBlock()
if err != nil {
return nil, err
}
from := bi.Height + 1
to := bestHeight + 1
if from >= to {
return []string{}, nil
}
if to-from > uint32(pageSize) {
to = from + uint32(pageSize)
}
r := make([]string, 0, to-from)
for i := from; i < to; i++ {
blockHash, err := w.db.GetBlockHash(uint32(i))
if err != nil {
return nil, err
}
blockFilter, err := w.db.GetBlockFilter(blockHash)
if err != nil {
return nil, err
}
r = append(r, fmt.Sprintf("%d:%s:%s", i, blockHash, blockFilter))
}
return r, err
}
// ComputeFeeStats computes fee distribution in defined blocks and logs them to log // ComputeFeeStats computes fee distribution in defined blocks and logs them to log
func (w *Worker) ComputeFeeStats(blockFrom, blockTo int, stopCompute chan os.Signal) error { func (w *Worker) ComputeFeeStats(blockFrom, blockTo int, stopCompute chan os.Signal) error {
bestheight, _, err := w.db.GetBestBlock() bestheight, _, err := w.db.GetBestBlock()
@ -2302,6 +2437,7 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
} }
blockbookInfo := &BlockbookInfo{ blockbookInfo := &BlockbookInfo{
Coin: w.is.Coin, Coin: w.is.Coin,
Network: w.is.GetNetwork(),
Host: w.is.Host, Host: w.is.Host,
Version: vi.Version, Version: vi.Version,
GitCommit: vi.GitCommit, GitCommit: vi.GitCommit,
@ -2320,6 +2456,7 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
CurrentFiatRatesTime: nonZeroTime(currentFiatRatesTime), CurrentFiatRatesTime: nonZeroTime(currentFiatRatesTime),
HistoricalFiatRatesTime: nonZeroTime(w.is.HistoricalFiatRatesTime), HistoricalFiatRatesTime: nonZeroTime(w.is.HistoricalFiatRatesTime),
HistoricalTokenFiatRatesTime: nonZeroTime(w.is.HistoricalTokenFiatRatesTime), HistoricalTokenFiatRatesTime: nonZeroTime(w.is.HistoricalTokenFiatRatesTime),
SupportedStakingPools: w.chain.EthereumTypeGetSupportedStakingPools(),
DbSize: w.db.DatabaseSizeOnDisk(), DbSize: w.db.DatabaseSizeOnDisk(),
DbSizeFromColumns: internalDBSize, DbSizeFromColumns: internalDBSize,
DbColumns: columnStats, DbColumns: columnStats,

View File

@ -556,7 +556,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
usedTokens++ usedTokens++
} }
if option > AccountDetailsBasic { if option > AccountDetailsBasic {
token := w.tokenFromXpubAddress(data, ad, ci, i, option) token := w.tokenFromXpubAddress(data, ad, int(xd.ChangeIndexes[ci]), i, option)
if filter.TokensToReturn == TokensToReturnDerived || if filter.TokensToReturn == TokensToReturnDerived ||
filter.TokensToReturn == TokensToReturnUsed && ad.balance != nil || filter.TokensToReturn == TokensToReturnUsed && ad.balance != nil ||
filter.TokensToReturn == TokensToReturnNonzeroBalance && ad.balance != nil && !IsZeroBigInt(&ad.balance.BalanceSat) { filter.TokensToReturn == TokensToReturnNonzeroBalance && ad.balance != nil && !IsZeroBigInt(&ad.balance.BalanceSat) {

View File

@ -41,30 +41,47 @@ func (b *BaseChain) GetMempoolEntry(txid string) (*MempoolEntry, error) {
// EthereumTypeGetBalance is not supported // EthereumTypeGetBalance is not supported
func (b *BaseChain) EthereumTypeGetBalance(addrDesc AddressDescriptor) (*big.Int, error) { func (b *BaseChain) EthereumTypeGetBalance(addrDesc AddressDescriptor) (*big.Int, error) {
return nil, errors.New("Not supported") return nil, errors.New("not supported")
} }
// EthereumTypeGetNonce is not supported // EthereumTypeGetNonce is not supported
func (b *BaseChain) EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error) { func (b *BaseChain) EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error) {
return 0, errors.New("Not supported") return 0, errors.New("not supported")
} }
// EthereumTypeEstimateGas is not supported // EthereumTypeEstimateGas is not supported
func (b *BaseChain) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) { func (b *BaseChain) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) {
return 0, errors.New("Not supported") return 0, errors.New("not supported")
} }
// GetContractInfo is not supported // GetContractInfo is not supported
func (b *BaseChain) GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error) { func (b *BaseChain) GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error) {
return nil, errors.New("Not supported") return nil, errors.New("not supported")
} }
// EthereumTypeGetErc20ContractBalance is not supported // EthereumTypeGetErc20ContractBalance is not supported
func (b *BaseChain) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error) { func (b *BaseChain) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error) {
return nil, errors.New("Not supported") 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) { func (p *BaseChain) GetTokenURI(contractDesc AddressDescriptor, tokenID *big.Int) (string, error) {
return "", errors.New("Not supported") return "", errors.New("not supported")
}
func (b *BaseChain) EthereumTypeGetSupportedStakingPools() []string {
return nil
}
func (b *BaseChain) EthereumTypeGetStakingPoolsData(addrDesc AddressDescriptor) ([]StakingPoolData, error) {
return nil, errors.New("not supported")
}
// EthereumTypeRpcCall calls eth_call with given data and to address
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")
} }

View File

@ -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
}

View File

@ -110,7 +110,6 @@ func (b *AvalancheRPC) Initialize() error {
func (b *AvalancheRPC) GetChainInfo() (*bchain.ChainInfo, error) { func (b *AvalancheRPC) GetChainInfo() (*bchain.ChainInfo, error) {
ci, err := b.EthereumRPC.GetChainInfo() ci, err := b.EthereumRPC.GetChainInfo()
if err != nil { if err != nil {
fmt.Println(err)
return nil, err return nil, err
} }

View File

@ -337,10 +337,15 @@ func Test_UnpackTx(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
b, _ := hex.DecodeString(tt.args.packedTx) b, _ := hex.DecodeString(tt.args.packedTx)
got, got1, err := tt.args.parser.UnpackTx(b) got, got1, err := tt.args.parser.UnpackTx(b)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
// ignore witness unpacking
for i := range got.Vin {
got.Vin[i].Witness = nil
}
if !reflect.DeepEqual(got, tt.want) { if !reflect.DeepEqual(got, tt.want) {
t.Errorf("unpackTx() got = %v, want %v", got, tt.want) t.Errorf("unpackTx() got = %v, want %v", got, tt.want)
} }

View File

@ -316,6 +316,10 @@ func Test_UnpackTx(t *testing.T) {
t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
// ignore witness unpacking
for i := range got.Vin {
got.Vin[i].Witness = nil
}
if !reflect.DeepEqual(got, tt.want) { if !reflect.DeepEqual(got, tt.want) {
t.Errorf("unpackTx() got = %v, want %v", got, tt.want) t.Errorf("unpackTx() got = %v, want %v", got, tt.want)
} }

View File

@ -4,13 +4,14 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"math/big" "math/big"
"os"
"reflect" "reflect"
"time" "time"
"github.com/juju/errors" "github.com/juju/errors"
"github.com/trezor/blockbook/bchain" "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/avalanche"
"github.com/trezor/blockbook/bchain/coins/bch" "github.com/trezor/blockbook/bchain/coins/bch"
"github.com/trezor/blockbook/bchain/coins/bellcoin" "github.com/trezor/blockbook/bchain/coins/bellcoin"
@ -42,8 +43,10 @@ import (
"github.com/trezor/blockbook/bchain/coins/namecoin" "github.com/trezor/blockbook/bchain/coins/namecoin"
"github.com/trezor/blockbook/bchain/coins/nuls" "github.com/trezor/blockbook/bchain/coins/nuls"
"github.com/trezor/blockbook/bchain/coins/omotenashicoin" "github.com/trezor/blockbook/bchain/coins/omotenashicoin"
"github.com/trezor/blockbook/bchain/coins/optimism"
"github.com/trezor/blockbook/bchain/coins/pivx" "github.com/trezor/blockbook/bchain/coins/pivx"
"github.com/trezor/blockbook/bchain/coins/polis" "github.com/trezor/blockbook/bchain/coins/polis"
"github.com/trezor/blockbook/bchain/coins/polygon"
"github.com/trezor/blockbook/bchain/coins/qtum" "github.com/trezor/blockbook/bchain/coins/qtum"
"github.com/trezor/blockbook/bchain/coins/ravencoin" "github.com/trezor/blockbook/bchain/coins/ravencoin"
"github.com/trezor/blockbook/bchain/coins/ritocoin" "github.com/trezor/blockbook/bchain/coins/ritocoin"
@ -65,6 +68,7 @@ var BlockChainFactories = make(map[string]blockChainFactory)
func init() { func init() {
BlockChainFactories["Bitcoin"] = btc.NewBitcoinRPC BlockChainFactories["Bitcoin"] = btc.NewBitcoinRPC
BlockChainFactories["Testnet"] = btc.NewBitcoinRPC BlockChainFactories["Testnet"] = btc.NewBitcoinRPC
BlockChainFactories["Testnet4"] = btc.NewBitcoinRPC
BlockChainFactories["Signet"] = btc.NewBitcoinRPC BlockChainFactories["Signet"] = btc.NewBitcoinRPC
BlockChainFactories["Regtest"] = btc.NewBitcoinRPC BlockChainFactories["Regtest"] = btc.NewBitcoinRPC
BlockChainFactories["Zcash"] = zec.NewZCashRPC BlockChainFactories["Zcash"] = zec.NewZCashRPC
@ -72,12 +76,10 @@ func init() {
BlockChainFactories["Ethereum"] = eth.NewEthereumRPC BlockChainFactories["Ethereum"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Archive"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Archive"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Classic"] = 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["Ethereum Testnet Goerli Archive"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Testnet Sepolia"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Sepolia"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Testnet Sepolia Archive"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Sepolia Archive"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Testnet Holesky"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Testnet Holesky Archive"] = eth.NewEthereumRPC
BlockChainFactories["Bcash"] = bch.NewBCashRPC BlockChainFactories["Bcash"] = bch.NewBCashRPC
BlockChainFactories["Bcash Testnet"] = bch.NewBCashRPC BlockChainFactories["Bcash Testnet"] = bch.NewBCashRPC
BlockChainFactories["Bgold"] = btg.NewBGoldRPC BlockChainFactories["Bgold"] = btg.NewBGoldRPC
@ -113,6 +115,7 @@ func init() {
BlockChainFactories["Firo"] = firo.NewFiroRPC BlockChainFactories["Firo"] = firo.NewFiroRPC
BlockChainFactories["Fujicoin"] = fujicoin.NewFujicoinRPC BlockChainFactories["Fujicoin"] = fujicoin.NewFujicoinRPC
BlockChainFactories["Flo"] = flo.NewFloRPC BlockChainFactories["Flo"] = flo.NewFloRPC
BlockChainFactories["Flo Testnet"] = flo.NewFloRPC
BlockChainFactories["Bellcoin"] = bellcoin.NewBellcoinRPC BlockChainFactories["Bellcoin"] = bellcoin.NewBellcoinRPC
BlockChainFactories["Qtum"] = qtum.NewQtumRPC BlockChainFactories["Qtum"] = qtum.NewQtumRPC
BlockChainFactories["Viacoin"] = viacoin.NewViacoinRPC BlockChainFactories["Viacoin"] = viacoin.NewViacoinRPC
@ -137,25 +140,19 @@ func init() {
BlockChainFactories["Avalanche Archive"] = avalanche.NewAvalancheRPC BlockChainFactories["Avalanche Archive"] = avalanche.NewAvalancheRPC
BlockChainFactories["BNB Smart Chain"] = bsc.NewBNBSmartChainRPC BlockChainFactories["BNB Smart Chain"] = bsc.NewBNBSmartChainRPC
BlockChainFactories["BNB Smart Chain Archive"] = bsc.NewBNBSmartChainRPC BlockChainFactories["BNB Smart Chain Archive"] = bsc.NewBNBSmartChainRPC
} BlockChainFactories["Polygon"] = polygon.NewPolygonRPC
BlockChainFactories["Polygon Archive"] = polygon.NewPolygonRPC
// GetCoinNameFromConfig gets coin name and coin shortcut from config file BlockChainFactories["Optimism"] = optimism.NewOptimismRPC
func GetCoinNameFromConfig(configFileContent []byte) (string, string, string, error) { BlockChainFactories["Optimism Archive"] = optimism.NewOptimismRPC
var cn struct { BlockChainFactories["Arbitrum"] = arbitrum.NewArbitrumRPC
CoinName string `json:"coin_name"` BlockChainFactories["Arbitrum Archive"] = arbitrum.NewArbitrumRPC
CoinShortcut string `json:"coin_shortcut"` BlockChainFactories["Arbitrum Nova"] = arbitrum.NewArbitrumRPC
CoinLabel string `json:"coin_label"` BlockChainFactories["Arbitrum Nova Archive"] = arbitrum.NewArbitrumRPC
}
err := json.Unmarshal(configFileContent, &cn)
if err != nil {
return "", "", "", errors.Annotatef(err, "Error parsing config file ")
}
return cn.CoinName, cn.CoinShortcut, cn.CoinLabel, nil
} }
// NewBlockChain creates bchain.BlockChain and bchain.Mempool for the coin passed by the parameter coin // NewBlockChain creates bchain.BlockChain and bchain.Mempool for the coin passed by the parameter coin
func NewBlockChain(coin string, configfile string, pushHandler func(bchain.NotificationType), metrics *common.Metrics) (bchain.BlockChain, bchain.Mempool, error) { func NewBlockChain(coin string, configfile string, pushHandler func(bchain.NotificationType), metrics *common.Metrics) (bchain.BlockChain, bchain.Mempool, error) {
data, err := ioutil.ReadFile(configfile) data, err := os.ReadFile(configfile)
if err != nil { if err != nil {
return nil, nil, errors.Annotatef(err, "Error reading file %v", configfile) return nil, nil, errors.Annotatef(err, "Error reading file %v", configfile)
} }
@ -337,12 +334,32 @@ func (c *blockChainWithMetrics) EthereumTypeGetErc20ContractBalance(addrDesc, co
return c.b.EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc) 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) { 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()) defer func(s time.Time) { c.observeRPCLatency("GetTokenURI", s, err) }(time.Now())
return c.b.GetTokenURI(contractDesc, tokenID) return c.b.GetTokenURI(contractDesc, tokenID)
} }
func (c *blockChainWithMetrics) EthereumTypeGetSupportedStakingPools() []string {
return c.b.EthereumTypeGetSupportedStakingPools()
}
func (c *blockChainWithMetrics) EthereumTypeGetStakingPoolsData(addrDesc bchain.AddressDescriptor) (v []bchain.StakingPoolData, err error) {
defer func(s time.Time) { c.observeRPCLatency("EthereumTypeStakingPoolsData", s, err) }(time.Now())
return c.b.EthereumTypeGetStakingPoolsData(addrDesc)
}
// EthereumTypeRpcCall calls eth_call with given data and to address
func (c *blockChainWithMetrics) EthereumTypeRpcCall(data, to, from string) (v string, err error) {
defer func(s time.Time) { c.observeRPCLatency("EthereumTypeRpcCall", s, err) }(time.Now())
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 { type mempoolWithMetrics struct {
mempool bchain.Mempool mempool bchain.Mempool
m *common.Metrics m *common.Metrics

View File

@ -4,8 +4,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain"
@ -40,21 +38,14 @@ func NewBNBSmartChainRPC(config json.RawMessage, pushHandler func(bchain.Notific
s := &BNBSmartChainRPC{ s := &BNBSmartChainRPC{
EthereumRPC: c.(*eth.EthereumRPC), EthereumRPC: c.(*eth.EthereumRPC),
} }
s.Parser.EnsSuffix = ".bnb"
return s, nil return s, nil
} }
// Initialize bnb smart chain rpc interface // Initialize bnb smart chain rpc interface
func (b *BNBSmartChainRPC) Initialize() error { func (b *BNBSmartChainRPC) Initialize() error {
b.OpenRPC = func(url string) (bchain.EVMRPCClient, bchain.EVMClient, error) { b.OpenRPC = eth.OpenRPC
r, err := rpc.Dial(url)
if err != nil {
return nil, nil, err
}
rc := &eth.EthereumRPCClient{Client: r}
ec := &eth.EthereumClient{Client: ethclient.NewClient(r)}
return rc, ec, nil
}
rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL) rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL)
if err != nil { if err != nil {

View File

@ -0,0 +1,68 @@
package btc
import (
"fmt"
"math/big"
"sync"
"time"
"github.com/golang/glog"
"github.com/juju/errors"
"github.com/trezor/blockbook/bchain"
)
type alternativeFeeProviderFee struct {
blocks int
feePerKB int
}
type alternativeFeeProvider struct {
fees []alternativeFeeProviderFee
lastSync time.Time
chain bchain.BlockChain
mux sync.Mutex
}
type alternativeFeeProviderInterface interface {
compareToDefault()
estimateFee(blocks int) (big.Int, error)
}
func (p *alternativeFeeProvider) compareToDefault() {
output := ""
for _, fee := range p.fees {
conservative, err := p.chain.(*BitcoinRPC).blockchainEstimateSmartFee(fee.blocks, true)
if err != nil {
glog.Error(err)
return
}
economical, err := p.chain.(*BitcoinRPC).blockchainEstimateSmartFee(fee.blocks, false)
if err != nil {
glog.Error(err)
return
}
output += fmt.Sprintf("Blocks %d: alternative %d, conservative %s, economical %s\n", fee.blocks, fee.feePerKB, conservative.String(), economical.String())
}
glog.Info("alternativeFeeProviderCompareToDefault\n", output)
}
func (p *alternativeFeeProvider) estimateFee(blocks int) (big.Int, error) {
var r big.Int
p.mux.Lock()
defer p.mux.Unlock()
if len(p.fees) == 0 {
return r, errors.New("alternativeFeeProvider: no fees")
}
if p.lastSync.Before(time.Now().Add(time.Duration(-10) * time.Minute)) {
return r, errors.Errorf("alternativeFeeProvider: Missing recent value, last sync at %v", p.lastSync)
}
for i := range p.fees {
if p.fees[i].blocks >= blocks {
r = *big.NewInt(int64(p.fees[i].feePerKB))
return r, nil
}
}
// use the last value as fallback
r = *big.NewInt(int64(p.fees[len(p.fees)-1].feePerKB))
return r, nil
}

View File

@ -231,6 +231,7 @@ func (p *BitcoinLikeParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bcha
Vout: in.PreviousOutPoint.Index, Vout: in.PreviousOutPoint.Index,
Sequence: in.Sequence, Sequence: in.Sequence,
ScriptSig: s, ScriptSig: s,
Witness: in.Witness,
} }
} }
vout := make([]bchain.Vout, len(t.TxOut)) vout := make([]bchain.Vout, len(t.TxOut))

View File

@ -4,11 +4,28 @@ import (
"encoding/json" "encoding/json"
"math/big" "math/big"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/common" "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 // BitcoinParser handle
type BitcoinParser struct { type BitcoinParser struct {
*BitcoinLikeParser *BitcoinLikeParser
@ -33,6 +50,8 @@ func GetChainParams(chain string) *chaincfg.Params {
switch chain { switch chain {
case "test": case "test":
return &chaincfg.TestNet3Params return &chaincfg.TestNet3Params
case "testnet4":
return &TestNet4Params
case "regtest": case "regtest":
return &chaincfg.RegressionNetParams return &chaincfg.RegressionNetParams
case "signet": case "signet":

View File

@ -467,11 +467,12 @@ func TestGetAddressesFromAddrDescTestnet(t *testing.T) {
} }
var ( var (
testTx1, testTx2, testTx3 bchain.Tx testTx1, testTx2, testTx3, testTx4 bchain.Tx
testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700" testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700"
testTxPacked2 = "0007c91a899ab7da6a010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000" testTxPacked2 = "0007c91a899ab7da6a010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000"
testTxPacked3 = "00003d818bfda9aa3e02000000000102deb1999a857ab0a13d6b12fbd95ea75b409edde5f2ff747507ce42d9986a8b9d0000000000fdffffff9fd2d3361e203b2375eba6438efbef5b3075531e7e583c7cc76b7294fe7f22980000000000fdffffff02a0860100000000001600148091746745464e7555c31e9a5afceac14a02978ae7fc1c0000000000160014565ea9ff4589d3e05ba149ae6e257752bfdc2a1e0247304402207d67d320a8e813f986b35e9791935fcb736754812b7038686f5de6cfdcda99cd02201c3bb2c178e0056016437ecfe365a7eef84aa9d293ebdc566177af82e22fcdd3012103abb30c1bbe878b07b58dc169b1d061d48c60be8107f632a59778b38bf7ceea5a02473044022044f54a478cfe086e870cb026c9dcd4e14e63778bef569a4d55a6332725cd9a9802202f0e94c04e6f328fc64ad9efe552888c299750d1b8d033324825a3ff29920e030121036fcd433428aa7dc65c4f5408fa31f208c54fe4b4c6c1ae9c39a825ed4f1ac039813d0000" testTxPacked3 = "00003d818bfda9aa3e02000000000102deb1999a857ab0a13d6b12fbd95ea75b409edde5f2ff747507ce42d9986a8b9d0000000000fdffffff9fd2d3361e203b2375eba6438efbef5b3075531e7e583c7cc76b7294fe7f22980000000000fdffffff02a0860100000000001600148091746745464e7555c31e9a5afceac14a02978ae7fc1c0000000000160014565ea9ff4589d3e05ba149ae6e257752bfdc2a1e0247304402207d67d320a8e813f986b35e9791935fcb736754812b7038686f5de6cfdcda99cd02201c3bb2c178e0056016437ecfe365a7eef84aa9d293ebdc566177af82e22fcdd3012103abb30c1bbe878b07b58dc169b1d061d48c60be8107f632a59778b38bf7ceea5a02473044022044f54a478cfe086e870cb026c9dcd4e14e63778bef569a4d55a6332725cd9a9802202f0e94c04e6f328fc64ad9efe552888c299750d1b8d033324825a3ff29920e030121036fcd433428aa7dc65c4f5408fa31f208c54fe4b4c6c1ae9c39a825ed4f1ac039813d0000"
testTxPacked4 = "0000a2b98ced82b6400300000000010148f8f93ebb12407809920d2ab9cc1bf01289b314eb23028c83fdab21e5fefa690100000000fdffffff0150c3000000000000160014cb888de3c89670a3061fb6ef6590f187649cca060247304402206a9db8d7157e4b0a06a1f090b9de88cdc616028b431b80617a055117877e479a02202937d6d1658d4a8afde86b245325c3bb0e769a87cb09d802bcefaa21550065e201210374aa8f312de4ebccbef55609700a39764387aa4ff5d76f1ccb4d2382e454f05b00000000"
) )
func init() { 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) { func TestPackTx(t *testing.T) {
@ -643,6 +675,17 @@ func TestPackTx(t *testing.T) {
want: testTxPacked3, want: testTxPacked3,
wantErr: false, 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 { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -701,6 +744,16 @@ func TestUnpackTx(t *testing.T) {
want1: 15745, want1: 15745,
wantErr: false, wantErr: false,
}, },
{
name: "testnet4-1",
args: args{
packedTx: testTxPacked4,
parser: NewBitcoinParser(GetChainParams("testnet4"), &Configuration{}),
},
want: &testTx4,
want1: 41657,
wantErr: false,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -710,6 +763,10 @@ func TestUnpackTx(t *testing.T) {
t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
// ignore witness unpacking
for i := range got.Vin {
got.Vin[i].Witness = nil
}
if !reflect.DeepEqual(got, tt.want) { if !reflect.DeepEqual(got, tt.want) {
t.Errorf("unpackTx() got = %v, want %v", got, tt.want) t.Errorf("unpackTx() got = %v, want %v", got, tt.want)
} }

View File

@ -6,7 +6,6 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"io" "io"
"io/ioutil"
"math/big" "math/big"
"net" "net"
"net/http" "net/http"
@ -23,18 +22,20 @@ import (
// BitcoinRPC is an interface to JSON-RPC bitcoind service. // BitcoinRPC is an interface to JSON-RPC bitcoind service.
type BitcoinRPC struct { type BitcoinRPC struct {
*bchain.BaseChain *bchain.BaseChain
client http.Client client http.Client
rpcURL string rpcURL string
user string user string
password string password string
Mempool *bchain.MempoolBitcoinType Mempool *bchain.MempoolBitcoinType
ParseBlocks bool ParseBlocks bool
pushHandler func(bchain.NotificationType) pushHandler func(bchain.NotificationType)
mq *bchain.MQ mq *bchain.MQ
ChainConfig *Configuration ChainConfig *Configuration
RPCMarshaler RPCMarshaler RPCMarshaler RPCMarshaler
golombFilterP uint8 mempoolGolombFilterP uint8
mempoolFilterScripts string mempoolFilterScripts string
mempoolUseZeroedKey bool
alternativeFeeProvider alternativeFeeProviderInterface
} }
// Configuration represents json config file // Configuration represents json config file
@ -62,8 +63,9 @@ type Configuration struct {
AlternativeEstimateFee string `json:"alternative_estimate_fee,omitempty"` AlternativeEstimateFee string `json:"alternative_estimate_fee,omitempty"`
AlternativeEstimateFeeParams string `json:"alternative_estimate_fee_params,omitempty"` AlternativeEstimateFeeParams string `json:"alternative_estimate_fee_params,omitempty"`
MinimumCoinbaseConfirmations int `json:"minimumCoinbaseConfirmations,omitempty"` MinimumCoinbaseConfirmations int `json:"minimumCoinbaseConfirmations,omitempty"`
GolombFilterP uint8 `json:"golomb_filter_p,omitempty"` MempoolGolombFilterP uint8 `json:"mempool_golomb_filter_p,omitempty"`
MempoolFilterScripts string `json:"mempool_filter_scripts,omitempty"` MempoolFilterScripts string `json:"mempool_filter_scripts,omitempty"`
MempoolFilterUseZeroedKey bool `json:"mempool_filter_use_zeroed_key,omitempty"`
} }
// NewBitcoinRPC returns new BitcoinRPC instance. // NewBitcoinRPC returns new BitcoinRPC instance.
@ -109,8 +111,9 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationT
ChainConfig: &c, ChainConfig: &c,
pushHandler: pushHandler, pushHandler: pushHandler,
RPCMarshaler: JSONMarshalerV2{}, RPCMarshaler: JSONMarshalerV2{},
golombFilterP: c.GolombFilterP, mempoolGolombFilterP: c.MempoolGolombFilterP,
mempoolFilterScripts: c.MempoolFilterScripts, mempoolFilterScripts: c.MempoolFilterScripts,
mempoolUseZeroedKey: c.MempoolFilterUseZeroedKey,
} }
return s, nil return s, nil
@ -143,10 +146,16 @@ func (b *BitcoinRPC) Initialize() error {
glog.Info("rpc: block chain ", params.Name) glog.Info("rpc: block chain ", params.Name)
if b.ChainConfig.AlternativeEstimateFee == "whatthefee" { if b.ChainConfig.AlternativeEstimateFee == "whatthefee" {
if err = InitWhatTheFee(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil { if b.alternativeFeeProvider, err = NewWhatTheFee(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil {
glog.Error("InitWhatTheFee error ", err, " Reverting to default estimateFee functionality") glog.Error("NewWhatTheFee error ", err, " Reverting to default estimateFee functionality")
// disable AlternativeEstimateFee logic // disable AlternativeEstimateFee logic
b.ChainConfig.AlternativeEstimateFee = "" b.alternativeFeeProvider = nil
}
} else if b.ChainConfig.AlternativeEstimateFee == "mempoolspace" {
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
} }
} }
@ -156,7 +165,7 @@ func (b *BitcoinRPC) Initialize() error {
// CreateMempool creates mempool if not already created, however does not initialize it // CreateMempool creates mempool if not already created, however does not initialize it
func (b *BitcoinRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) { func (b *BitcoinRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) {
if b.Mempool == nil { if b.Mempool == nil {
b.Mempool = bchain.NewMempoolBitcoinType(chain, b.ChainConfig.MempoolWorkers, b.ChainConfig.MempoolSubWorkers, b.golombFilterP, b.mempoolFilterScripts) b.Mempool = bchain.NewMempoolBitcoinType(chain, b.ChainConfig.MempoolWorkers, b.ChainConfig.MempoolSubWorkers, b.mempoolGolombFilterP, b.mempoolFilterScripts, b.mempoolUseZeroedKey)
} }
return b.Mempool, nil return b.Mempool, nil
} }
@ -772,8 +781,7 @@ func (b *BitcoinRPC) getRawTransaction(txid string) (json.RawMessage, error) {
return res.Result, nil return res.Result, nil
} }
// EstimateSmartFee returns fee estimation func (b *BitcoinRPC) blockchainEstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
// use EstimateFee if EstimateSmartFee is not supported // use EstimateFee if EstimateSmartFee is not supported
if !b.ChainConfig.SupportsEstimateSmartFee && b.ChainConfig.SupportsEstimateFee { if !b.ChainConfig.SupportsEstimateSmartFee && b.ChainConfig.SupportsEstimateFee {
return b.EstimateFee(blocks) return b.EstimateFee(blocks)
@ -790,7 +798,6 @@ func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, e
req.Params.EstimateMode = "ECONOMICAL" req.Params.EstimateMode = "ECONOMICAL"
} }
err := b.Call(&req, &res) err := b.Call(&req, &res)
var r big.Int var r big.Int
if err != nil { if err != nil {
return r, err return r, err
@ -805,8 +812,31 @@ func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, e
return r, nil return r, nil
} }
// EstimateSmartFee returns fee estimation
func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
// use alternative estimator if enabled
if b.alternativeFeeProvider != nil {
r, err := b.alternativeFeeProvider.estimateFee(blocks)
// in case of error, fallback to default estimator
if err == nil {
return r, nil
}
}
return b.blockchainEstimateSmartFee(blocks, conservative)
}
// EstimateFee returns fee estimation. // EstimateFee returns fee estimation.
func (b *BitcoinRPC) EstimateFee(blocks int) (big.Int, error) { func (b *BitcoinRPC) EstimateFee(blocks int) (big.Int, error) {
var r big.Int
var err error
// use alternative estimator if enabled
if b.alternativeFeeProvider != nil {
r, err = b.alternativeFeeProvider.estimateFee(blocks)
// in case of error, fallback to default estimator
if err == nil {
return r, nil
}
}
// use EstimateSmartFee if EstimateFee is not supported // use EstimateSmartFee if EstimateFee is not supported
if !b.ChainConfig.SupportsEstimateFee && b.ChainConfig.SupportsEstimateSmartFee { if !b.ChainConfig.SupportsEstimateFee && b.ChainConfig.SupportsEstimateSmartFee {
return b.EstimateSmartFee(blocks, true) return b.EstimateSmartFee(blocks, true)
@ -817,9 +847,8 @@ func (b *BitcoinRPC) EstimateFee(blocks int) (big.Int, error) {
res := ResEstimateFee{} res := ResEstimateFee{}
req := CmdEstimateFee{Method: "estimatefee"} req := CmdEstimateFee{Method: "estimatefee"}
req.Params.Blocks = blocks req.Params.Blocks = blocks
err := b.Call(&req, &res) err = b.Call(&req, &res)
var r big.Int
if err != nil { if err != nil {
return r, err return r, err
} }
@ -891,7 +920,7 @@ func safeDecodeResponse(body io.ReadCloser, res interface{}) (err error) {
} }
} }
}() }()
data, err = ioutil.ReadAll(body) data, err = io.ReadAll(body)
if err != nil { if err != nil {
return err return err
} }

View File

@ -0,0 +1,135 @@
package btc
import (
"bytes"
"encoding/json"
"net/http"
"strconv"
"time"
"github.com/golang/glog"
"github.com/juju/errors"
"github.com/trezor/blockbook/bchain"
)
// https://mempool.space/api/v1/fees/recommended returns
// {"fastestFee":41,"halfHourFee":39,"hourFee":36,"economyFee":36,"minimumFee":20}
type mempoolSpaceFeeResult struct {
FastestFee int `json:"fastestFee"`
HalfHourFee int `json:"halfHourFee"`
HourFee int `json:"hourFee"`
EconomyFee int `json:"economyFee"`
MinimumFee int `json:"minimumFee"`
}
type mempoolSpaceFeeParams struct {
URL string `json:"url"`
PeriodSeconds int `periodSeconds:"url"`
}
type mempoolSpaceFeeProvider struct {
*alternativeFeeProvider
params mempoolSpaceFeeParams
}
// NewMempoolSpaceFee initializes https://mempool.space provider
func NewMempoolSpaceFee(chain bchain.BlockChain, params string) (alternativeFeeProviderInterface, error) {
p := &mempoolSpaceFeeProvider{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("NewWhatTheFee: Missing parameters")
}
p.chain = chain
go p.mempoolSpaceFeeDownloader()
return p, nil
}
func (p *mempoolSpaceFeeProvider) mempoolSpaceFeeDownloader() {
period := time.Duration(p.params.PeriodSeconds) * time.Second
timer := time.NewTimer(period)
counter := 0
for {
var data mempoolSpaceFeeResult
err := p.mempoolSpaceFeeGetData(&data)
if err != nil {
glog.Error("mempoolSpaceFeeGetData ", err)
} else {
if p.mempoolSpaceFeeProcessData(&data) {
if counter%60 == 0 {
p.compareToDefault()
}
counter++
}
}
<-timer.C
timer.Reset(period)
}
}
func (p *mempoolSpaceFeeProvider) mempoolSpaceFeeProcessData(data *mempoolSpaceFeeResult) bool {
if data.MinimumFee == 0 || data.EconomyFee == 0 || data.HourFee == 0 || data.HalfHourFee == 0 || data.FastestFee == 0 {
glog.Errorf("mempoolSpaceFeeProcessData: invalid data %+v", data)
return false
}
p.mux.Lock()
defer p.mux.Unlock()
p.fees = make([]alternativeFeeProviderFee, 5)
// map mempoool.space fees to blocks
// FastestFee is for 1 block
p.fees[0] = alternativeFeeProviderFee{
blocks: 1,
feePerKB: data.FastestFee * 1000,
}
// HalfHourFee is for 2-6 blocks
p.fees[1] = alternativeFeeProviderFee{
blocks: 6,
feePerKB: data.HalfHourFee * 1000,
}
// HourFee is for 7-36 blocks
p.fees[2] = alternativeFeeProviderFee{
blocks: 36,
feePerKB: data.HourFee * 1000,
}
// EconomyFee is for 37-200 blocks
p.fees[3] = alternativeFeeProviderFee{
blocks: 500,
feePerKB: data.EconomyFee * 1000,
}
// MinimumFee is for over 500 blocks
p.fees[4] = alternativeFeeProviderFee{
blocks: 1000,
feePerKB: data.MinimumFee * 1000,
}
p.lastSync = time.Now()
// glog.Infof("mempoolSpaceFees: %+v", p.fees)
return true
}
func (p *mempoolSpaceFeeProvider) mempoolSpaceFeeGetData(res interface{}) error {
var httpData []byte
httpReq, err := http.NewRequest("GET", p.params.URL, bytes.NewBuffer(httpData))
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 safeDecodeResponse(httpRes.Body, &res)
}

View File

@ -0,0 +1,53 @@
package btc
import (
"math/big"
"strconv"
"testing"
)
func Test_mempoolSpaceFeeProvider(t *testing.T) {
m := &mempoolSpaceFeeProvider{alternativeFeeProvider: &alternativeFeeProvider{}}
m.mempoolSpaceFeeProcessData(&mempoolSpaceFeeResult{
MinimumFee: 10,
EconomyFee: 20,
HourFee: 30,
HalfHourFee: 40,
FastestFee: 50,
})
tests := []struct {
blocks int
want big.Int
}{
{0, *big.NewInt(50000)},
{1, *big.NewInt(50000)},
{2, *big.NewInt(40000)},
{5, *big.NewInt(40000)},
{6, *big.NewInt(40000)},
{7, *big.NewInt(30000)},
{10, *big.NewInt(30000)},
{18, *big.NewInt(30000)},
{19, *big.NewInt(30000)},
{36, *big.NewInt(30000)},
{37, *big.NewInt(20000)},
{100, *big.NewInt(20000)},
{101, *big.NewInt(20000)},
{200, *big.NewInt(20000)},
{201, *big.NewInt(20000)},
{500, *big.NewInt(20000)},
{501, *big.NewInt(10000)},
{5000000, *big.NewInt(10000)},
}
for _, tt := range tests {
t.Run(strconv.Itoa(tt.blocks), func(t *testing.T) {
got, err := m.estimateFee(tt.blocks)
if err != nil {
t.Error("estimateFee returned error ", err)
}
if got.Cmp(&tt.want) != 0 {
t.Errorf("estimateFee(%d) = %v, want %v", tt.blocks, got, tt.want)
}
})
}
}

View File

@ -3,11 +3,9 @@ package btc
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"math" "math"
"net/http" "net/http"
"strconv" "strconv"
"sync"
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
@ -34,49 +32,40 @@ type whatTheFeeParams struct {
PeriodSeconds int `periodSeconds:"url"` PeriodSeconds int `periodSeconds:"url"`
} }
type whatTheFeeFee struct { type whatTheFeeProvider struct {
blocks int *alternativeFeeProvider
feesPerKB []int
}
type whatTheFeeData struct {
params whatTheFeeParams params whatTheFeeParams
probabilities []string probabilities []string
fees []whatTheFeeFee
lastSync time.Time
chain bchain.BlockChain
mux sync.Mutex
} }
var whatTheFee whatTheFeeData // NewWhatTheFee initializes https://whatthefee.io provider
func NewWhatTheFee(chain bchain.BlockChain, params string) (alternativeFeeProviderInterface, error) {
// InitWhatTheFee initializes https://whatthefee.io handler var p whatTheFeeProvider
func InitWhatTheFee(chain bchain.BlockChain, params string) error { err := json.Unmarshal([]byte(params), &p.params)
err := json.Unmarshal([]byte(params), &whatTheFee.params)
if err != nil { if err != nil {
return err return nil, err
} }
if whatTheFee.params.URL == "" || whatTheFee.params.PeriodSeconds == 0 { if p.params.URL == "" || p.params.PeriodSeconds == 0 {
return errors.New("Missing parameters") return nil, errors.New("NewWhatTheFee: Missing parameters")
} }
whatTheFee.chain = chain p.chain = chain
go whatTheFeeDownloader() go p.whatTheFeeDownloader()
return nil return &p, nil
} }
func whatTheFeeDownloader() { func (p *whatTheFeeProvider) whatTheFeeDownloader() {
period := time.Duration(whatTheFee.params.PeriodSeconds) * time.Second period := time.Duration(p.params.PeriodSeconds) * time.Second
timer := time.NewTimer(period) timer := time.NewTimer(period)
counter := 0 counter := 0
for { for {
var data whatTheFeeServiceResult var data whatTheFeeServiceResult
err := whatTheFeeGetData(&data) err := p.whatTheFeeGetData(&data)
if err != nil { if err != nil {
glog.Error("whatTheFeeGetData ", err) glog.Error("whatTheFeeGetData ", err)
} else { } else {
if whatTheFeeProcessData(&data) { if p.whatTheFeeProcessData(&data) {
if counter%60 == 0 { if counter%60 == 0 {
whatTheFeeCompareToDefault() p.compareToDefault()
} }
counter++ counter++
} }
@ -86,15 +75,15 @@ func whatTheFeeDownloader() {
} }
} }
func whatTheFeeProcessData(data *whatTheFeeServiceResult) bool { func (p *whatTheFeeProvider) whatTheFeeProcessData(data *whatTheFeeServiceResult) bool {
if len(data.Index) == 0 || len(data.Index) != len(data.Data) || len(data.Columns) == 0 { if len(data.Index) == 0 || len(data.Index) != len(data.Data) || len(data.Columns) == 0 {
glog.Errorf("invalid data %+v", data) glog.Errorf("invalid data %+v", data)
return false return false
} }
whatTheFee.mux.Lock() p.mux.Lock()
defer whatTheFee.mux.Unlock() defer p.mux.Unlock()
whatTheFee.probabilities = data.Columns p.probabilities = data.Columns
whatTheFee.fees = make([]whatTheFeeFee, len(data.Index)) p.fees = make([]alternativeFeeProviderFee, len(data.Index))
for i, blocks := range data.Index { for i, blocks := range data.Index {
if len(data.Columns) != len(data.Data[i]) { if len(data.Columns) != len(data.Data[i]) {
glog.Errorf("invalid data %+v", data) glog.Errorf("invalid data %+v", data)
@ -104,19 +93,19 @@ func whatTheFeeProcessData(data *whatTheFeeServiceResult) bool {
for j, l := range data.Data[i] { for j, l := range data.Data[i] {
fees[j] = int(1000 * math.Exp(float64(l)/100)) fees[j] = int(1000 * math.Exp(float64(l)/100))
} }
whatTheFee.fees[i] = whatTheFeeFee{ p.fees[i] = alternativeFeeProviderFee{
blocks: blocks, blocks: blocks,
feesPerKB: fees, feePerKB: fees[len(fees)/2],
} }
} }
whatTheFee.lastSync = time.Now() p.lastSync = time.Now()
glog.Infof("%+v", whatTheFee.fees) glog.Infof("whatTheFees: %+v", p.fees)
return true return true
} }
func whatTheFeeGetData(res interface{}) error { func (p *whatTheFeeProvider) whatTheFeeGetData(res interface{}) error {
var httpData []byte var httpData []byte
httpReq, err := http.NewRequest("GET", whatTheFee.params.URL, bytes.NewBuffer(httpData)) httpReq, err := http.NewRequest("GET", p.params.URL, bytes.NewBuffer(httpData))
if err != nil { if err != nil {
return err return err
} }
@ -132,25 +121,3 @@ func whatTheFeeGetData(res interface{}) error {
} }
return safeDecodeResponse(httpRes.Body, &res) return safeDecodeResponse(httpRes.Body, &res)
} }
func whatTheFeeCompareToDefault() {
output := ""
for _, fee := range whatTheFee.fees {
output += fmt.Sprint(fee.blocks, ",")
for _, wtf := range fee.feesPerKB {
output += fmt.Sprint(wtf, ",")
}
conservative, err := whatTheFee.chain.EstimateSmartFee(fee.blocks, true)
if err != nil {
glog.Error(err)
return
}
economical, err := whatTheFee.chain.EstimateSmartFee(fee.blocks, false)
if err != nil {
glog.Error(err)
return
}
output += fmt.Sprint(conservative.String(), ",", economical.String(), "\n")
}
glog.Info("whatTheFeeCompareToDefault\n", output)
}

View File

@ -3,11 +3,11 @@ package ecash
import ( import (
"fmt" "fmt"
"github.com/pirk/ecashutil"
"github.com/martinboehm/btcutil" "github.com/martinboehm/btcutil"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"github.com/martinboehm/btcutil/txscript" "github.com/martinboehm/btcutil/txscript"
"github.com/pirk/ecashaddr-converter/address" "github.com/pirk/ecashaddr-converter/address"
"github.com/pirk/ecashutil"
"github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/bchain/coins/btc" "github.com/trezor/blockbook/bchain/coins/btc"
) )

View File

@ -342,6 +342,10 @@ func Test_UnpackTx(t *testing.T) {
t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
// ignore witness unpacking
for i := range got.Vin {
got.Vin[i].Witness = nil
}
if !reflect.DeepEqual(got, tt.want) { if !reflect.DeepEqual(got, tt.want) {
t.Errorf("unpackTx() got = %v, want %v", got, tt.want) t.Errorf("unpackTx() got = %v, want %v", got, tt.want)
} }

View File

@ -273,14 +273,19 @@ func contractGetTransfersFromTx(tx *bchain.RpcTransaction) (bchain.TokenTransfer
return r, nil return r, nil
} }
func (b *EthereumRPC) ethCall(data, to string) (string, error) { // 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) ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
defer cancel() defer cancel()
var r string var r string
err := b.RPC.CallContext(ctx, &r, "eth_call", map[string]interface{}{ args := map[string]interface{}{
"data": data, "data": data,
"to": to, "to": to,
}, "latest") }
if from != "" {
args["from"] = from
}
err := b.RPC.CallContext(ctx, &r, "eth_call", args, "latest")
if err != nil { if err != nil {
return "", err return "", err
} }
@ -289,7 +294,7 @@ func (b *EthereumRPC) ethCall(data, to string) (string, error) {
func (b *EthereumRPC) fetchContractInfo(address string) (*bchain.ContractInfo, error) { func (b *EthereumRPC) fetchContractInfo(address string) (*bchain.ContractInfo, error) {
var contract bchain.ContractInfo var contract bchain.ContractInfo
data, err := b.ethCall(contractNameSignature, address) data, err := b.EthereumTypeRpcCall(contractNameSignature, address, "")
if err != nil { if err != nil {
// ignore the error from the eth_call - since geth v1.9.15 they changed the behavior // 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 // and returning error "execution reverted" for some non contract addresses
@ -300,14 +305,14 @@ func (b *EthereumRPC) fetchContractInfo(address string) (*bchain.ContractInfo, e
} }
name := strings.TrimSpace(parseSimpleStringProperty(data)) name := strings.TrimSpace(parseSimpleStringProperty(data))
if name != "" { if name != "" {
data, err = b.ethCall(contractSymbolSignature, address) data, err = b.EthereumTypeRpcCall(contractSymbolSignature, address, "")
if err != nil { if err != nil {
// glog.Warning(errors.Annotatef(err, "Contract SymbolSignature %v", address)) // glog.Warning(errors.Annotatef(err, "Contract SymbolSignature %v", address))
return nil, nil return nil, nil
// return nil, errors.Annotatef(err, "erc20SymbolSignature %v", address) // return nil, errors.Annotatef(err, "erc20SymbolSignature %v", address)
} }
symbol := strings.TrimSpace(parseSimpleStringProperty(data)) symbol := strings.TrimSpace(parseSimpleStringProperty(data))
data, _ = b.ethCall(contractDecimalsSignature, address) data, _ = b.EthereumTypeRpcCall(contractDecimalsSignature, address, "")
// if err != nil { // if err != nil {
// glog.Warning(errors.Annotatef(err, "Contract DecimalsSignature %v", address)) // glog.Warning(errors.Annotatef(err, "Contract DecimalsSignature %v", address))
// // return nil, errors.Annotatef(err, "erc20DecimalsSignature %v", address) // // return nil, errors.Annotatef(err, "erc20DecimalsSignature %v", address)
@ -337,10 +342,10 @@ func (b *EthereumRPC) GetContractInfo(contractDesc bchain.AddressDescriptor) (*b
// EthereumTypeGetErc20ContractBalance returns balance of ERC20 contract for given address // EthereumTypeGetErc20ContractBalance returns balance of ERC20 contract for given address
func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) { func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) {
addr := hexutil.Encode(addrDesc) addr := hexutil.Encode(addrDesc)[2:]
contract := hexutil.Encode(contractDesc) contract := hexutil.Encode(contractDesc)
req := contractBalanceOfSignature + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr)-2:] + addr[2:] req := contractBalanceOfSignature + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr):] + addr
data, err := b.ethCall(req, contract) data, err := b.EthereumTypeRpcCall(req, contract, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -351,7 +356,7 @@ func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc
return r, nil 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) { func (b *EthereumRPC) GetTokenURI(contractDesc bchain.AddressDescriptor, tokenID *big.Int) (string, error) {
address := hexutil.Encode(contractDesc) address := hexutil.Encode(contractDesc)
// CryptoKitties do not fully support ERC721 standard, do not have tokenURI method // CryptoKitties do not fully support ERC721 standard, do not have tokenURI method
@ -364,7 +369,7 @@ func (b *EthereumRPC) GetTokenURI(contractDesc bchain.AddressDescriptor, tokenID
} }
// try ERC721 tokenURI method and ERC1155 uri method // try ERC721 tokenURI method and ERC1155 uri method
for _, method := range []string{erc721TokenURIMethodSignature, erc1155URIMethodSignature} { for _, method := range []string{erc721TokenURIMethodSignature, erc1155URIMethodSignature} {
data, err := b.ethCall(method+id, address) data, err := b.EthereumTypeRpcCall(method+id, address, "")
if err == nil && data != "" { if err == nil && data != "" {
uri := parseSimpleStringProperty(data) uri := parseSimpleStringProperty(data)
// try to sanitize the URI returned from the contract // try to sanitize the URI returned from the contract

View File

@ -1,291 +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 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.NonFungibleToken,
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.MultiToken,
Contract: "0x6Fd712E3A5B556654044608F9129040A4839E36c",
From: "0xa3950b823cb063dd9afc0d27f35008b805b3ed53",
To: "0x4392faf3bb96b5694ecc6ef64726f61cdd4bb0ec",
MultiTokenValues: []bchain.MultiTokenValue{{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.MultiToken,
Contract: "0x6c42c26a081c2f509f8bb68fb7ac3062311ccfb7",
From: "0x0000000000000000000000000000000000000000",
To: "0x5dc6288b35e0807a3d6feb89b3a2ff4ab773168e",
MultiTokenValues: []bchain.MultiTokenValue{
{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, false)
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.FungibleToken,
Contract: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2",
From: "0x20cd153de35d469ba46127a0c8f18626b59a256a",
To: "0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f",
Value: *bn,
},
},
},
{
name: "ERC721 transferFrom",
args: (b2.Txs[2].CoinSpecificData.(bchain.EthereumSpecificData)).Tx,
want: bchain.TokenTransfers{
{
Type: bchain.NonFungibleToken,
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])
}
}
})
}
}

File diff suppressed because one or more lines are too long

View File

@ -7,10 +7,10 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/protobuf/proto"
"github.com/juju/errors" "github.com/juju/errors"
"github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
"google.golang.org/protobuf/proto"
) )
// EthereumTypeAddressDescriptorLen - the AddressDescriptor of EthereumType has fixed length // EthereumTypeAddressDescriptorLen - the AddressDescriptor of EthereumType has fixed length
@ -25,15 +25,19 @@ const EtherAmountDecimalPoint = 18
// EthereumParser handle // EthereumParser handle
type EthereumParser struct { type EthereumParser struct {
*bchain.BaseParser *bchain.BaseParser
EnsSuffix string
} }
// NewEthereumParser returns new EthereumParser instance // NewEthereumParser returns new EthereumParser instance
func NewEthereumParser(b int, addressAliases bool) *EthereumParser { func NewEthereumParser(b int, addressAliases bool) *EthereumParser {
return &EthereumParser{&bchain.BaseParser{ return &EthereumParser{
BlockAddressesToKeep: b, BaseParser: &bchain.BaseParser{
AmountDecimalPoint: EtherAmountDecimalPoint, BlockAddressesToKeep: b,
AddressAliases: addressAliases, AmountDecimalPoint: EtherAmountDecimalPoint,
}} AddressAliases: addressAliases,
},
EnsSuffix: ".eth",
}
} }
type rpcHeader struct { type rpcHeader struct {
@ -331,6 +335,24 @@ func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) (
} }
pt.Receipt.Log = ptLogs pt.Receipt.Log = ptLogs
if r.Receipt.L1Fee != "" {
if pt.Receipt.L1Fee, err = hexDecodeBig(r.Receipt.L1Fee); err != nil {
return nil, errors.Annotatef(err, "L1Fee %v", r.Receipt.L1Fee)
}
}
if r.Receipt.L1FeeScalar != "" {
pt.Receipt.L1FeeScalar = []byte(r.Receipt.L1FeeScalar)
}
if r.Receipt.L1GasPrice != "" {
if pt.Receipt.L1GasPrice, err = hexDecodeBig(r.Receipt.L1GasPrice); err != nil {
return nil, errors.Annotatef(err, "L1GasPrice %v", r.Receipt.L1GasPrice)
}
}
if r.Receipt.L1GasUsed != "" {
if pt.Receipt.L1GasUsed, err = hexDecodeBig(r.Receipt.L1GasUsed); err != nil {
return nil, errors.Annotatef(err, "L1GasUsed %v", r.Receipt.L1GasUsed)
}
}
} }
return proto.Marshal(pt) return proto.Marshal(pt)
} }
@ -359,27 +381,37 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
} }
var rr *bchain.RpcReceipt var rr *bchain.RpcReceipt
if pt.Receipt != nil { if pt.Receipt != nil {
logs := make([]*bchain.RpcLog, len(pt.Receipt.Log)) rr = &bchain.RpcReceipt{
GasUsed: hexEncodeBig(pt.Receipt.GasUsed),
Status: "",
Logs: make([]*bchain.RpcLog, len(pt.Receipt.Log)),
}
for i, l := range pt.Receipt.Log { for i, l := range pt.Receipt.Log {
topics := make([]string, len(l.Topics)) topics := make([]string, len(l.Topics))
for j, t := range l.Topics { for j, t := range l.Topics {
topics[j] = hexutil.Encode(t) topics[j] = hexutil.Encode(t)
} }
logs[i] = &bchain.RpcLog{ rr.Logs[i] = &bchain.RpcLog{
Address: EIP55Address(l.Address), Address: EIP55Address(l.Address),
Data: hexutil.Encode(l.Data), Data: hexutil.Encode(l.Data),
Topics: topics, Topics: topics,
} }
} }
status := ""
// handle a special value []byte{'U'} as unknown state // handle a special value []byte{'U'} as unknown state
if len(pt.Receipt.Status) != 1 || pt.Receipt.Status[0] != 'U' { if len(pt.Receipt.Status) != 1 || pt.Receipt.Status[0] != 'U' {
status = hexEncodeBig(pt.Receipt.Status) rr.Status = hexEncodeBig(pt.Receipt.Status)
} }
rr = &bchain.RpcReceipt{ if len(pt.Receipt.L1Fee) > 0 {
GasUsed: hexEncodeBig(pt.Receipt.GasUsed), rr.L1Fee = hexEncodeBig(pt.Receipt.L1Fee)
Status: status, }
Logs: logs, if len(pt.Receipt.L1FeeScalar) > 0 {
rr.L1FeeScalar = string(pt.Receipt.L1FeeScalar)
}
if len(pt.Receipt.L1GasPrice) > 0 {
rr.L1GasPrice = hexEncodeBig(pt.Receipt.L1GasPrice)
}
if len(pt.Receipt.L1GasUsed) > 0 {
rr.L1GasUsed = hexEncodeBig(pt.Receipt.L1GasUsed)
} }
} }
// TODO handle internal transactions // TODO handle internal transactions
@ -461,7 +493,7 @@ func (p *EthereumParser) EthereumTypeGetTokenTransfersFromTx(tx *bchain.Tx) (bch
// FormatAddressAlias adds .eth to a name alias // FormatAddressAlias adds .eth to a name alias
func (p *EthereumParser) FormatAddressAlias(address string, name string) string { func (p *EthereumParser) FormatAddressAlias(address string, name string) string {
return name + ".eth" return name + p.EnsSuffix
} }
// TxStatus is status of transaction // TxStatus is status of transaction
@ -477,12 +509,16 @@ const (
// EthereumTxData contains ethereum specific transaction data // EthereumTxData contains ethereum specific transaction data
type EthereumTxData struct { type EthereumTxData struct {
Status TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown Status TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown
Nonce uint64 `json:"nonce"` Nonce uint64 `json:"nonce"`
GasLimit *big.Int `json:"gaslimit"` GasLimit *big.Int `json:"gaslimit"`
GasUsed *big.Int `json:"gasused"` GasUsed *big.Int `json:"gasused"`
GasPrice *big.Int `json:"gasprice"` GasPrice *big.Int `json:"gasprice"`
Data string `json:"data"` 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 // GetEthereumTxData returns EthereumTxData from bchain.Tx
@ -511,6 +547,10 @@ func GetEthereumTxDataFromSpecificData(coinSpecificData interface{}) *EthereumTx
etd.Status = TxStatusFailure etd.Status = TxStatusFailure
} }
etd.GasUsed, _ = hexutil.DecodeBig(csd.Receipt.GasUsed) etd.GasUsed, _ = hexutil.DecodeBig(csd.Receipt.GasUsed)
etd.L1Fee, _ = hexutil.DecodeBig(csd.Receipt.L1Fee)
etd.L1GasPrice, _ = hexutil.DecodeBig(csd.Receipt.L1GasPrice)
etd.L1GasUsed, _ = hexutil.DecodeBig(csd.Receipt.L1GasUsed)
etd.L1FeeScalar = csd.Receipt.L1FeeScalar
} }
} }
return &etd return &etd

View File

@ -1,496 +0,0 @@
//go:build unittest
package eth
import (
"encoding/hex"
"fmt"
"math/big"
"reflect"
"testing"
"github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/tests/dbtestdata"
)
func TestEthParser_GetAddrDescFromAddress(t *testing.T) {
type args struct {
address string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "with 0x prefix",
args: args{address: "0x81b7e08f65bdf5648606c89998a9cc8164397647"},
want: "81b7e08f65bdf5648606c89998a9cc8164397647",
},
{
name: "without 0x prefix",
args: args{address: "47526228d673e9f079630d6cdaff5a2ed13e0e60"},
want: "47526228d673e9f079630d6cdaff5a2ed13e0e60",
},
{
name: "address of wrong length",
args: args{address: "7526228d673e9f079630d6cdaff5a2ed13e0e60"},
want: "",
wantErr: true,
},
{
name: "ErrAddressMissing",
args: args{address: ""},
want: "",
wantErr: true,
},
{
name: "error - not eth address",
args: args{address: "1JKgN43B9SyLuZH19H5ECvr4KcfrbVHzZ6"},
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
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)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("EthParser.GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
var testTx1, testTx2, testTx1Failed, testTx1NoStatus bchain.Tx
func init() {
testTx1 = bchain.Tx{
Blocktime: 1534858022,
Time: 1534858022,
Txid: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{
{
Addresses: []string{"0x3E3a3D69dc66bA10737F531ed088954a9EC89d97"},
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(1999622000000000000),
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"},
},
},
},
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",
},
Receipt: &bchain.RpcReceipt{
GasUsed: "0x5208",
Status: "0x1",
Logs: []*bchain.RpcLog{},
},
},
}
testTx2 = bchain.Tx{
Blocktime: 1534858022,
Time: 1534858022,
Txid: "0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101",
Vin: []bchain.Vin{
{
Addresses: []string{"0x20cD153de35D469BA46127A0C8F18626b59a256A"},
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(0),
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x4af4114F73d1c1C903aC9E0361b379D1291808A2"},
},
},
},
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"},
Receipt: &bchain.RpcReceipt{
GasUsed: "0xcb39",
Status: "0x1",
Logs: []*bchain.RpcLog{
{
Address: "0x4af4114F73d1c1C903aC9E0361b379D1291808A2",
Data: "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000",
Topics: []string{
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a",
"0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f",
},
},
},
},
},
}
testTx1Failed = bchain.Tx{
Blocktime: 1534858022,
Time: 1534858022,
Txid: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{
{
Addresses: []string{"0x3E3a3D69dc66bA10737F531ed088954a9EC89d97"},
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(1999622000000000000),
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"},
},
},
},
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",
},
Receipt: &bchain.RpcReceipt{
GasUsed: "0x5208",
Status: "0x0",
Logs: []*bchain.RpcLog{},
},
},
}
testTx1NoStatus = bchain.Tx{
Blocktime: 1534858022,
Time: 1534858022,
Txid: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{
{
Addresses: []string{"0x3E3a3D69dc66bA10737F531ed088954a9EC89d97"},
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(1999622000000000000),
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"},
},
},
},
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",
},
Receipt: &bchain.RpcReceipt{
GasUsed: "0x5208",
Status: "",
Logs: []*bchain.RpcLog{},
},
},
}
}
func TestEthereumParser_PackTx(t *testing.T) {
type args struct {
tx *bchain.Tx
height uint32
blockTime int64
}
tests := []struct {
name string
p *EthereumParser
args args
want string
wantErr bool
}{
{
name: "1",
args: args{
tx: &testTx1,
height: 4321000,
blockTime: 1534858022,
},
want: dbtestdata.EthTx1Packed,
},
{
name: "2",
args: args{
tx: &testTx2,
height: 4321000,
blockTime: 1534858022,
},
want: dbtestdata.EthTx2Packed,
},
{
name: "3",
args: args{
tx: &testTx1Failed,
height: 4321000,
blockTime: 1534858022,
},
want: dbtestdata.EthTx1FailedPacked,
},
{
name: "4",
args: args{
tx: &testTx1NoStatus,
height: 4321000,
blockTime: 1534858022,
},
want: dbtestdata.EthTx1NoStatusPacked,
},
}
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)
if (err != nil) != tt.wantErr {
t.Errorf("EthereumParser.PackTx() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("EthereumParser.PackTx() = %v, want %v", h, tt.want)
}
})
}
}
func TestEthereumParser_UnpackTx(t *testing.T) {
type args struct {
hex string
}
tests := []struct {
name string
p *EthereumParser
args args
want *bchain.Tx
want1 uint32
wantErr bool
}{
{
name: "1",
args: args{hex: dbtestdata.EthTx1Packed},
want: &testTx1,
want1: 4321000,
},
{
name: "2",
args: args{hex: dbtestdata.EthTx2Packed},
want: &testTx2,
want1: 4321000,
},
{
name: "3",
args: args{hex: dbtestdata.EthTx1FailedPacked},
want: &testTx1Failed,
want1: 4321000,
},
{
name: "4",
args: args{hex: dbtestdata.EthTx1NoStatusPacked},
want: &testTx1NoStatus,
want1: 4321000,
},
}
p := NewEthereumParser(1, false)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, err := hex.DecodeString(tt.args.hex)
if err != nil {
panic(err)
}
got, got1, err := p.UnpackTx(b)
if (err != nil) != tt.wantErr {
t.Errorf("EthereumParser.UnpackTx() error = %v, wantErr %v", err, tt.wantErr)
return
}
// DeepEqual has problems with pointers in completeTransaction
gs := got.CoinSpecificData.(bchain.EthereumSpecificData)
ws := tt.want.CoinSpecificData.(bchain.EthereumSpecificData)
gc := *got
wc := *tt.want
gc.CoinSpecificData = nil
wc.CoinSpecificData = nil
if fmt.Sprint(gc) != fmt.Sprint(wc) {
// if !reflect.DeepEqual(gc, wc) {
t.Errorf("EthereumParser.UnpackTx() gc got = %+v, want %+v", gc, wc)
}
if !reflect.DeepEqual(gs.Tx, ws.Tx) {
t.Errorf("EthereumParser.UnpackTx() gs.Tx got = %+v, want %+v", gs.Tx, ws.Tx)
}
if !reflect.DeepEqual(gs.Receipt, ws.Receipt) {
t.Errorf("EthereumParser.UnpackTx() gs.Receipt got = %+v, want %+v", gs.Receipt, ws.Receipt)
}
if got1 != tt.want1 {
t.Errorf("EthereumParser.UnpackTx() got1 = %v, want %v", got1, tt.want1)
}
})
}
}
func TestEthereumParser_GetEthereumTxData(t *testing.T) {
tests := []struct {
name string
tx *bchain.Tx
want string
}{
{
name: "Test empty data",
tx: &testTx1,
want: "0x",
},
{
name: "Test non empty data",
tx: &testTx2,
want: "0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := GetEthereumTxData(tt.tx)
if got.Data != tt.want {
t.Errorf("EthereumParser.GetEthereumTxData() = %v, want %v", got.Data, tt.want)
}
})
}
}
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)
}
})
}
}

View File

@ -4,7 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"math/big" "math/big"
"net/http" "net/http"
"strconv" "strconv"
@ -30,18 +30,17 @@ type Network uint32
const ( const (
// MainNet is production network // MainNet is production network
MainNet Network = 1 MainNet Network = 1
// TestNet is Ropsten test network
TestNet Network = 3
// TestNetGoerli is Goerli test network
TestNetGoerli Network = 5
// TestNetSepolia is Sepolia test network // TestNetSepolia is Sepolia test network
TestNetSepolia Network = 11155111 TestNetSepolia Network = 11155111
// TestNetHolesky is Holesky test network
TestNetHolesky Network = 17000
) )
// Configuration represents json config file // Configuration represents json config file
type Configuration struct { type Configuration struct {
CoinName string `json:"coin_name"` CoinName string `json:"coin_name"`
CoinShortcut string `json:"coin_shortcut"` CoinShortcut string `json:"coin_shortcut"`
Network string `json:"network"`
RPCURL string `json:"rpc_url"` RPCURL string `json:"rpc_url"`
RPCTimeout int `json:"rpc_timeout"` RPCTimeout int `json:"rpc_timeout"`
BlockAddressesToKeep int `json:"block_addresses_to_keep"` BlockAddressesToKeep int `json:"block_addresses_to_keep"`
@ -51,28 +50,32 @@ type Configuration struct {
ProcessInternalTransactions bool `json:"processInternalTransactions"` ProcessInternalTransactions bool `json:"processInternalTransactions"`
ProcessZeroInternalTransactions bool `json:"processZeroInternalTransactions"` ProcessZeroInternalTransactions bool `json:"processZeroInternalTransactions"`
ConsensusNodeVersionURL string `json:"consensusNodeVersion"` ConsensusNodeVersionURL string `json:"consensusNodeVersion"`
DisableMempoolSync bool `json:"disableMempoolSync,omitempty"`
} }
// EthereumRPC is an interface to JSON-RPC eth service. // EthereumRPC is an interface to JSON-RPC eth service.
type EthereumRPC struct { type EthereumRPC struct {
*bchain.BaseChain *bchain.BaseChain
Client bchain.EVMClient Client bchain.EVMClient
RPC bchain.EVMRPCClient RPC bchain.EVMRPCClient
MainNetChainID Network MainNetChainID Network
Timeout time.Duration Timeout time.Duration
Parser *EthereumParser Parser *EthereumParser
PushHandler func(bchain.NotificationType) PushHandler func(bchain.NotificationType)
OpenRPC func(string) (bchain.EVMRPCClient, bchain.EVMClient, error) OpenRPC func(string) (bchain.EVMRPCClient, bchain.EVMClient, error)
Mempool *bchain.MempoolEthereumType Mempool *bchain.MempoolEthereumType
mempoolInitialized bool mempoolInitialized bool
bestHeaderLock sync.Mutex bestHeaderLock sync.Mutex
bestHeader bchain.EVMHeader bestHeader bchain.EVMHeader
bestHeaderTime time.Time bestHeaderTime time.Time
NewBlock bchain.EVMNewBlockSubscriber NewBlock bchain.EVMNewBlockSubscriber
newBlockSubscription bchain.EVMClientSubscription newBlockSubscription bchain.EVMClientSubscription
NewTx bchain.EVMNewTxSubscriber NewTx bchain.EVMNewTxSubscriber
newTxSubscription bchain.EVMClientSubscription newTxSubscription bchain.EVMClientSubscription
ChainConfig *Configuration ChainConfig *Configuration
supportedStakingPools []string
stakingPoolNames []string
stakingPoolContracts []string
} }
// ProcessInternalTransactions specifies if internal transactions are processed // ProcessInternalTransactions specifies if internal transactions are processed
@ -106,17 +109,22 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification
return s, nil return s, nil
} }
// OpenRPC opens RPC connection to ETH backend
var OpenRPC = func(url string) (bchain.EVMRPCClient, bchain.EVMClient, error) {
opts := []rpc.ClientOption{}
opts = append(opts, rpc.WithWebsocketMessageSizeLimit(0))
r, err := rpc.DialOptions(context.Background(), url, opts...)
if err != nil {
return nil, nil, err
}
rc := &EthereumRPCClient{Client: r}
ec := &EthereumClient{Client: ethclient.NewClient(r)}
return rc, ec, nil
}
// Initialize initializes ethereum rpc interface // Initialize initializes ethereum rpc interface
func (b *EthereumRPC) Initialize() error { func (b *EthereumRPC) Initialize() error {
b.OpenRPC = func(url string) (bchain.EVMRPCClient, bchain.EVMClient, error) { b.OpenRPC = OpenRPC
r, err := rpc.Dial(url)
if err != nil {
return nil, nil, err
}
rc := &EthereumRPCClient{Client: r}
ec := &EthereumClient{Client: ethclient.NewClient(r)}
return rc, ec, nil
}
rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL) rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL)
if err != nil { if err != nil {
@ -143,18 +151,21 @@ func (b *EthereumRPC) Initialize() error {
case MainNet: case MainNet:
b.Testnet = false b.Testnet = false
b.Network = "livenet" b.Network = "livenet"
case TestNet:
b.Testnet = true
b.Network = "testnet"
case TestNetGoerli:
b.Testnet = true
b.Network = "goerli"
case TestNetSepolia: case TestNetSepolia:
b.Testnet = true b.Testnet = true
b.Network = "sepolia" b.Network = "sepolia"
case TestNetHolesky:
b.Testnet = true
b.Network = "holesky"
default: default:
return errors.Errorf("Unknown network id %v", id) return errors.Errorf("Unknown network id %v", id)
} }
err = b.initStakingPools()
if err != nil {
return err
}
glog.Info("rpc: block chain ", b.Network) glog.Info("rpc: block chain ", b.Network)
return nil return nil
@ -164,7 +175,7 @@ func (b *EthereumRPC) Initialize() error {
func (b *EthereumRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) { func (b *EthereumRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) {
if b.Mempool == nil { if b.Mempool == nil {
b.Mempool = bchain.NewMempoolEthereumType(chain, b.ChainConfig.MempoolTxTimeoutHours, b.ChainConfig.QueryBackendOnMempoolResync) 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 return b.Mempool, nil
} }
@ -175,11 +186,19 @@ func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOu
return errors.New("Mempool not created") return errors.New("Mempool not created")
} }
var err error
var txs []string
// get initial mempool transactions // get initial mempool transactions
txs, err := b.GetMempoolTransactions() // workaround for an occasional `decoding block` error from getBlockRaw - try 3 times with a delay and then proceed
if err != nil { for i := 0; i < 3; i++ {
return err txs, err = b.GetMempoolTransactions()
if err == nil {
break
}
glog.Error("GetMempoolTransaction ", err)
time.Sleep(time.Second * 5)
} }
for _, txid := range txs { for _, txid := range txs {
b.Mempool.AddTransactionToMempool(txid) b.Mempool.AddTransactionToMempool(txid)
} }
@ -238,26 +257,30 @@ func (b *EthereumRPC) subscribeEvents() error {
if glog.V(2) { if glog.V(2) {
glog.Info("rpc: new tx ", hex) glog.Info("rpc: new tx ", hex)
} }
b.Mempool.AddTransactionToMempool(hex) added := b.Mempool.AddTransactionToMempool(hex)
b.PushHandler(bchain.NotificationNewTx) if added {
b.PushHandler(bchain.NotificationNewTx)
}
} }
}() }()
// new mempool transaction subscription if !b.ChainConfig.DisableMempoolSync {
if err := b.subscribe(func() (bchain.EVMClientSubscription, error) { // new mempool transaction subscription
// invalidate the previous subscription - it is either the first one or there was an error if err := b.subscribe(func() (bchain.EVMClientSubscription, error) {
b.newTxSubscription = nil // invalidate the previous subscription - it is either the first one or there was an error
ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) b.newTxSubscription = nil
defer cancel() ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
sub, err := b.RPC.EthSubscribe(ctx, b.NewTx.Channel(), "newPendingTransactions") defer cancel()
if err != nil { sub, err := b.RPC.EthSubscribe(ctx, b.NewTx.Channel(), "newPendingTransactions")
return nil, errors.Annotatef(err, "EthSubscribe 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 return nil
@ -358,7 +381,7 @@ func (b *EthereumRPC) getConsensusVersion() string {
glog.Error("getConsensusVersion ", err) glog.Error("getConsensusVersion ", err)
return "" return ""
} }
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
glog.Error("getConsensusVersion ", err) glog.Error("getConsensusVersion ", err)
return "" return ""
@ -591,19 +614,24 @@ type rpcTraceResult struct {
} }
func (b *EthereumRPC) getCreationContractInfo(contract string, height uint32) *bchain.ContractInfo { func (b *EthereumRPC) getCreationContractInfo(contract string, height uint32) *bchain.ContractInfo {
ci, err := b.fetchContractInfo(contract) // do not fetch fetchContractInfo in sync, it slows it down
if ci == nil || err != nil { // the contract will be fetched only when asked by a client
ci = &bchain.ContractInfo{ // ci, err := b.fetchContractInfo(contract)
Contract: contract, // if ci == nil || err != nil {
} ci := &bchain.ContractInfo{
Contract: contract,
} }
ci.Type = bchain.UnknownTokenType // }
ci.Type = bchain.UnhandledTokenType
ci.CreatedInBlock = height ci.CreatedInBlock = height
return ci return ci
} }
func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInternalData, contracts []bchain.ContractInfo, blockHeight uint32) []bchain.ContractInfo { func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInternalData, contracts []bchain.ContractInfo, blockHeight uint32) []bchain.ContractInfo {
value, err := hexutil.DecodeBig(call.Value) value, err := hexutil.DecodeBig(call.Value)
if err != nil {
value = new(big.Int)
}
if call.Type == "CREATE" || call.Type == "CREATE2" { if call.Type == "CREATE" || call.Type == "CREATE2" {
d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{
Type: bchain.CREATE, Type: bchain.CREATE,
@ -653,8 +681,28 @@ func (b *EthereumRPC) getInternalDataForBlock(blockHash string, blockHeight uint
return data, contracts, err return data, contracts, err
} }
if len(trace) != len(data) { if len(trace) != len(data) {
glog.Error("debug_traceBlockByHash block ", blockHash, ", error: trace length does not match block length ", len(trace), "!=", len(data)) if len(trace) < len(data) {
return data, contracts, err for i := range transactions {
tx := &transactions[i]
// bridging transactions in Polygon do not create trace and cause mismatch between the trace size and block size, it is necessary to adjust the trace size
// bridging transaction that from and to zero address
if tx.To == "0x0000000000000000000000000000000000000000" && tx.From == "0x0000000000000000000000000000000000000000" {
if i >= len(trace) {
trace = append(trace, rpcTraceResult{})
} else {
trace = append(trace[:i+1], trace[i:]...)
trace[i] = rpcTraceResult{}
}
}
}
}
if len(trace) != len(data) {
e := fmt.Sprint("trace length does not match block length ", len(trace), "!=", len(data))
glog.Error("debug_traceBlockByHash block ", blockHash, ", error: ", e)
return data, contracts, errors.New(e)
} else {
glog.Warning("debug_traceBlockByHash block ", blockHash, ", trace adjusted to match the number of transactions in block")
}
} }
for i, result := range trace { for i, result := range trace {
r := &result.Result r := &result.Result
@ -795,12 +843,13 @@ func (b *EthereumRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error)
func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
defer cancel() defer cancel()
var tx *bchain.RpcTransaction tx := &bchain.RpcTransaction{}
hash := ethcommon.HexToHash(txid) hash := ethcommon.HexToHash(txid)
err := b.RPC.CallContext(ctx, &tx, "eth_getTransactionByHash", hash) err := b.RPC.CallContext(ctx, tx, "eth_getTransactionByHash", hash)
if err != nil { if err != nil {
return nil, err return nil, err
} else if tx == nil { }
if *tx == (bchain.RpcTransaction{}) {
if b.mempoolInitialized { if b.mempoolInitialized {
b.Mempool.RemoveTransactionFromMempool(txid) b.Mempool.RemoveTransactionFromMempool(txid)
} }
@ -943,21 +992,31 @@ func (b *EthereumRPC) EthereumTypeEstimateGas(params map[string]interface{}) (ui
// SendRawTransaction sends raw transaction // SendRawTransaction sends raw transaction
func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) { 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) ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
defer cancel() defer cancel()
var raw json.RawMessage var raw json.RawMessage
err := b.RPC.CallContext(ctx, &raw, "eth_sendRawTransaction", hex) err := b.RPC.CallContext(ctx, &raw, rpcMethod, args...)
if err != nil { if err != nil {
return "", err return "", err
} else if len(raw) == 0 { } else if len(raw) == 0 {
return "", errors.New("SendRawTransaction: failed") return "", errors.New(rpcMethod + " : failed")
} }
var result string var result string
if err := json.Unmarshal(raw, &result); err != nil { if err := json.Unmarshal(raw, &result); err != nil {
return "", errors.Annotatef(err, "raw result %v", raw) return "", errors.Annotatef(err, "raw result %v", raw)
} }
if result == "" { if result == "" {
return "", errors.New("SendRawTransaction: failed, empty result") return "", errors.New(rpcMethod + " : failed, empty result")
} }
return result, nil return result, nil
} }

View File

@ -1,261 +1,530 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.12
// source: bchain/coins/eth/ethtx.proto // source: bchain/coins/eth/ethtx.proto
/*
Package eth is a generated protocol buffer package.
It is generated from these files:
bchain/coins/eth/ethtx.proto
It has these top-level messages:
ProtoCompleteTransaction
*/
package eth package eth
import proto "github.com/golang/protobuf/proto" import (
import fmt "fmt" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
import math "math" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
// Reference imports to suppress errors if they are not otherwise used. const (
var _ = proto.Marshal // Verify that this generated code is sufficiently up-to-date.
var _ = fmt.Errorf _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
var _ = math.Inf // Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
// 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
type ProtoCompleteTransaction struct { type ProtoCompleteTransaction struct {
BlockNumber uint32 `protobuf:"varint,1,opt,name=BlockNumber" json:"BlockNumber,omitempty"` state protoimpl.MessageState
BlockTime uint64 `protobuf:"varint,2,opt,name=BlockTime" json:"BlockTime,omitempty"` sizeCache protoimpl.SizeCache
Tx *ProtoCompleteTransaction_TxType `protobuf:"bytes,3,opt,name=Tx" json:"Tx,omitempty"` unknownFields protoimpl.UnknownFields
Receipt *ProtoCompleteTransaction_ReceiptType `protobuf:"bytes,4,opt,name=Receipt" json:"Receipt,omitempty"`
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"`
} }
func (m *ProtoCompleteTransaction) Reset() { *m = ProtoCompleteTransaction{} } func (x *ProtoCompleteTransaction) Reset() {
func (m *ProtoCompleteTransaction) String() string { return proto.CompactTextString(m) } *x = ProtoCompleteTransaction{}
func (*ProtoCompleteTransaction) ProtoMessage() {} if protoimpl.UnsafeEnabled {
func (*ProtoCompleteTransaction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (m *ProtoCompleteTransaction) GetBlockNumber() uint32 { func (x *ProtoCompleteTransaction) String() string {
if m != nil { return protoimpl.X.MessageStringOf(x)
return m.BlockNumber }
func (*ProtoCompleteTransaction) ProtoMessage() {}
func (x *ProtoCompleteTransaction) ProtoReflect() protoreflect.Message {
mi := &file_bchain_coins_eth_ethtx_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 ProtoCompleteTransaction.ProtoReflect.Descriptor instead.
func (*ProtoCompleteTransaction) Descriptor() ([]byte, []int) {
return file_bchain_coins_eth_ethtx_proto_rawDescGZIP(), []int{0}
}
func (x *ProtoCompleteTransaction) GetBlockNumber() uint32 {
if x != nil {
return x.BlockNumber
} }
return 0 return 0
} }
func (m *ProtoCompleteTransaction) GetBlockTime() uint64 { func (x *ProtoCompleteTransaction) GetBlockTime() uint64 {
if m != nil { if x != nil {
return m.BlockTime return x.BlockTime
} }
return 0 return 0
} }
func (m *ProtoCompleteTransaction) GetTx() *ProtoCompleteTransaction_TxType { func (x *ProtoCompleteTransaction) GetTx() *ProtoCompleteTransaction_TxType {
if m != nil { if x != nil {
return m.Tx return x.Tx
} }
return nil return nil
} }
func (m *ProtoCompleteTransaction) GetReceipt() *ProtoCompleteTransaction_ReceiptType { func (x *ProtoCompleteTransaction) GetReceipt() *ProtoCompleteTransaction_ReceiptType {
if m != nil { if x != nil {
return m.Receipt return x.Receipt
} }
return nil return nil
} }
type ProtoCompleteTransaction_TxType struct { type ProtoCompleteTransaction_TxType struct {
AccountNonce uint64 `protobuf:"varint,1,opt,name=AccountNonce" json:"AccountNonce,omitempty"` 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"` GasPrice []byte `protobuf:"bytes,2,opt,name=GasPrice,proto3" json:"GasPrice,omitempty"`
GasLimit uint64 `protobuf:"varint,3,opt,name=GasLimit" json:"GasLimit,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"` Value []byte `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"`
Payload []byte `protobuf:"bytes,5,opt,name=Payload,proto3" json:"Payload,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"` Hash []byte `protobuf:"bytes,6,opt,name=Hash,proto3" json:"Hash,omitempty"`
To []byte `protobuf:"bytes,7,opt,name=To,proto3" json:"To,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"` From []byte `protobuf:"bytes,8,opt,name=From,proto3" json:"From,omitempty"`
TransactionIndex uint32 `protobuf:"varint,9,opt,name=TransactionIndex" json:"TransactionIndex,omitempty"` TransactionIndex uint32 `protobuf:"varint,9,opt,name=TransactionIndex,proto3" json:"TransactionIndex,omitempty"`
} }
func (m *ProtoCompleteTransaction_TxType) Reset() { *m = ProtoCompleteTransaction_TxType{} } func (x *ProtoCompleteTransaction_TxType) Reset() {
func (m *ProtoCompleteTransaction_TxType) String() string { return proto.CompactTextString(m) } *x = ProtoCompleteTransaction_TxType{}
func (*ProtoCompleteTransaction_TxType) ProtoMessage() {} if protoimpl.UnsafeEnabled {
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 {
return protoimpl.X.MessageStringOf(x)
}
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 {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ProtoCompleteTransaction_TxType.ProtoReflect.Descriptor instead.
func (*ProtoCompleteTransaction_TxType) Descriptor() ([]byte, []int) { func (*ProtoCompleteTransaction_TxType) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{0, 0} return file_bchain_coins_eth_ethtx_proto_rawDescGZIP(), []int{0, 0}
} }
func (m *ProtoCompleteTransaction_TxType) GetAccountNonce() uint64 { func (x *ProtoCompleteTransaction_TxType) GetAccountNonce() uint64 {
if m != nil { if x != nil {
return m.AccountNonce return x.AccountNonce
} }
return 0 return 0
} }
func (m *ProtoCompleteTransaction_TxType) GetGasPrice() []byte { func (x *ProtoCompleteTransaction_TxType) GetGasPrice() []byte {
if m != nil { if x != nil {
return m.GasPrice return x.GasPrice
} }
return nil return nil
} }
func (m *ProtoCompleteTransaction_TxType) GetGasLimit() uint64 { func (x *ProtoCompleteTransaction_TxType) GetGasLimit() uint64 {
if m != nil { if x != nil {
return m.GasLimit return x.GasLimit
} }
return 0 return 0
} }
func (m *ProtoCompleteTransaction_TxType) GetValue() []byte { func (x *ProtoCompleteTransaction_TxType) GetValue() []byte {
if m != nil { if x != nil {
return m.Value return x.Value
} }
return nil return nil
} }
func (m *ProtoCompleteTransaction_TxType) GetPayload() []byte { func (x *ProtoCompleteTransaction_TxType) GetPayload() []byte {
if m != nil { if x != nil {
return m.Payload return x.Payload
} }
return nil return nil
} }
func (m *ProtoCompleteTransaction_TxType) GetHash() []byte { func (x *ProtoCompleteTransaction_TxType) GetHash() []byte {
if m != nil { if x != nil {
return m.Hash return x.Hash
} }
return nil return nil
} }
func (m *ProtoCompleteTransaction_TxType) GetTo() []byte { func (x *ProtoCompleteTransaction_TxType) GetTo() []byte {
if m != nil { if x != nil {
return m.To return x.To
} }
return nil return nil
} }
func (m *ProtoCompleteTransaction_TxType) GetFrom() []byte { func (x *ProtoCompleteTransaction_TxType) GetFrom() []byte {
if m != nil { if x != nil {
return m.From return x.From
} }
return nil return nil
} }
func (m *ProtoCompleteTransaction_TxType) GetTransactionIndex() uint32 { func (x *ProtoCompleteTransaction_TxType) GetTransactionIndex() uint32 {
if m != nil { if x != nil {
return m.TransactionIndex return x.TransactionIndex
} }
return 0 return 0
} }
type ProtoCompleteTransaction_ReceiptType struct { type ProtoCompleteTransaction_ReceiptType struct {
GasUsed []byte `protobuf:"bytes,1,opt,name=GasUsed,proto3" json:"GasUsed,omitempty"` state protoimpl.MessageState
Status []byte `protobuf:"bytes,2,opt,name=Status,proto3" json:"Status,omitempty"` sizeCache protoimpl.SizeCache
Log []*ProtoCompleteTransaction_ReceiptType_LogType `protobuf:"bytes,3,rep,name=Log" json:"Log,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"`
} }
func (m *ProtoCompleteTransaction_ReceiptType) Reset() { *m = ProtoCompleteTransaction_ReceiptType{} } func (x *ProtoCompleteTransaction_ReceiptType) Reset() {
func (m *ProtoCompleteTransaction_ReceiptType) String() string { return proto.CompactTextString(m) } *x = ProtoCompleteTransaction_ReceiptType{}
func (*ProtoCompleteTransaction_ReceiptType) ProtoMessage() {} if protoimpl.UnsafeEnabled {
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 {
return protoimpl.X.MessageStringOf(x)
}
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 {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ProtoCompleteTransaction_ReceiptType.ProtoReflect.Descriptor instead.
func (*ProtoCompleteTransaction_ReceiptType) Descriptor() ([]byte, []int) { func (*ProtoCompleteTransaction_ReceiptType) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{0, 1} return file_bchain_coins_eth_ethtx_proto_rawDescGZIP(), []int{0, 1}
} }
func (m *ProtoCompleteTransaction_ReceiptType) GetGasUsed() []byte { func (x *ProtoCompleteTransaction_ReceiptType) GetGasUsed() []byte {
if m != nil { if x != nil {
return m.GasUsed return x.GasUsed
} }
return nil return nil
} }
func (m *ProtoCompleteTransaction_ReceiptType) GetStatus() []byte { func (x *ProtoCompleteTransaction_ReceiptType) GetStatus() []byte {
if m != nil { if x != nil {
return m.Status return x.Status
} }
return nil return nil
} }
func (m *ProtoCompleteTransaction_ReceiptType) GetLog() []*ProtoCompleteTransaction_ReceiptType_LogType { func (x *ProtoCompleteTransaction_ReceiptType) GetLog() []*ProtoCompleteTransaction_ReceiptType_LogType {
if m != nil { if x != nil {
return m.Log return x.Log
}
return nil
}
func (x *ProtoCompleteTransaction_ReceiptType) GetL1Fee() []byte {
if x != nil {
return x.L1Fee
}
return nil
}
func (x *ProtoCompleteTransaction_ReceiptType) GetL1FeeScalar() []byte {
if x != nil {
return x.L1FeeScalar
}
return nil
}
func (x *ProtoCompleteTransaction_ReceiptType) GetL1GasPrice() []byte {
if x != nil {
return x.L1GasPrice
}
return nil
}
func (x *ProtoCompleteTransaction_ReceiptType) GetL1GasUsed() []byte {
if x != nil {
return x.L1GasUsed
} }
return nil return nil
} }
type ProtoCompleteTransaction_ReceiptType_LogType struct { type ProtoCompleteTransaction_ReceiptType_LogType struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Address []byte `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"` Address []byte `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=Data,proto3" json:"Data,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"` Topics [][]byte `protobuf:"bytes,3,rep,name=Topics,proto3" json:"Topics,omitempty"`
} }
func (m *ProtoCompleteTransaction_ReceiptType_LogType) Reset() { func (x *ProtoCompleteTransaction_ReceiptType_LogType) Reset() {
*m = ProtoCompleteTransaction_ReceiptType_LogType{} *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)
}
} }
func (m *ProtoCompleteTransaction_ReceiptType_LogType) String() string {
return proto.CompactTextString(m) func (x *ProtoCompleteTransaction_ReceiptType_LogType) String() string {
return protoimpl.X.MessageStringOf(x)
} }
func (*ProtoCompleteTransaction_ReceiptType_LogType) ProtoMessage() {} 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 {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ProtoCompleteTransaction_ReceiptType_LogType.ProtoReflect.Descriptor instead.
func (*ProtoCompleteTransaction_ReceiptType_LogType) Descriptor() ([]byte, []int) { func (*ProtoCompleteTransaction_ReceiptType_LogType) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{0, 1, 0} return file_bchain_coins_eth_ethtx_proto_rawDescGZIP(), []int{0, 1, 0}
} }
func (m *ProtoCompleteTransaction_ReceiptType_LogType) GetAddress() []byte { func (x *ProtoCompleteTransaction_ReceiptType_LogType) GetAddress() []byte {
if m != nil { if x != nil {
return m.Address return x.Address
} }
return nil return nil
} }
func (m *ProtoCompleteTransaction_ReceiptType_LogType) GetData() []byte { func (x *ProtoCompleteTransaction_ReceiptType_LogType) GetData() []byte {
if m != nil { if x != nil {
return m.Data return x.Data
} }
return nil return nil
} }
func (m *ProtoCompleteTransaction_ReceiptType_LogType) GetTopics() [][]byte { func (x *ProtoCompleteTransaction_ReceiptType_LogType) GetTopics() [][]byte {
if m != nil { if x != nil {
return m.Topics return x.Topics
} }
return nil return nil
} }
func init() { var File_bchain_coins_eth_ethtx_proto protoreflect.FileDescriptor
proto.RegisterType((*ProtoCompleteTransaction)(nil), "eth.ProtoCompleteTransaction")
proto.RegisterType((*ProtoCompleteTransaction_TxType)(nil), "eth.ProtoCompleteTransaction.TxType") var file_bchain_coins_eth_ethtx_proto_rawDesc = []byte{
proto.RegisterType((*ProtoCompleteTransaction_ReceiptType)(nil), "eth.ProtoCompleteTransaction.ReceiptType") 0x0a, 0x1c, 0x62, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x2f, 0x65,
proto.RegisterType((*ProtoCompleteTransaction_ReceiptType_LogType)(nil), "eth.ProtoCompleteTransaction.ReceiptType.LogType") 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,
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,
0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,
0x52, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x02, 0x54,
0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 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, 0x54, 0x78, 0x54, 0x79, 0x70, 0x65, 0x52, 0x02, 0x54, 0x78, 0x12, 0x3f, 0x0a,
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,
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,
0x08, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x47, 0x61, 0x73,
0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x47, 0x61, 0x73,
0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x50,
0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x50, 0x61,
0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x61, 0x73, 0x68, 0x18, 0x06, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x0e, 0x0a, 0x02, 0x54, 0x6f, 0x18,
0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x54, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x72, 0x6f,
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,
} }
func init() { proto.RegisterFile("bchain/coins/eth/ethtx.proto", fileDescriptor0) } var (
file_bchain_coins_eth_ethtx_proto_rawDescOnce sync.Once
file_bchain_coins_eth_ethtx_proto_rawDescData = file_bchain_coins_eth_ethtx_proto_rawDesc
)
var fileDescriptor0 = []byte{ func file_bchain_coins_eth_ethtx_proto_rawDescGZIP() []byte {
// 409 bytes of a gzipped FileDescriptorProto file_bchain_coins_eth_ethtx_proto_rawDescOnce.Do(func() {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xdf, 0x8a, 0xd4, 0x30, file_bchain_coins_eth_ethtx_proto_rawDescData = protoimpl.X.CompressGZIP(file_bchain_coins_eth_ethtx_proto_rawDescData)
0x18, 0xc5, 0xe9, 0x9f, 0x99, 0xd9, 0xfd, 0xa6, 0x8a, 0x04, 0x91, 0x30, 0xec, 0x45, 0x59, 0xbc, })
0x18, 0xbd, 0xe8, 0xe2, 0xea, 0x0b, 0xac, 0x23, 0xae, 0xc2, 0xb0, 0x0e, 0x31, 0x7a, 0x9f, 0x49, return file_bchain_coins_eth_ethtx_proto_rawDescData
0xc3, 0x36, 0x38, 0x6d, 0x4a, 0x93, 0x42, 0xf7, 0x8d, 0x7c, 0x21, 0xdf, 0xc5, 0x4b, 0xc9, 0xd7, }
0x74, 0x1d, 0x11, 0x65, 0x2f, 0x0a, 0xf9, 0x9d, 0x7e, 0xa7, 0x39, 0x27, 0x29, 0x9c, 0xed, 0x65,
0x25, 0x74, 0x73, 0x21, 0x8d, 0x6e, 0xec, 0x85, 0x72, 0x95, 0x7f, 0xdc, 0x50, 0xb4, 0x9d, 0x71, var file_bchain_coins_eth_ethtx_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
0x86, 0x24, 0xca, 0x55, 0xe7, 0xdf, 0x67, 0x40, 0x77, 0x1e, 0x37, 0xa6, 0x6e, 0x0f, 0xca, 0x29, var file_bchain_coins_eth_ethtx_proto_goTypes = []interface{}{
0xde, 0x89, 0xc6, 0x0a, 0xe9, 0xb4, 0x69, 0x48, 0x0e, 0xcb, 0xb7, 0x07, 0x23, 0xbf, 0xdd, 0xf4, (*ProtoCompleteTransaction)(nil), // 0: ProtoCompleteTransaction
0xf5, 0x5e, 0x75, 0x34, 0xca, 0xa3, 0xf5, 0x23, 0x76, 0x2c, 0x91, 0x33, 0x38, 0x45, 0xe4, 0xba, (*ProtoCompleteTransaction_TxType)(nil), // 1: ProtoCompleteTransaction.TxType
0x56, 0x34, 0xce, 0xa3, 0x75, 0xca, 0x7e, 0x0b, 0xe4, 0x0d, 0xc4, 0x7c, 0xa0, 0x49, 0x1e, 0xad, (*ProtoCompleteTransaction_ReceiptType)(nil), // 2: ProtoCompleteTransaction.ReceiptType
0x97, 0x97, 0xcf, 0x0b, 0xe5, 0xaa, 0xe2, 0x5f, 0x5b, 0x15, 0x7c, 0xe0, 0x77, 0xad, 0x62, 0x31, (*ProtoCompleteTransaction_ReceiptType_LogType)(nil), // 3: ProtoCompleteTransaction.ReceiptType.LogType
0x1f, 0xc8, 0x06, 0x16, 0x4c, 0x49, 0xa5, 0x5b, 0x47, 0x53, 0xb4, 0xbe, 0xf8, 0xbf, 0x35, 0x0c, }
0xa3, 0x7f, 0x72, 0xae, 0x7e, 0x46, 0x30, 0x1f, 0xbf, 0x49, 0xce, 0x21, 0xbb, 0x92, 0xd2, 0xf4, var file_bchain_coins_eth_ethtx_proto_depIdxs = []int32{
0x8d, 0xbb, 0x31, 0x8d, 0x54, 0x58, 0x23, 0x65, 0x7f, 0x68, 0x64, 0x05, 0x27, 0xd7, 0xc2, 0xee, 1, // 0: ProtoCompleteTransaction.Tx:type_name -> ProtoCompleteTransaction.TxType
0x3a, 0x2d, 0xc7, 0x1a, 0x19, 0xbb, 0xe7, 0xf0, 0x6e, 0xab, 0x6b, 0xed, 0xb0, 0x4b, 0xca, 0xee, 2, // 1: ProtoCompleteTransaction.Receipt:type_name -> ProtoCompleteTransaction.ReceiptType
0x99, 0x3c, 0x85, 0xd9, 0x57, 0x71, 0xe8, 0x15, 0x26, 0xcd, 0xd8, 0x08, 0x84, 0xc2, 0x62, 0x27, 3, // 2: ProtoCompleteTransaction.ReceiptType.Log:type_name -> ProtoCompleteTransaction.ReceiptType.LogType
0xee, 0x0e, 0x46, 0x94, 0x74, 0x86, 0xfa, 0x84, 0x84, 0x40, 0xfa, 0x41, 0xd8, 0x8a, 0xce, 0x51, 3, // [3:3] is the sub-list for method output_type
0xc6, 0x35, 0x79, 0x0c, 0x31, 0x37, 0x74, 0x81, 0x4a, 0xcc, 0x8d, 0x9f, 0x79, 0xdf, 0x99, 0x9a, 3, // [3:3] is the sub-list for method input_type
0x9e, 0x8c, 0x33, 0x7e, 0x4d, 0x5e, 0xc2, 0x93, 0xa3, 0xca, 0x1f, 0x9b, 0x52, 0x0d, 0xf4, 0x14, 3, // [3:3] is the sub-list for extension type_name
0xaf, 0xe3, 0x2f, 0x7d, 0xf5, 0x23, 0x82, 0xe5, 0xd1, 0x99, 0xf8, 0x34, 0xd7, 0xc2, 0x7e, 0xb1, 3, // [3:3] is the sub-list for extension extendee
0xaa, 0xc4, 0xea, 0x19, 0x9b, 0x90, 0x3c, 0x83, 0xf9, 0x67, 0x27, 0x5c, 0x6f, 0x43, 0xe7, 0x40, 0, // [0:3] is the sub-list for field type_name
0x64, 0x03, 0xc9, 0xd6, 0xdc, 0xd2, 0x24, 0x4f, 0xd6, 0xcb, 0xcb, 0x57, 0x0f, 0x3e, 0xfd, 0x62, }
0x6b, 0x6e, 0xf1, 0x16, 0xbc, 0x7b, 0xf5, 0x09, 0x16, 0x81, 0x7d, 0x82, 0xab, 0xb2, 0xec, 0x94,
0xb5, 0x53, 0x82, 0x80, 0xbe, 0xeb, 0x3b, 0xe1, 0x44, 0xd8, 0x1f, 0xd7, 0x3e, 0x15, 0x37, 0xad, func init() { file_bchain_coins_eth_ethtx_proto_init() }
0x96, 0x16, 0x03, 0x64, 0x2c, 0xd0, 0x7e, 0x8e, 0xbf, 0xed, 0xeb, 0x5f, 0x01, 0x00, 0x00, 0xff, func file_bchain_coins_eth_ethtx_proto_init() {
0xff, 0xc2, 0x69, 0x8d, 0xdf, 0xd6, 0x02, 0x00, 0x00, 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{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_bchain_coins_eth_ethtx_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_bchain_coins_eth_ethtx_proto_goTypes,
DependencyIndexes: file_bchain_coins_eth_ethtx_proto_depIdxs,
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
} }

View File

@ -1,30 +1,34 @@
syntax = "proto3"; syntax = "proto3";
package eth; option go_package = "bchain/coins/eth";
message ProtoCompleteTransaction { message ProtoCompleteTransaction {
message TxType { message TxType {
uint64 AccountNonce = 1; uint64 AccountNonce = 1;
bytes GasPrice = 2; bytes GasPrice = 2;
uint64 GasLimit = 3; uint64 GasLimit = 3;
bytes Value = 4; bytes Value = 4;
bytes Payload = 5; bytes Payload = 5;
bytes Hash = 6; bytes Hash = 6;
bytes To = 7; bytes To = 7;
bytes From = 8; bytes From = 8;
uint32 TransactionIndex = 9; uint32 TransactionIndex = 9;
} }
message ReceiptType { message ReceiptType {
message LogType { message LogType {
bytes Address = 1; bytes Address = 1;
bytes Data = 2; bytes Data = 2;
repeated bytes Topics = 3; repeated bytes Topics = 3;
} }
bytes GasUsed = 1; bytes GasUsed = 1;
bytes Status = 2; bytes Status = 2;
repeated LogType Log = 3; repeated LogType Log = 3;
} optional bytes L1Fee = 4;
uint32 BlockNumber = 1; optional bytes L1FeeScalar = 5;
uint64 BlockTime = 2; optional bytes L1GasPrice = 6;
TxType Tx = 3; optional bytes L1GasUsed = 7;
ReceiptType Receipt = 4; }
} uint32 BlockNumber = 1;
uint64 BlockTime = 2;
TxType Tx = 3;
ReceiptType Receipt = 4;
}

View File

@ -0,0 +1,146 @@
package eth
import (
"math/big"
"os"
"strings"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/glog"
"github.com/juju/errors"
"github.com/trezor/blockbook/bchain"
)
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)
if envValue != "" {
parts := strings.Split(envValue, "/")
if len(parts) != 2 {
glog.Errorf("Wrong format of environment variable %s=%s, expecting value '<pool name>/<pool contract>', staking pools not enabled", envVar, envValue)
return nil
}
b.supportedStakingPools = []string{envValue}
b.stakingPoolNames = []string{parts[0]}
b.stakingPoolContracts = []string{parts[1]}
glog.Info("Support of staking pools enabled with these pools: ", b.supportedStakingPools)
}
return nil
}
func (b *EthereumRPC) EthereumTypeGetSupportedStakingPools() []string {
return b.supportedStakingPools
}
func (b *EthereumRPC) EthereumTypeGetStakingPoolsData(addrDesc bchain.AddressDescriptor) ([]bchain.StakingPoolData, error) {
// for now only single staking pool - Everstake
addr := hexutil.Encode(addrDesc)[2:]
if len(b.supportedStakingPools) == 1 {
data, err := b.everstakePoolData(addr, b.stakingPoolContracts[0], b.stakingPoolNames[0])
if err != nil {
return nil, err
}
if data != nil {
return []bchain.StakingPoolData{*data}, nil
}
}
return nil, nil
}
const everstakePendingBalanceOfMethodSignature = "0x59b8c763" // pendingBalanceOf(address)
const everstakePendingDepositedBalanceOfMethodSignature = "0x80f14ecc" // pendingDepositedBalanceOf(address)
const everstakeDepositedBalanceOfMethodSignature = "0x68b48254" // depositedBalanceOf(address)
const everstakeWithdrawRequestMethodSignature = "0x14cbc46a" // withdrawRequest(address)
const everstakeRestakedRewardOfMethodSignature = "0x0c98929a" // restakedRewardOf(address)
const everstakeAutocompoundBalanceOfMethodSignature = "0x2fec7966" // autocompoundBalanceOf(address)
func isZeroBigInt(b *big.Int) bool {
return len(b.Bits()) == 0
}
func (b *EthereumRPC) everstakeBalanceTypeContractCall(signature, addr, contract string) (string, error) {
req := signature + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr):] + addr
return b.EthereumTypeRpcCall(req, contract, "")
}
func (b *EthereumRPC) everstakeContractCallSimpleNumeric(signature, addr, contract string) (*big.Int, error) {
data, err := b.everstakeBalanceTypeContractCall(signature, addr, contract)
if err != nil {
return nil, err
}
r := parseSimpleNumericProperty(data)
if r == nil {
return nil, errors.New("Invalid balance")
}
return r, nil
}
func (b *EthereumRPC) everstakePoolData(addr, contract, name string) (*bchain.StakingPoolData, error) {
poolData := bchain.StakingPoolData{
Contract: contract,
Name: name,
}
allZeros := true
value, err := b.everstakeContractCallSimpleNumeric(everstakePendingBalanceOfMethodSignature, addr, contract)
if err != nil {
return nil, err
}
poolData.PendingBalance = *value
allZeros = allZeros && isZeroBigInt(value)
value, err = b.everstakeContractCallSimpleNumeric(everstakePendingDepositedBalanceOfMethodSignature, addr, contract)
if err != nil {
return nil, err
}
poolData.PendingDepositedBalance = *value
allZeros = allZeros && isZeroBigInt(value)
value, err = b.everstakeContractCallSimpleNumeric(everstakeDepositedBalanceOfMethodSignature, addr, contract)
if err != nil {
return nil, err
}
poolData.DepositedBalance = *value
allZeros = allZeros && isZeroBigInt(value)
data, err := b.everstakeBalanceTypeContractCall(everstakeWithdrawRequestMethodSignature, addr, contract)
if err != nil {
return nil, err
}
value = parseSimpleNumericProperty(data)
if value == nil {
return nil, errors.New("Invalid balance")
}
poolData.WithdrawTotalAmount = *value
allZeros = allZeros && isZeroBigInt(value)
value = parseSimpleNumericProperty(data[64+2:])
if value == nil {
return nil, errors.New("Invalid balance")
}
poolData.ClaimableAmount = *value
allZeros = allZeros && isZeroBigInt(value)
value, err = b.everstakeContractCallSimpleNumeric(everstakeRestakedRewardOfMethodSignature, addr, contract)
if err != nil {
return nil, err
}
poolData.RestakedReward = *value
allZeros = allZeros && isZeroBigInt(value)
value, err = b.everstakeContractCallSimpleNumeric(everstakeAutocompoundBalanceOfMethodSignature, addr, contract)
if err != nil {
return nil, err
}
poolData.AutocompoundBalance = *value
allZeros = allZeros && isZeroBigInt(value)
if allZeros {
return nil, nil
}
return &poolData, nil
}

View File

@ -14,13 +14,13 @@ import (
) )
const ( const (
OpZeroCoinMint = 0xc1 OpZeroCoinMint = 0xc1
OpZeroCoinSpend = 0xc2 OpZeroCoinSpend = 0xc2
OpSigmaMint = 0xc3 OpSigmaMint = 0xc3
OpSigmaSpend = 0xc4 OpSigmaSpend = 0xc4
OpLelantusMint = 0xc5 OpLelantusMint = 0xc5
OpLelantusJMint = 0xc6 OpLelantusJMint = 0xc6
OpLelantusJoinSplit = 0xc7 OpLelantusJoinSplit = 0xc7
OpLelantusJoinSplitPayload = 0xc9 OpLelantusJoinSplitPayload = 0xc9
MainnetMagic wire.BitcoinNet = 0xe3d9fef1 MainnetMagic wire.BitcoinNet = 0xe3d9fef1
@ -194,7 +194,6 @@ func (p *FiroParser) ParseBlock(b []byte) (*bchain.Block, error) {
break break
} }
} }
if !isAllZero { if !isAllZero {
// hash data // hash data
@ -344,7 +343,7 @@ type MTPHashDataRoot struct {
} }
type MTPHashData struct { type MTPHashData struct {
BlockMTP [128][128]uint64 BlockMTP [128][128]uint64
} }
type MTPBlockHeader struct { type MTPBlockHeader struct {

View File

@ -0,0 +1,45 @@
package optimism
import (
"context"
"github.com/ethereum/go-ethereum/rpc"
"github.com/trezor/blockbook/bchain"
)
// OptimismRPCClient wraps an rpc client to implement the EVMRPCClient interface
type OptimismRPCClient struct {
*rpc.Client
}
// EthSubscribe subscribes to events and returns a client subscription that implements the EVMClientSubscription interface
func (c *OptimismRPCClient) EthSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (bchain.EVMClientSubscription, error) {
sub, err := c.Client.EthSubscribe(ctx, channel, args...)
if err != nil {
return nil, err
}
return &OptimismClientSubscription{ClientSubscription: sub}, nil
}
// CallContext performs a JSON-RPC call with the given arguments
func (c *OptimismRPCClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
if err := c.Client.CallContext(ctx, result, method, args...); err != nil {
return err
}
// special case to handle empty gas price for a valid rpc transaction
// (https://goerli-optimism.etherscan.io/tx/0x9b62094073147508471e3371920b68070979beea32100acdc49c721350b69cb9)
if r, ok := result.(*bchain.RpcTransaction); ok {
if *r != (bchain.RpcTransaction{}) && r.GasPrice == "" {
r.GasPrice = "0x0"
}
}
return nil
}
// OptimismClientSubscription wraps a client subcription to implement the EVMClientSubscription interface
type OptimismClientSubscription struct {
*rpc.ClientSubscription
}

View File

@ -0,0 +1,73 @@
package optimism
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 = 10
)
// OptimismRPC is an interface to JSON-RPC optimism service.
type OptimismRPC struct {
*eth.EthereumRPC
}
// NewOptimismRPC returns new OptimismRPC instance.
func NewOptimismRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
c, err := eth.NewEthereumRPC(config, pushHandler)
if err != nil {
return nil, err
}
s := &OptimismRPC{
EthereumRPC: c.(*eth.EthereumRPC),
}
return s, nil
}
// Initialize bnb smart chain rpc interface
func (b *OptimismRPC) 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
}

View File

@ -2,8 +2,10 @@ package pivx
import ( import (
"bytes" "bytes"
"encoding/binary"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"math/big" "math/big"
@ -13,7 +15,6 @@ import (
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/bchain/coins/btc" "github.com/trezor/blockbook/bchain/coins/btc"
"github.com/trezor/blockbook/bchain/coins/utils"
) )
// magic numbers // magic numbers
@ -100,7 +101,12 @@ func (p *PivXParser) ParseBlock(b []byte) (*bchain.Block, error) {
r.Seek(32, io.SeekCurrent) r.Seek(32, io.SeekCurrent)
} }
err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w) if h.Version > 7 {
// Skip new hashFinalSaplingRoot (block version 8 or newer)
r.Seek(32, io.SeekCurrent)
}
err = p.PivxDecodeTransactions(r, 0, &w)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "DecodeTransactions") return nil, errors.Annotatef(err, "DecodeTransactions")
} }
@ -255,6 +261,90 @@ func (p *PivXParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain
return s return s
} }
func (p *PivXParser) PivxDecodeTransactions(r *bytes.Reader, pver uint32, blk *wire.MsgBlock) error {
maxTxPerBlock := uint64((wire.MaxBlockPayload / 10) + 1)
txCount, err := wire.ReadVarInt(r, pver)
if err != nil {
return err
}
// Prevent more transactions than could possibly fit into a block.
// It would be possible to cause memory exhaustion and panics without
// a sane upper bound on this count.
if txCount > maxTxPerBlock {
str := fmt.Sprintf("too many transactions to fit into a block "+
"[count %d, max %d]", txCount, maxTxPerBlock)
return &wire.MessageError{Func: "utils.decodeTransactions", Description: str}
}
blk.Transactions = make([]*wire.MsgTx, 0, txCount)
for i := uint64(0); i < txCount; i++ {
tx := wire.MsgTx{}
// read version & seek back to original state
var version uint32 = 0
if err = binary.Read(r, binary.LittleEndian, &version); err != nil {
return err
}
if _, err = r.Seek(-4, io.SeekCurrent); err != nil {
return err
}
txVersion := version & 0xffff
enc := wire.WitnessEncoding
// shielded transactions
if txVersion >= 3 {
enc = wire.BaseEncoding
}
err := p.PivxDecode(&tx, r, pver, enc)
if err != nil {
return err
}
blk.Transactions = append(blk.Transactions, &tx)
}
return nil
}
func (p *PivXParser) PivxDecode(MsgTx *wire.MsgTx, r *bytes.Reader, pver uint32, enc wire.MessageEncoding) error {
if err := MsgTx.BtcDecode(r, pver, enc); err != nil {
return err
}
// extra
version := uint32(MsgTx.Version)
txVersion := version & 0xffff
if txVersion >= 3 {
// valueBalance
r.Seek(9, io.SeekCurrent)
vShieldedSpend, err := wire.ReadVarInt(r, 0)
if err != nil {
return err
}
if vShieldedSpend > 0 {
r.Seek(int64(vShieldedSpend*384), io.SeekCurrent)
}
vShieldOutput, err := wire.ReadVarInt(r, 0)
if err != nil {
return err
}
if vShieldOutput > 0 {
r.Seek(int64(vShieldOutput*948), io.SeekCurrent)
}
// bindingSig
r.Seek(64, io.SeekCurrent)
}
return nil
}
// Checks if script is OP_ZEROCOINMINT // Checks if script is OP_ZEROCOINMINT
func isZeroCoinMintScript(signatureScript []byte) bool { func isZeroCoinMintScript(signatureScript []byte) bool {
return len(signatureScript) > 1 && signatureScript[0] == OP_ZEROCOINMINT return len(signatureScript) > 1 && signatureScript[0] == OP_ZEROCOINMINT

View File

@ -0,0 +1,73 @@
package polygon
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 = 137
)
// PolygonRPC is an interface to JSON-RPC polygon service.
type PolygonRPC struct {
*eth.EthereumRPC
}
// NewPolygonRPC returns new PolygonRPC instance.
func NewPolygonRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
c, err := eth.NewEthereumRPC(config, pushHandler)
if err != nil {
return nil, err
}
s := &PolygonRPC{
EthereumRPC: c.(*eth.EthereumRPC),
}
return s, nil
}
// Initialize polygon rpc interface
func (b *PolygonRPC) 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
}

View File

@ -294,6 +294,10 @@ func Test_UnpackTx(t *testing.T) {
t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
// ignore witness unpacking
for i := range got.Vin {
got.Vin[i].Witness = nil
}
if !reflect.DeepEqual(got, tt.want) { if !reflect.DeepEqual(got, tt.want) {
t.Errorf("unpackTx() got = %v, want %v", got, tt.want) t.Errorf("unpackTx() got = %v, want %v", got, tt.want)
} }

217
bchain/golomb.go Normal file
View File

@ -0,0 +1,217 @@
package bchain
import (
"bytes"
"encoding/hex"
"github.com/golang/glog"
"github.com/juju/errors"
"github.com/martinboehm/btcutil/gcs"
)
type FilterScriptsType int
const (
FilterScriptsInvalid = FilterScriptsType(iota)
FilterScriptsAll
FilterScriptsTaproot
FilterScriptsTaprootNoOrdinals
)
// GolombFilter is computing golomb filter of address descriptors
type GolombFilter struct {
Enabled bool
UseZeroedKey bool
p uint8
key string
filterScripts string
filterScriptsType FilterScriptsType
filterData [][]byte
uniqueData map[string]struct{}
// All the unique txids that contain ordinal data
ordinalTxIds map[string]struct{}
// Mapping of txid to address descriptors - only used in case of taproot-noordinals
allAddressDescriptors map[string][]AddressDescriptor
}
// NewGolombFilter initializes the GolombFilter handler
func NewGolombFilter(p uint8, filterScripts string, key string, useZeroedKey bool) (*GolombFilter, error) {
if p == 0 {
return &GolombFilter{Enabled: false}, nil
}
gf := GolombFilter{
Enabled: true,
UseZeroedKey: useZeroedKey,
p: p,
key: key,
filterScripts: filterScripts,
filterScriptsType: filterScriptsToScriptsType(filterScripts),
filterData: make([][]byte, 0),
uniqueData: make(map[string]struct{}),
}
// reject invalid filterScripts
if gf.filterScriptsType == FilterScriptsInvalid {
return nil, errors.Errorf("Invalid/unsupported filterScripts parameter %s", filterScripts)
}
// set ordinal-related fields if needed
if gf.ignoreOrdinals() {
gf.ordinalTxIds = make(map[string]struct{})
gf.allAddressDescriptors = make(map[string][]AddressDescriptor)
}
return &gf, nil
}
// Gets the M parameter that we are using for the filter
// Currently it relies on P parameter, but that can change
func GetGolombParamM(p uint8) uint64 {
return uint64(1 << uint64(p))
}
// Checks whether this input contains ordinal data
func isInputOrdinal(vin Vin) bool {
byte_pattern := []byte{
0x00, // OP_0, OP_FALSE
0x63, // OP_IF
0x03, // OP_PUSHBYTES_3
0x6f, // "o"
0x72, // "r"
0x64, // "d"
0x01, // OP_PUSHBYTES_1
}
// Witness needs to have at least 3 items and the second one needs to contain certain pattern
return len(vin.Witness) > 2 && bytes.Contains(vin.Witness[1], byte_pattern)
}
// Whether a transaction contains any ordinal data
func txContainsOrdinal(tx *Tx) bool {
for _, vin := range tx.Vin {
if isInputOrdinal(vin) {
return true
}
}
return false
}
// Saving all the ordinal-related txIds so we can later ignore their address descriptors
func (f *GolombFilter) markTxAndParentsAsOrdinals(tx *Tx) {
f.ordinalTxIds[tx.Txid] = struct{}{}
for _, vin := range tx.Vin {
f.ordinalTxIds[vin.Txid] = struct{}{}
}
}
// Adding a new address descriptor mapped to a txid
func (f *GolombFilter) addTxIdMapping(ad AddressDescriptor, tx *Tx) {
f.allAddressDescriptors[tx.Txid] = append(f.allAddressDescriptors[tx.Txid], ad)
}
// AddAddrDesc adds taproot address descriptor to the data for the filter
func (f *GolombFilter) AddAddrDesc(ad AddressDescriptor, tx *Tx) {
if f.ignoreNonTaproot() && !ad.IsTaproot() {
return
}
if f.ignoreOrdinals() && tx != nil && txContainsOrdinal(tx) {
f.markTxAndParentsAsOrdinals(tx)
return
}
if len(ad) == 0 {
return
}
// When ignoring ordinals, we need to save all the address descriptors before
// filtering out the "invalid" ones.
if f.ignoreOrdinals() && tx != nil {
f.addTxIdMapping(ad, tx)
return
}
f.includeAddrDesc(ad)
}
// Private function to be called with descriptors that were already validated
func (f *GolombFilter) includeAddrDesc(ad AddressDescriptor) {
s := string(ad)
if _, found := f.uniqueData[s]; !found {
f.filterData = append(f.filterData, ad)
f.uniqueData[s] = struct{}{}
}
}
// Including all the address descriptors from non-ordinal transactions
func (f *GolombFilter) includeAllAddressDescriptorsOrdinals() {
for txid, ads := range f.allAddressDescriptors {
// Ignoring the txids that contain ordinal data
if _, found := f.ordinalTxIds[txid]; found {
continue
}
for _, ad := range ads {
f.includeAddrDesc(ad)
}
}
}
// Compute computes golomb filter from the data
func (f *GolombFilter) Compute() []byte {
m := GetGolombParamM(f.p)
// In case of ignoring the ordinals, we still need to assemble the filter data
if f.ignoreOrdinals() {
f.includeAllAddressDescriptorsOrdinals()
}
if len(f.filterData) == 0 {
return nil
}
// Used key is possibly just zeroes, otherwise get it from the supplied key
var key [gcs.KeySize]byte
if f.UseZeroedKey {
key = [gcs.KeySize]byte{}
} else {
b, _ := hex.DecodeString(f.key)
if len(b) < gcs.KeySize {
return nil
}
copy(key[:], b[:gcs.KeySize])
}
filter, err := gcs.BuildGCSFilter(f.p, m, key, f.filterData)
if err != nil {
glog.Error("Cannot create golomb filter for ", f.key, ", ", err)
return nil
}
fb, err := filter.NBytes()
if err != nil {
glog.Error("Error getting NBytes from golomb filter for ", f.key, ", ", err)
return nil
}
return fb
}
func (f *GolombFilter) ignoreNonTaproot() bool {
switch f.filterScriptsType {
case FilterScriptsTaproot, FilterScriptsTaprootNoOrdinals:
return true
}
return false
}
func (f *GolombFilter) ignoreOrdinals() bool {
switch f.filterScriptsType {
case FilterScriptsTaprootNoOrdinals:
return true
}
return false
}
func filterScriptsToScriptsType(filterScripts string) FilterScriptsType {
switch filterScripts {
case "":
return FilterScriptsAll
case "taproot":
return FilterScriptsTaproot
case "taproot-noordinals":
return FilterScriptsTaprootNoOrdinals
}
return FilterScriptsInvalid
}

282
bchain/golomb_test.go Normal file
View File

@ -0,0 +1,282 @@
// //go:build unittest
package bchain
import (
"encoding/hex"
"testing"
)
func getCommonAddressDescriptors() []AddressDescriptor {
return []AddressDescriptor{
// bc1pgeqrcq5capal83ypxczmypjdhk4d9wwcea4k66c7ghe07p2qt97sqh8sy5
hexToBytes("512046403c0298e87bf3c4813605b2064dbdaad2b9d8cf6b6d6b1e45f2ff0540597d"),
// bc1p7en40zu9hmf9d3luh8evmfyg655pu5k2gtna6j7zr623f9tz7z0stfnwav
hexToBytes("5120f667578b85bed256c7fcb9f2cda488d5281e52ca42e7dd4bc21e95149562f09f"),
// 39ECUF8YaFRX7XfttfAiLa5ir43bsrQUZJ
hexToBytes("a91452ae9441d9920d9eb4a3c0a877ca8d8de547ce6587"),
}
}
func TestGolombFilter(t *testing.T) {
tests := []struct {
name string
p uint8
useZeroedKey bool
filterScripts string
key string
addressDescriptors []AddressDescriptor
wantError bool
wantEnabled bool
want string
}{
{
name: "taproot",
p: 20,
useZeroedKey: false,
filterScripts: "taproot",
key: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
addressDescriptors: getCommonAddressDescriptors(),
wantEnabled: true,
wantError: false,
want: "0235dddcce5d60",
},
{
name: "taproot-zeroed-key",
p: 20,
useZeroedKey: true,
filterScripts: "taproot",
key: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
addressDescriptors: getCommonAddressDescriptors(),
wantEnabled: true,
wantError: false,
want: "0218c23a013600",
},
{
name: "taproot p=21",
p: 21,
useZeroedKey: false,
filterScripts: "taproot",
key: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
addressDescriptors: getCommonAddressDescriptors(),
wantEnabled: true,
wantError: false,
want: "0235ddda672eb0",
},
{
name: "all",
p: 20,
useZeroedKey: false,
filterScripts: "",
key: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
addressDescriptors: getCommonAddressDescriptors(),
wantEnabled: true,
wantError: false,
want: "0350ccc61ac611976c80",
},
{
name: "taproot-noordinals",
p: 20,
useZeroedKey: false,
filterScripts: "taproot-noordinals",
key: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
addressDescriptors: getCommonAddressDescriptors(),
wantEnabled: true,
wantError: false,
want: "0235dddcce5d60",
},
{
name: "not supported filter",
p: 20,
useZeroedKey: false,
filterScripts: "notsupported",
wantEnabled: false,
wantError: true,
want: "",
},
{
name: "not enabled",
p: 0,
useZeroedKey: false,
filterScripts: "",
wantEnabled: false,
wantError: false,
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gf, err := NewGolombFilter(tt.p, tt.filterScripts, tt.key, tt.useZeroedKey)
if err != nil && !tt.wantError {
t.Errorf("TestGolombFilter.NewGolombFilter() got unexpected error '%v'", err)
return
}
if err == nil && tt.wantError {
t.Errorf("TestGolombFilter.NewGolombFilter() wanted error, got none")
return
}
if gf == nil && tt.wantError {
return
}
if gf.Enabled != tt.wantEnabled {
t.Errorf("TestGolombFilter.NewGolombFilter() got gf.Enabled %v, want %v", gf.Enabled, tt.wantEnabled)
return
}
for _, ad := range tt.addressDescriptors {
gf.AddAddrDesc(ad, nil)
}
f := gf.Compute()
got := hex.EncodeToString(f)
if got != tt.want {
t.Errorf("TestGolombFilter Compute() got %v, want %v", got, tt.want)
}
})
}
}
// Preparation transaction, locking BTC redeemable by ordinal witness - parent of the reveal transaction
func getOrdinalCommitTx() (Tx, []AddressDescriptor) {
tx := Tx{
// https://mempool.space/tx/11111c17cbe86aebab146ee039d4e354cb55a9fb226ebdd2e30948630e7710ad
Txid: "11111c17cbe86aebab146ee039d4e354cb55a9fb226ebdd2e30948630e7710ad",
Vin: []Vin{
{
// https://mempool.space/tx/c4cae52a6e681b66c85c12feafb42f3617f34977032df1ee139eae07370863ef
Txid: "c163fe1fdc21269cb05621adec38045e46a65289a356f9354df6010bce064916",
Vout: 0,
Witness: [][]byte{
hexToBytes("0371633164dd16345c02e80c9963042f9a502aa2c8109c0f61da333ac1503c3ce2a1b79895359bbdee5979ab2cb44f3395892e1c419c3a8f67d31d33d7e764c9"),
},
},
},
Vout: []Vout{
{
ScriptPubKey: ScriptPubKey{
Hex: "51206a711358bac6ca8f7ddfdf8f733546e658208122939f0bf7a3727f8143dfbbff",
Addresses: []string{
"bc1pdfc3xk96cm9g7lwlm78hxd2xuevzpqfzjw0shaarwflczs7lh0lstksdn0",
},
},
},
{
ScriptPubKey: ScriptPubKey{
Hex: "a9144390d0b3d2b6d48b8c205ffbe40b2d84c40de07f87",
Addresses: []string{
"37rGgLSLX6C6LS9am4KWd6GT1QCEP4H4py",
},
},
},
{
ScriptPubKey: ScriptPubKey{
Hex: "76a914ba6b046dd832aa8bc41c158232bcc18211387c4388ac",
Addresses: []string{
"1HzgtNdRCXszf95rFYemsDSHJQBbs9rbZf",
},
},
},
},
}
addressDescriptors := []AddressDescriptor{
// bc1pdfc3xk96cm9g7lwlm78hxd2xuevzpqfzjw0shaarwflczs7lh0lstksdn0
hexToBytes("51206a711358bac6ca8f7ddfdf8f733546e658208122939f0bf7a3727f8143dfbbff"),
// 37rGgLSLX6C6LS9am4KWd6GT1QCEP4H4py
hexToBytes("a9144390d0b3d2b6d48b8c205ffbe40b2d84c40de07f87"),
// 1HzgtNdRCXszf95rFYemsDSHJQBbs9rbZf
hexToBytes("76a914ba6b046dd832aa8bc41c158232bcc18211387c4388ac"),
}
return tx, addressDescriptors
}
// Transaction containing the actual ordinal data in witness - child of the commit transaction
func getOrdinalRevealTx() (Tx, []AddressDescriptor) {
tx := Tx{
// https://mempool.space/tx/c4cae52a6e681b66c85c12feafb42f3617f34977032df1ee139eae07370863ef
Txid: "c4cae52a6e681b66c85c12feafb42f3617f34977032df1ee139eae07370863ef",
Vin: []Vin{
{
Txid: "11111c17cbe86aebab146ee039d4e354cb55a9fb226ebdd2e30948630e7710ad",
Vout: 0,
Witness: [][]byte{
hexToBytes("737ad2835962e3d147cd74a578f1109e9314eac9d00c9fad304ce2050b78fac21a2d124fd886d1d646cf1de5d5c9754b0415b960b1319526fa25e36ca1f650ce"),
hexToBytes("2029f34532e043fade4471779b4955005db8fa9b64c9e8d0a2dae4a38bbca23328ac0063036f726401010a696d6167652f77656270004d08025249464650020000574542505650384c440200002f57c2950067a026009086939b7785a104699656f4f53388355445b6415d22f8924000fd83bd31d346ca69f8fcfed6d8d18231846083f90f00ffbf203883666c36463c6ba8662257d789935e002192245bd15ac00216b080052cac85b380052c60e1593859f33a7a7abff7ed88feb361db3692341bc83553aef7aec75669ffb1ffd87fec3ff61ffb8ffdc736f20a96a0fba34071d4fdf111c435381df667728f95c4e82b6872d82471bfdc1665107bb80fd46df1686425bcd2e27eb59adc9d17b54b997ee96776a7c37ca2b57b9551bcffeb71d88768765af7384c2e3ba031ca3f19c9ddb0c6ec55223fbfe3731a1e8d7bb010de8532d53293bbbb6145597ee53559a612e6de4f8fc66936ef463eea7498555643ac0dafad6627575f2733b9fb352e411e7d9df8fc80fde75f5f66f5c5381a46b9a697d9c97555c4bf41a4909b9dd071557c3dfe0bfcd6459e06514266c65756ce9f25705230df63d30fef6076b797e1f49d00b41e87b5ccecb1c237f419e4b3ca6876053c14fc979a629459a62f78d735fb078bfa0e7a1fc69ad379447d817e06b3d7f1de820f28534f85fa20469cd6f93ddc6c5f2a94878fc64a98ac336294c99d27d11742268ae1a34cd61f31e2e4aee94b0ff496f55068fa727ace6ad2ec1e6e3f59e6a8bd154f287f652fbfaa05cac067951de1bfacc0e330c3bf6dd2efde4c509646566836eb71986154731daf722a6ff585001e87f9479559a61265d6e330f3682bf87ab2598fc3fca36da778e59cee71584594ef175e6d7d5f70d6deb02c4b371e5063c35669ffb1ffd87ffe0e730068"),
hexToBytes("c129f34532e043fade4471779b4955005db8fa9b64c9e8d0a2dae4a38bbca23328"),
},
},
},
Vout: []Vout{
{
ScriptPubKey: ScriptPubKey{
Hex: "51206850b179630df0f7012ae2b111bafa52ebb9b54e1435fc4f98fbe0af6f95076a",
Addresses: []string{
"bc1pdpgtz7trphc0wqf2u2c3rwh62t4mnd2wzs6lcnucl0s27mu4qa4q4md9ta",
},
},
},
},
}
addressDescriptors := []AddressDescriptor{
// bc1pdpgtz7trphc0wqf2u2c3rwh62t4mnd2wzs6lcnucl0s27mu4qa4q4md9ta
hexToBytes("51206850b179630df0f7012ae2b111bafa52ebb9b54e1435fc4f98fbe0af6f95076a"),
}
return tx, addressDescriptors
}
func TestGolombIsOrdinal(t *testing.T) {
revealTx, _ := getOrdinalRevealTx()
if txContainsOrdinal(&revealTx) != true {
t.Error("Ordinal not found in reveal Tx")
}
commitTx, _ := getOrdinalCommitTx()
if txContainsOrdinal(&commitTx) != false {
t.Error("Ordinal found in commit Tx, but should not be there")
}
}
func TestGolombOrdinalTransactions(t *testing.T) {
tests := []struct {
name string
filterScripts string
want string
}{
{
name: "all",
filterScripts: "",
want: "04256e660160e42ff40ee320", // take all four descriptors
},
{
name: "taproot",
filterScripts: "taproot",
want: "0212b734c2ebe0", // filter out two non-taproot ones
},
{
name: "taproot-noordinals",
filterScripts: "taproot-noordinals",
want: "", // ignore everything
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gf, err := NewGolombFilter(20, tt.filterScripts, "", true)
if err != nil {
t.Errorf("TestGolombOrdinalTransactions.NewGolombFilter() got unexpected error '%v'", err)
return
}
commitTx, addressDescriptorsCommit := getOrdinalCommitTx()
revealTx, addressDescriptorsReveal := getOrdinalRevealTx()
for _, ad := range addressDescriptorsCommit {
gf.AddAddrDesc(ad, &commitTx)
}
for _, ad := range addressDescriptorsReveal {
gf.AddAddrDesc(ad, &revealTx)
}
f := gf.Compute()
got := hex.EncodeToString(f)
if got != tt.want {
t.Errorf("TestGolombOrdinalTransactions Compute() got %v, want %v", got, tt.want)
}
})
}
}

View File

@ -2,13 +2,11 @@ package bchain
import ( import (
"encoding/hex" "encoding/hex"
"errors"
"fmt"
"math/big" "math/big"
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/martinboehm/btcutil/gcs" "github.com/juju/errors"
) )
type chanInputPayload struct { type chanInputPayload struct {
@ -16,14 +14,6 @@ type chanInputPayload struct {
index int index int
} }
type filterScriptsType int
const (
filterScriptsInvalid = filterScriptsType(iota)
filterScriptsAll
filterScriptsTaproot
)
// MempoolBitcoinType is mempool handle. // MempoolBitcoinType is mempool handle.
type MempoolBitcoinType struct { type MempoolBitcoinType struct {
BaseMempool BaseMempool
@ -31,19 +21,13 @@ type MempoolBitcoinType struct {
chanAddrIndex chan txidio chanAddrIndex chan txidio
AddrDescForOutpoint AddrDescForOutpointFunc AddrDescForOutpoint AddrDescForOutpointFunc
golombFilterP uint8 golombFilterP uint8
golombFilterM uint64 filterScripts string
filterScripts filterScriptsType useZeroedKey bool
} }
// NewMempoolBitcoinType creates new mempool handler. // NewMempoolBitcoinType creates new mempool handler.
// For now there is no cleanup of sync routines, the expectation is that the mempool is created only once per process // For now there is no cleanup of sync routines, the expectation is that the mempool is created only once per process
func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golombFilterP uint8, filterScripts string) *MempoolBitcoinType { func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golombFilterP uint8, filterScripts string, useZeroedKey bool) *MempoolBitcoinType {
filterScriptsType := filterScriptsToScriptsType(filterScripts)
if filterScriptsType == filterScriptsInvalid {
glog.Error("Invalid filterScripts ", filterScripts, ", switching off golomb filter")
golombFilterP = 0
}
golombFilterM := uint64(1 << golombFilterP)
m := &MempoolBitcoinType{ m := &MempoolBitcoinType{
BaseMempool: BaseMempool{ BaseMempool: BaseMempool{
chain: chain, chain: chain,
@ -53,8 +37,8 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golomb
chanTxid: make(chan string, 1), chanTxid: make(chan string, 1),
chanAddrIndex: make(chan txidio, 1), chanAddrIndex: make(chan txidio, 1),
golombFilterP: golombFilterP, golombFilterP: golombFilterP,
golombFilterM: golombFilterM, filterScripts: filterScripts,
filterScripts: filterScriptsType, useZeroedKey: useZeroedKey,
} }
for i := 0; i < workers; i++ { for i := 0; i < workers; i++ {
go func(i int) { go func(i int) {
@ -81,16 +65,6 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golomb
return m return m
} }
func filterScriptsToScriptsType(filterScripts string) filterScriptsType {
switch filterScripts {
case "":
return filterScriptsAll
case "taproot":
return filterScriptsTaproot
}
return filterScriptsInvalid
}
func (m *MempoolBitcoinType) getInputAddress(payload *chanInputPayload) *addrIndex { func (m *MempoolBitcoinType) getInputAddress(payload *chanInputPayload) *addrIndex {
var addrDesc AddressDescriptor var addrDesc AddressDescriptor
var value *big.Int var value *big.Int
@ -125,56 +99,21 @@ func (m *MempoolBitcoinType) getInputAddress(payload *chanInputPayload) *addrInd
} }
func isTaproot(addrDesc AddressDescriptor) bool { func (m *MempoolBitcoinType) computeGolombFilter(mtx *MempoolTx, tx *Tx) string {
if len(addrDesc) == 34 && addrDesc[0] == 0x51 && addrDesc[1] == 0x20 { gf, _ := NewGolombFilter(m.golombFilterP, m.filterScripts, mtx.Txid, m.useZeroedKey)
return true if gf == nil || !gf.Enabled {
return ""
} }
return false for _, vin := range mtx.Vin {
} gf.AddAddrDesc(vin.AddrDesc, tx)
func (m *MempoolBitcoinType) computeGolombFilter(mtx *MempoolTx) string {
uniqueScripts := make(map[string]struct{})
filterData := make([][]byte, 0)
for i := range mtx.Vin {
vin := &mtx.Vin[i]
if m.filterScripts == filterScriptsAll || (m.filterScripts == filterScriptsTaproot && isTaproot(vin.AddrDesc)) {
s := string(vin.AddrDesc)
if _, found := uniqueScripts[s]; !found {
filterData = append(filterData, vin.AddrDesc)
uniqueScripts[s] = struct{}{}
}
}
} }
for i := range mtx.Vout { for _, vout := range mtx.Vout {
vout := &mtx.Vout[i]
b, err := hex.DecodeString(vout.ScriptPubKey.Hex) b, err := hex.DecodeString(vout.ScriptPubKey.Hex)
if err == nil { if err == nil {
if m.filterScripts == filterScriptsAll || (m.filterScripts == filterScriptsTaproot && isTaproot(b)) { gf.AddAddrDesc(b, tx)
s := string(b)
if _, found := uniqueScripts[s]; !found {
filterData = append(filterData, b)
uniqueScripts[s] = struct{}{}
}
}
} }
} }
if len(filterData) == 0 { fb := gf.Compute()
return ""
}
b, _ := hex.DecodeString(mtx.Txid)
if len(b) < gcs.KeySize {
return ""
}
filter, err := gcs.BuildGCSFilter(m.golombFilterP, m.golombFilterM, *(*[gcs.KeySize]byte)(b[:gcs.KeySize]), filterData)
if err != nil {
glog.Error("Cannot create golomb filter for ", mtx.Txid, ", ", err)
return ""
}
fb, err := filter.NBytes()
if err != nil {
glog.Error("Error getting NBytes from golomb filter for ", mtx.Txid, ", ", err)
return ""
}
return hex.EncodeToString(fb) return hex.EncodeToString(fb)
} }
@ -231,7 +170,7 @@ func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan chanInputPay
} }
var golombFilter string var golombFilter string
if m.golombFilterP > 0 { if m.golombFilterP > 0 {
golombFilter = m.computeGolombFilter(mtx) golombFilter = m.computeGolombFilter(mtx, tx)
} }
if m.OnNewTx != nil { if m.OnNewTx != nil {
m.OnNewTx(mtx) m.OnNewTx(mtx)
@ -301,8 +240,8 @@ func (m *MempoolBitcoinType) Resync() (int, error) {
// GetTxidFilterEntries returns all mempool entries with golomb filter from // GetTxidFilterEntries returns all mempool entries with golomb filter from
func (m *MempoolBitcoinType) GetTxidFilterEntries(filterScripts string, fromTimestamp uint32) (MempoolTxidFilterEntries, error) { func (m *MempoolBitcoinType) GetTxidFilterEntries(filterScripts string, fromTimestamp uint32) (MempoolTxidFilterEntries, error) {
if m.filterScripts != filterScriptsToScriptsType(filterScripts) { if m.filterScripts != filterScripts {
return MempoolTxidFilterEntries{}, errors.New(fmt.Sprint("Unsupported script filter ", filterScripts)) return MempoolTxidFilterEntries{}, errors.Errorf("Unsupported script filter %s", filterScripts)
} }
m.mux.Lock() m.mux.Lock()
entries := make(map[string]string) entries := make(map[string]string)
@ -312,5 +251,5 @@ func (m *MempoolBitcoinType) GetTxidFilterEntries(filterScripts string, fromTime
} }
} }
m.mux.Unlock() m.mux.Unlock()
return MempoolTxidFilterEntries{entries}, nil return MempoolTxidFilterEntries{entries, m.useZeroedKey}, nil
} }

View File

@ -16,9 +16,9 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
randomScript := hexToBytes("a914ff074800343a81ada8fe86c2d5d5a0e55b93dd7a87") randomScript := hexToBytes("a914ff074800343a81ada8fe86c2d5d5a0e55b93dd7a87")
m := &MempoolBitcoinType{ m := &MempoolBitcoinType{
golombFilterP: 20, golombFilterP: 20,
golombFilterM: uint64(1 << 20), filterScripts: "taproot",
filterScripts: filterScriptsTaproot,
} }
golombFilterM := GetGolombParamM(m.golombFilterP)
tests := []struct { tests := []struct {
name string name string
mtx MempoolTx mtx MempoolTx
@ -194,13 +194,13 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got := m.computeGolombFilter(&tt.mtx) got := m.computeGolombFilter(&tt.mtx, nil)
if got != tt.want { if got != tt.want {
t.Errorf("MempoolBitcoinType.computeGolombFilter() = %v, want %v", got, tt.want) t.Errorf("MempoolBitcoinType.computeGolombFilter() = %v, want %v", got, tt.want)
} }
if got != "" { if got != "" {
// build the filter from computed value // build the filter from computed value
filter, err := gcs.FromNBytes(m.golombFilterP, m.golombFilterM, hexToBytes(got)) filter, err := gcs.FromNBytes(m.golombFilterP, golombFilterM, hexToBytes(got))
if err != nil { if err != nil {
t.Errorf("gcs.BuildGCSFilter() unexpected error %v", err) t.Errorf("gcs.BuildGCSFilter() unexpected error %v", err)
} }
@ -211,8 +211,8 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("filter.Match vin[%d] unexpected error %v", i, err) t.Errorf("filter.Match vin[%d] unexpected error %v", i, err)
} }
if match != isTaproot(tt.mtx.Vin[i].AddrDesc) { if match != tt.mtx.Vin[i].AddrDesc.IsTaproot() {
t.Errorf("filter.Match vin[%d] got %v, want %v", i, match, isTaproot(tt.mtx.Vin[i].AddrDesc)) t.Errorf("filter.Match vin[%d] got %v, want %v", i, match, tt.mtx.Vin[i].AddrDesc.IsTaproot())
} }
} }
// check that the vout scripts match the filter // check that the vout scripts match the filter
@ -222,8 +222,8 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("filter.Match vout[%d] unexpected error %v", i, err) t.Errorf("filter.Match vout[%d] unexpected error %v", i, err)
} }
if match != isTaproot(s) { if match != AddressDescriptor(s).IsTaproot() {
t.Errorf("filter.Match vout[%d] got %v, want %v", i, match, isTaproot(s)) t.Errorf("filter.Match vout[%d] got %v, want %v", i, match, AddressDescriptor(s).IsTaproot())
} }
} }
// check that a random script does not match the filter // check that a random script does not match the filter
@ -238,3 +238,115 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
}) })
} }
} }
func TestMempoolBitcoinType_computeGolombFilter_taproot_noordinals(t *testing.T) {
m := &MempoolBitcoinType{
golombFilterP: 20,
filterScripts: "taproot-noordinals",
}
tests := []struct {
name string
mtx MempoolTx
tx Tx
want string
}{
{
name: "taproot-no-ordinals normal taproot tx",
mtx: MempoolTx{
Txid: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
Vin: []MempoolVin{
{
// bc1pdfc3xk96cm9g7lwlm78hxd2xuevzpqfzjw0shaarwflczs7lh0lstksdn0
AddrDesc: hexToBytes("51206a711358bac6ca8f7ddfdf8f733546e658208122939f0bf7a3727f8143dfbbff"),
},
},
Vout: []Vout{
{
ScriptPubKey: ScriptPubKey{
Hex: "51206850b179630df0f7012ae2b111bafa52ebb9b54e1435fc4f98fbe0af6f95076a",
Addresses: []string{
"bc1pdpgtz7trphc0wqf2u2c3rwh62t4mnd2wzs6lcnucl0s27mu4qa4q4md9ta",
},
},
},
},
},
tx: Tx{
Vin: []Vin{
{
Witness: [][]byte{
hexToBytes("737ad2835962e3d147cd74a578f1109e9314eac9d00c9fad304ce2050b78fac21a2d124fd886d1d646cf1de5d5c9754b0415b960b1319526fa25e36ca1f650ce"),
},
},
},
Vout: []Vout{
{
ScriptPubKey: ScriptPubKey{
Hex: "51206850b179630df0f7012ae2b111bafa52ebb9b54e1435fc4f98fbe0af6f95076a",
Addresses: []string{
"bc1pdpgtz7trphc0wqf2u2c3rwh62t4mnd2wzs6lcnucl0s27mu4qa4q4md9ta",
},
},
},
},
},
want: "02899e8c952b40",
},
{
name: "taproot-no-ordinals ordinal tx",
mtx: MempoolTx{
Txid: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
Vin: []MempoolVin{
{
// bc1pdfc3xk96cm9g7lwlm78hxd2xuevzpqfzjw0shaarwflczs7lh0lstksdn0
AddrDesc: hexToBytes("51206a711358bac6ca8f7ddfdf8f733546e658208122939f0bf7a3727f8143dfbbff"),
},
},
Vout: []Vout{
{
ScriptPubKey: ScriptPubKey{
Hex: "51206850b179630df0f7012ae2b111bafa52ebb9b54e1435fc4f98fbe0af6f95076a",
Addresses: []string{
"bc1pdpgtz7trphc0wqf2u2c3rwh62t4mnd2wzs6lcnucl0s27mu4qa4q4md9ta",
},
},
},
},
},
tx: Tx{
// https://mempool.space/tx/c4cae52a6e681b66c85c12feafb42f3617f34977032df1ee139eae07370863ef
Txid: "c4cae52a6e681b66c85c12feafb42f3617f34977032df1ee139eae07370863ef",
Vin: []Vin{
{
Txid: "11111c17cbe86aebab146ee039d4e354cb55a9fb226ebdd2e30948630e7710ad",
Vout: 0,
Witness: [][]byte{
hexToBytes("737ad2835962e3d147cd74a578f1109e9314eac9d00c9fad304ce2050b78fac21a2d124fd886d1d646cf1de5d5c9754b0415b960b1319526fa25e36ca1f650ce"),
hexToBytes("2029f34532e043fade4471779b4955005db8fa9b64c9e8d0a2dae4a38bbca23328ac0063036f726401010a696d6167652f77656270004d08025249464650020000574542505650384c440200002f57c2950067a026009086939b7785a104699656f4f53388355445b6415d22f8924000fd83bd31d346ca69f8fcfed6d8d18231846083f90f00ffbf203883666c36463c6ba8662257d789935e002192245bd15ac00216b080052cac85b380052c60e1593859f33a7a7abff7ed88feb361db3692341bc83553aef7aec75669ffb1ffd87fec3ff61ffb8ffdc736f20a96a0fba34071d4fdf111c435381df667728f95c4e82b6872d82471bfdc1665107bb80fd46df1686425bcd2e27eb59adc9d17b54b997ee96776a7c37ca2b57b9551bcffeb71d88768765af7384c2e3ba031ca3f19c9ddb0c6ec55223fbfe3731a1e8d7bb010de8532d53293bbbb6145597ee53559a612e6de4f8fc66936ef463eea7498555643ac0dafad6627575f2733b9fb352e411e7d9df8fc80fde75f5f66f5c5381a46b9a697d9c97555c4bf41a4909b9dd071557c3dfe0bfcd6459e06514266c65756ce9f25705230df63d30fef6076b797e1f49d00b41e87b5ccecb1c237f419e4b3ca6876053c14fc979a629459a62f78d735fb078bfa0e7a1fc69ad379447d817e06b3d7f1de820f28534f85fa20469cd6f93ddc6c5f2a94878fc64a98ac336294c99d27d11742268ae1a34cd61f31e2e4aee94b0ff496f55068fa727ace6ad2ec1e6e3f59e6a8bd154f287f652fbfaa05cac067951de1bfacc0e330c3bf6dd2efde4c509646566836eb71986154731daf722a6ff585001e87f9479559a61265d6e330f3682bf87ab2598fc3fca36da778e59cee71584594ef175e6d7d5f70d6deb02c4b371e5063c35669ffb1ffd87ffe0e730068"),
hexToBytes("c129f34532e043fade4471779b4955005db8fa9b64c9e8d0a2dae4a38bbca23328"),
},
},
},
Vout: []Vout{
{
ScriptPubKey: ScriptPubKey{
Hex: "51206850b179630df0f7012ae2b111bafa52ebb9b54e1435fc4f98fbe0af6f95076a",
Addresses: []string{
"bc1pdpgtz7trphc0wqf2u2c3rwh62t4mnd2wzs6lcnucl0s27mu4qa4q4md9ta",
},
},
},
},
},
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := m.computeGolombFilter(&tt.mtx, &tt.tx)
if got != tt.want {
t.Errorf("MempoolBitcoinType.computeGolombFilter() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -132,8 +132,8 @@ func (m *MempoolEthereumType) Resync() (int, error) {
return entries, nil return entries, nil
} }
// AddTransactionToMempool adds transactions to mempool // AddTransactionToMempool adds transactions to mempool, returns true if tx added to mempool, false if not added (for example duplicate call)
func (m *MempoolEthereumType) AddTransactionToMempool(txid string) { func (m *MempoolEthereumType) AddTransactionToMempool(txid string) bool {
m.mux.Lock() m.mux.Lock()
_, exists := m.txEntries[txid] _, exists := m.txEntries[txid]
m.mux.Unlock() m.mux.Unlock()
@ -143,7 +143,7 @@ func (m *MempoolEthereumType) AddTransactionToMempool(txid string) {
if !exists { if !exists {
entry, ok := m.createTxEntry(txid, uint32(time.Now().Unix())) entry, ok := m.createTxEntry(txid, uint32(time.Now().Unix()))
if !ok { if !ok {
return return false
} }
m.mux.Lock() m.mux.Lock()
m.txEntries[txid] = entry m.txEntries[txid] = entry
@ -152,6 +152,7 @@ func (m *MempoolEthereumType) AddTransactionToMempool(txid string) {
} }
m.mux.Unlock() m.mux.Unlock()
} }
return !exists
} }
// RemoveTransactionFromMempool removes transaction from mempool // RemoveTransactionFromMempool removes transaction from mempool

View File

@ -57,6 +57,7 @@ type Vin struct {
ScriptSig ScriptSig `json:"scriptSig"` ScriptSig ScriptSig `json:"scriptSig"`
Sequence uint32 `json:"sequence"` Sequence uint32 `json:"sequence"`
Addresses []string `json:"addresses"` Addresses []string `json:"addresses"`
Witness [][]byte `json:"-"`
} }
// ScriptPubKey contains data about output script // ScriptPubKey contains data about output script
@ -130,7 +131,8 @@ type TokenTypeName string
// Token types // Token types
const ( const (
UnknownTokenType TokenTypeName = "" UnknownTokenType TokenTypeName = ""
UnhandledTokenType TokenTypeName = "-"
// XPUBAddressTokenType is address derived from xpub // XPUBAddressTokenType is address derived from xpub
XPUBAddressTokenType TokenTypeName = "XPUBAddress" XPUBAddressTokenType TokenTypeName = "XPUBAddress"
@ -226,6 +228,13 @@ func (ad AddressDescriptor) String() string {
return "ad:" + hex.EncodeToString(ad) return "ad:" + hex.EncodeToString(ad)
} }
func (ad AddressDescriptor) IsTaproot() bool {
if len(ad) == 34 && ad[0] == 0x51 && ad[1] == 0x20 {
return true
}
return false
}
// AddressDescriptorFromString converts string created by AddressDescriptor.String to AddressDescriptor // AddressDescriptorFromString converts string created by AddressDescriptor.String to AddressDescriptor
func AddressDescriptorFromString(s string) (AddressDescriptor, error) { func AddressDescriptorFromString(s string) (AddressDescriptor, error) {
if len(s) > 3 && s[0:3] == "ad:" { if len(s) > 3 && s[0:3] == "ad:" {
@ -266,8 +275,10 @@ type XpubDescriptor struct {
type MempoolTxidEntries []MempoolTxidEntry type MempoolTxidEntries []MempoolTxidEntry
// MempoolTxidFilterEntries is a map of txids to mempool golomb filters // 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 { type MempoolTxidFilterEntries struct {
Entries map[string]string `json:"entries,omitempty"` Entries map[string]string `json:"entries,omitempty"`
UsedZeroedKey bool `json:"usedZeroedKey,omitempty"`
} }
// OnNewBlockFunc is used to send notification about a new block // OnNewBlockFunc is used to send notification about a new block
@ -323,6 +334,10 @@ type BlockChain interface {
EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error) EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error)
EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error)
EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error)
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) GetTokenURI(contractDesc AddressDescriptor, tokenID *big.Int) (string, error)
} }

View File

@ -122,9 +122,13 @@ type RpcLog struct {
// RpcLog is returned by eth_getTransactionReceipt // RpcLog is returned by eth_getTransactionReceipt
type RpcReceipt struct { type RpcReceipt struct {
GasUsed string `json:"gasUsed"` GasUsed string `json:"gasUsed"`
Status string `json:"status"` Status string `json:"status"`
Logs []*RpcLog `json:"logs"` Logs []*RpcLog `json:"logs"`
L1Fee string `json:"l1Fee,omitempty"`
L1FeeScalar string `json:"l1FeeScalar,omitempty"`
L1GasPrice string `json:"l1GasPrice,omitempty"`
L1GasUsed string `json:"l1GasUsed,omitempty"`
} }
// EthereumSpecificData contains data specific to Ethereum transactions // EthereumSpecificData contains data specific to Ethereum transactions
@ -146,3 +150,16 @@ type EthereumBlockSpecificData struct {
AddressAliasRecords []AddressAliasRecord AddressAliasRecords []AddressAliasRecord
Contracts []ContractInfo Contracts []ContractInfo
} }
// StakingPool 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
}

View File

@ -32,7 +32,11 @@ export interface EthereumSpecific {
nonce: number; nonce: number;
gasLimit: number; gasLimit: number;
gasUsed?: number; gasUsed?: number;
gasPrice: string; gasPrice?: string;
l1Fee?: number;
l1FeeScalar?: string;
l1GasPrice?: string;
l1GasUsed?: number;
data?: string; data?: string;
parsedData?: EthereumParsedInputData; parsedData?: EthereumParsedInputData;
internalTransfers?: EthereumInternalTransfer[]; internalTransfers?: EthereumInternalTransfer[];
@ -46,9 +50,9 @@ export interface TokenTransfer {
from: string; from: string;
to: string; to: string;
contract: string; contract: string;
name: string; name?: string;
symbol: string; symbol?: string;
decimals: number; decimals?: number;
value?: string; value?: string;
multiTokenValues?: MultiTokenValue[]; multiTokenValues?: MultiTokenValue[];
} }
@ -109,6 +113,17 @@ export interface FeeStats {
averageFeePerKb: number; averageFeePerKb: number;
decilesFeePerKb: number[]; decilesFeePerKb: number[];
} }
export interface StakingPool {
contract: string;
name: string;
pendingBalance: string;
pendingDepositedBalance: string;
depositedBalance: string;
withdrawTotalAmount: string;
claimableAmount: string;
restakedReward: string;
autocompoundBalance: string;
}
export interface ContractInfo { export interface ContractInfo {
type: string; type: string;
contract: string; contract: string;
@ -161,6 +176,7 @@ export interface Address {
contractInfo?: ContractInfo; contractInfo?: ContractInfo;
erc20Contract?: ContractInfo; erc20Contract?: ContractInfo;
addressAliases?: { [key: string]: AddressAlias }; addressAliases?: { [key: string]: AddressAlias };
stakingPools?: StakingPool[];
} }
export interface Utxo { export interface Utxo {
txid: string; txid: string;
@ -245,6 +261,7 @@ export interface InternalStateColumn {
} }
export interface BlockbookInfo { export interface BlockbookInfo {
coin: string; coin: string;
network: string;
host: string; host: string;
version: string; version: string;
gitCommit: string; gitCommit: string;
@ -264,6 +281,7 @@ export interface BlockbookInfo {
currentFiatRatesTime?: string; currentFiatRatesTime?: string;
historicalFiatRatesTime?: string; historicalFiatRatesTime?: string;
historicalTokenFiatRatesTime?: string; historicalTokenFiatRatesTime?: string;
supportedStakingPools?: string[];
dbSizeFromColumns?: number; dbSizeFromColumns?: number;
dbColumns?: InternalStateColumn[]; dbColumns?: InternalStateColumn[];
about: string; about: string;
@ -338,6 +356,7 @@ export interface WsBackendInfo {
export interface WsInfoRes { export interface WsInfoRes {
name: string; name: string;
shortcut: string; shortcut: string;
network: string;
decimals: number; decimals: number;
version: string; version: string;
bestHeight: number; bestHeight: number;
@ -357,6 +376,17 @@ export interface WsBlockReq {
pageSize?: number; pageSize?: number;
page?: number; page?: number;
} }
export interface WsBlockFilterReq {
scriptType: string;
blockHash: string;
M?: number;
}
export interface WsBlockFiltersBatchReq {
scriptType: string;
bestKnownBlockHash: string;
pageSize?: number;
M?: number;
}
export interface WsAccountUtxoReq { export interface WsAccountUtxoReq {
descriptor: string; descriptor: string;
} }
@ -416,7 +446,17 @@ export interface WsFiatRatesTickersListReq {
export interface WsMempoolFiltersReq { export interface WsMempoolFiltersReq {
scriptType: string; scriptType: string;
fromTimestamp: number; fromTimestamp: number;
M?: number;
}
export interface WsRpcCallReq {
from?: string;
to: string;
data: string;
}
export interface WsRpcCallRes {
data: string;
} }
export interface MempoolTxidFilterEntries { export interface MempoolTxidFilterEntries {
entries?: { [key: string]: string }; entries?: { [key: string]: string };
usedZeroedKey?: boolean;
} }

View File

@ -2,7 +2,6 @@ package main
import ( import (
"context" "context"
"encoding/json"
"flag" "flag"
"log" "log"
"math/rand" "math/rand"
@ -11,6 +10,7 @@ import (
"os" "os"
"os/signal" "os/signal"
"runtime/debug" "runtime/debug"
"strconv"
"strings" "strings"
"syscall" "syscall"
"time" "time"
@ -152,30 +152,19 @@ func mainWithExitCode() int {
return exitCodeOK return exitCodeOK
} }
if *configFile == "" { config, err := common.GetConfig(*configFile)
glog.Error("Missing blockchaincfg configuration parameter")
return exitCodeFatal
}
configFileContent, err := os.ReadFile(*configFile)
if err != nil {
glog.Errorf("Error reading file %v, %v", configFile, err)
return exitCodeFatal
}
coin, coinShortcut, coinLabel, err := coins.GetCoinNameFromConfig(configFileContent)
if err != nil { if err != nil {
glog.Error("config: ", err) glog.Error("config: ", err)
return exitCodeFatal return exitCodeFatal
} }
metrics, err = common.GetMetrics(coin) metrics, err = common.GetMetrics(config.CoinName)
if err != nil { if err != nil {
glog.Error("metrics: ", err) glog.Error("metrics: ", err)
return exitCodeFatal return exitCodeFatal
} }
if chain, mempool, err = getBlockChainWithRetry(coin, *configFile, pushSynchronizationHandler, metrics, 120); err != nil { if chain, mempool, err = getBlockChainWithRetry(config.CoinName, *configFile, pushSynchronizationHandler, metrics, 120); err != nil {
glog.Error("rpc: ", err) glog.Error("rpc: ", err)
return exitCodeFatal return exitCodeFatal
} }
@ -187,7 +176,7 @@ func mainWithExitCode() int {
} }
defer index.Close() defer index.Close()
internalState, err = newInternalState(coin, coinShortcut, coinLabel, index, *enableSubNewTx) internalState, err = newInternalState(config, index, *enableSubNewTx)
if err != nil { if err != nil {
glog.Error("internalState: ", err) glog.Error("internalState: ", err)
return exitCodeFatal return exitCodeFatal
@ -279,7 +268,7 @@ func mainWithExitCode() int {
return exitCodeFatal return exitCodeFatal
} }
if fiatRates, err = fiat.NewFiatRates(index, configFileContent, metrics, onNewFiatRatesTicker); err != nil { if fiatRates, err = fiat.NewFiatRates(index, config, metrics, onNewFiatRatesTicker); err != nil {
glog.Error("fiatRates ", err) glog.Error("fiatRates ", err)
return exitCodeFatal return exitCodeFatal
} }
@ -368,7 +357,7 @@ func mainWithExitCode() int {
if internalServer != nil || publicServer != nil || chain != nil { if internalServer != nil || publicServer != nil || chain != nil {
// start fiat rates downloader only if not shutting down immediately // start fiat rates downloader only if not shutting down immediately
initDownloaders(index, chain, configFileContent) initDownloaders(index, chain, config)
waitForSignalAndShutdown(internalServer, publicServer, chain, 10*time.Second) waitForSignalAndShutdown(internalServer, publicServer, chain, 10*time.Second)
} }
@ -501,16 +490,12 @@ func blockbookAppInfoMetric(db *db.RocksDB, chain bchain.BlockChain, txCache *db
return nil return nil
} }
func newInternalState(coin, coinShortcut, coinLabel string, d *db.RocksDB, enableSubNewTx bool) (*common.InternalState, error) { func newInternalState(config *common.Config, d *db.RocksDB, enableSubNewTx bool) (*common.InternalState, error) {
is, err := d.LoadInternalState(coin) is, err := d.LoadInternalState(config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
is.CoinShortcut = coinShortcut
if coinLabel == "" {
coinLabel = coin
}
is.CoinLabel = coinLabel
is.EnableSubNewTx = enableSubNewTx is.EnableSubNewTx = enableSubNewTx
name, err := os.Hostname() name, err := os.Hostname()
if err != nil { if err != nil {
@ -521,6 +506,12 @@ func newInternalState(coin, coinShortcut, coinLabel string, d *db.RocksDB, enabl
} }
is.Host = name is.Host = name
} }
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)
}
return is, nil return is, nil
} }
@ -702,21 +693,11 @@ func computeFeeStats(stopCompute chan os.Signal, blockFrom, blockTo int, db *db.
return err return err
} }
func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, configFileContent []byte) { func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, config *common.Config) {
if fiatRates.Enabled { if fiatRates.Enabled {
go fiatRates.RunDownloader() go fiatRates.RunDownloader()
} }
var config struct {
FourByteSignatures string `json:"fourByteSignatures"`
}
err := json.Unmarshal(configFileContent, &config)
if err != nil {
glog.Errorf("Error parsing config file %v, %v", *configFile, err)
return
}
if config.FourByteSignatures != "" && chain.GetChainParser().GetChainType() == bchain.ChainEthereumType { if config.FourByteSignatures != "" && chain.GetChainParser().GetChainType() == bchain.ChainEthereumType {
fbsd, err := fourbyte.NewFourByteSignaturesDownloader(db, config.FourByteSignatures) fbsd, err := fourbyte.NewFourByteSignaturesDownloader(db, config.FourByteSignatures)
if err != nil { if err != nil {

View File

@ -11,7 +11,7 @@ RUN apt-get update && \
libzstd-dev liblz4-dev graphviz && \ libzstd-dev liblz4-dev graphviz && \
apt-get clean apt-get clean
ARG GOLANG_VERSION ARG GOLANG_VERSION
ENV GOLANG_VERSION=go1.19.2 ENV GOLANG_VERSION=go1.22.8
ENV ROCKSDB_VERSION=v7.7.2 ENV ROCKSDB_VERSION=v7.7.2
ENV GOPATH=/go ENV GOPATH=/go
ENV PATH=$PATH:$GOPATH/bin ENV PATH=$PATH:$GOPATH/bin

View File

@ -38,5 +38,4 @@ prepare-sources:
mkdir -p $(BLOCKBOOK_BASE) mkdir -p $(BLOCKBOOK_BASE)
cp -r /src $(BLOCKBOOK_SRC) cp -r /src $(BLOCKBOOK_SRC)
cd $(BLOCKBOOK_SRC) && go mod download cd $(BLOCKBOOK_SRC) && go mod download
sed -i 's/wsMessageSizeLimit\ =\ 15\ \*\ 1024\ \*\ 1024/wsMessageSizeLimit = 80 * 1024 * 1024/g' $(GOPATH)/pkg/mod/github.com/ethereum/go-ethereum*/rpc/websocket.go
sed -i 's/wsMessageSizeLimit\ =\ 15\ \*\ 1024\ \*\ 1024/wsMessageSizeLimit = 80 * 1024 * 1024/g' $(GOPATH)/pkg/mod/github.com/ava-labs/coreth*/rpc/websocket.go sed -i 's/wsMessageSizeLimit\ =\ 15\ \*\ 1024\ \*\ 1024/wsMessageSizeLimit = 80 * 1024 * 1024/g' $(GOPATH)/pkg/mod/github.com/ava-labs/coreth*/rpc/websocket.go

View File

@ -6,9 +6,19 @@ ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \ RUN apt-get update && \
apt-get upgrade -y && \ apt-get upgrade -y && \
apt-get install -y devscripts debhelper make dh-exec && \ apt-get install -y devscripts debhelper make dh-exec zstd && \
apt-get clean 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 ADD gpg-keys /tmp/gpg-keys
RUN gpg --batch --import /tmp/gpg-keys/* RUN gpg --batch --import /tmp/gpg-keys/*

View File

@ -1,5 +1,103 @@
-----BEGIN PGP PUBLIC KEY BLOCK----- -----BEGIN PGP PUBLIC KEY BLOCK-----
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
FlhMhNHad7rz5hFAz8YkYEGX/bctF2y/gmHnu/xKkQsOlV+fQfROOlo/wQ/2vXRY
YBqWrVw0gAFDaI4P43CoKlYFzZOxrX+RLSc6eOSgmRkwMx5NzpOvfbypuiXLCmed
8pTF9SeXH3LzdO1gJQsKkia04OBohCosmnIjOCjeN3bxf606HZpBgXhj72kXZOX0
NeA+yxEh1QIhvjxvD0WyIUChaXYsGy61F16vIUytE319diU/e/KQKnTC+oepiju6
N23Iy8c2gRux48ghkmcN58bLOCUUvO+UYb7U9YYsi6HEiL8yd8KVPHVJ293NcMt0
FsmxFd4Fddr2HYK0NLtf5MDo4yYMw2PmbQ/1/cy/Sr6BvlHmZ6R9+I9beO5LjPBQ
EN62PWWBfl6b2EpYyA9RTFUKFiRhEoqLpmORlzMcUcmIsIYX5ZWanitBnSnIznGe
TapoOXPE93OrpDJU9vIcYx7Y4E8drNAdW1zZcFBo9ilNexq0i1Bhc3RhIFl1Ymlr
ZXkgKFRoaXMgaXMgYW4gb2ZmbGluZSBvbmx5IGtleSB1c2VkIGZvciB0aGUgaGln
aGVzdCBsZXZlbCBvZiB2YWxpZGF0aW9uLiBNeSBtYWluIGtleSBpcyA1MjUyN0JF
REFCRTg3OTg0LiBTZWUga2V5YmFzZS5pby9wYXN0YSmJAWUEEwEIAE8CGwMFCwkI
BwIGFQoJCAsCBRYCAwEAAh4EFiEEYKz3C/cSZFBJ7m8V7+rxZoYiX2QFAmWp/HEY
GGhrcHM6Ly9rZXlzLm9wZW5wZ3Aub3JnAAoJEO/q8WaGIl9kVUYH/2HrXiEHYIZU
NojBSKzBqWUSoXjvN1lITo7WSzdg/saQLtIBuEWwVtZKGH9HcRpi93glAZk+0xeO
Twke4fEAeEiYS3U3t+GqqH5bo4aJD1+EedvpjM5PVhtDyM4VVw8wu/29Tl7lIZQ9
57Un1dwuYrsO6BEmKWmnV31XpN7JMd4qIAIeQoN9NMOFBT2PS7LXiIUZ36TH3ZAP
hgbec/MhgCQW//KmMd6lqVCNhjJ4ggYeifsAhFo/xMMYxbpFZXkYkpMxziZoG7MT
gQLR2YQEVQm9rQOjdn4IOWN6qoEtxx/82mMq/JynGeMXMyt4rgdSpcjTgnBlKMBv
DU2FF+hvMWiJAjMEEAEIAB0WIQQpWQNi7IeKgf08ICtSUnvtq+h5hAUCZaoDowAK
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
gZfWLfoIul+B10SwJHXuoQ==
=/A3N
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBF1ULyUBEADFFliU0Hr+PRCQNT9/9ZEhZtLmMMu7tai3VCxhmrHrOpNJJHqX mQINBF1ULyUBEADFFliU0Hr+PRCQNT9/9ZEhZtLmMMu7tai3VCxhmrHrOpNJJHqX
f1/CeUyBhmCvXpKIpAAbH66l/Uc9GH5UgMZ19gMyGa3q3QJn9A6RR9ud4ALRg60P f1/CeUyBhmCvXpKIpAAbH66l/Uc9GH5UgMZ19gMyGa3q3QJn9A6RR9ud4ALRg60P
fmYTAci+6Luko7bqTzkS+fYOUSy/LY57s5ANTpveE+iTsBd5grXczCxaYYnthKKA fmYTAci+6Luko7bqTzkS+fYOUSy/LY57s5ANTpveE+iTsBd5grXczCxaYYnthKKA
@ -11,55 +109,317 @@ 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
96I= xITv/DddKRjYc7fPRlrje7H19+urJgqqkWzmuUbNlxKiXiVW/OPmCjjI89Okt3dZ
=85Kq GCTicEAPzJ6LTpoVgo4n/Eu81nMm6caf++Pzz1vEI3bJdPHPYyI+gN64mEhfP4OJ
-----END PGP PUBLIC KEY BLOCK----- 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
NAgNcAK3mOfpQ/ej25jgI5w=
=LIEu
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGKiMDcBEAC5eXHp6VV0fEBsHvqy2AGTuNAf9Zv7ux5GDT65XM1UuoXqhS0q
EGeijp1a70ndQ8TugzGSzoYT9W5xHPvgzDFpKAsiL1fELljnxd8KSl+3KKEX+QLK
1GHVDyLZTpL+vx+Nmb8920kqUMDLIeb/+TrbxGyWyOt8DFcCuigwNqsIVb5EMG/m
cbpO6pPiMahXZxmW1Hb8Pa047BC5kX2Qy07c/HhDAMyPp8C1xjyusgB+w7mSILzc
/n94CETPUztZbLEL+H9cUPFpSXEm53ZJ9MJIt2/eFYBVZ1XEU2hi341/mv2VPAiN
lUqoESJuim+OECPTUPdS8WLV5bmIAkyLj8uhArA1JpX6QwnhPuxCgptg00oHvmy0
+DAR4DoIU1jndOIU/go78CHGIg3MrtOrXOvarKIairsX0sczRrdedZx9o1JoOiCt
K6k/lK5cYoH/WMiq3DvIUizOboH9jTj5DRXPoX/0eilGRNgRkpX3E1CbUJimiggx
6sgaC13RIaP/8tb9XQVUDsqaXHVAASZHwq4lAu1VjIh//IwQe++Qgr/k3gtkX3Hd
TvC9/Npx4pyODBGxk8KhJDHBeTP7vwl/VtYGD2XUtV/UfRGIvx93VYicsS04QlOu
3oSEzX6ayFOhwZxlbi/KY65xBMfuWw+zTs2qXbPWPkLuMZUOu0KN3oEQKwARAQAB
tCRLb25zdGFudGluIEFraW1vdiA8a25zdHFxQGdtYWlsLmNvbT6JAjgEEwEIACwF
AmKiMDcJECF2xKXQHqUkAhsDBQkeEzgAAhkBBAsHCQMFFQgKAgMEFgABAgAAYAIP
/i/mjLqeJI4l5WUckyocqALaQhe9pAX6JEk0gOlEuIgH9N/cl8fuEEv8j51TNIh2
EQQZoNM//9Kj1dMxoy9Wtkh1yFe5OT9tKXkaXNwVeox45OqXYs/ARJ/rDUt1BNXu
Nbhdh5+OAYbFltF33JdfLXMRK22LoSOXPn1opEH1Zu6HS40lXl06CVqa7m3gvLY3
BC/9pi8bSow/INnpJPjavtSA2uLLtRQRaqXs0iwF2FkyAKmAT7zANCA1pkBVMa7E
W+ulP0cr5/nqIPKIBfZxYmqE4YvN3px3JBNtzj7cdC3hAn1km1thOWSaBzb9lXLT
eXSHSRgG6AY2GdfC3F5UC6g6rEIncEJ8drfnTPpMLvXF3+KZ0ssdbLG9ctfev6X+
lKS+TFEZs7TCANa1lEPr/ISCQYBbL63+xAbIz9SXG07jH6aFF07j6I3h+bWvZTJn
GIj2pq3QxBwh/pYf6hICxYU+fDP67mhlYor7yNIT83W+Ik4IhbLj9AtiW05NIavx
HPrEeYbjovsGWUhvN1LCAO7GFgmcTyQIqDDtLYLxLjnvjptc8HlKh4WW7KqCVawt
GayAcYYQXePDxerkiR0y6jCUSzr3MR8c9yfYarieQVKQLJTDP0UDYnXd20dlvzR9
Q7wCbwu6jb0EcRDcnbZg8K8gOu1N2gfyFnesz3rq+PCAtC5Lb25zdGFudGluIEFr
aW1vdiA8a29uc3RhbnRpbi5ha2ltb3ZAZGFzaC5vcmc+iQJUBBMBCgA+FiEEFRkd
BbXPlW/jfJWWIXbEpdAepSQFAmKiSfACGwMFCR4TOAAFCwkIBwIGFQoJCAsCBBYC
AwECHgECF4AACgkQIXbEpdAepSTsahAAlq+6OBs1BL7k0drcK3hzN22y3E1LzBEK
mpxeIJ+eHDMerhVoSuDM75fwWk6SXoKxaRRErQ2EP3a5jDfu8MGD2xDypEcMLvE+
EcFT3M2X79w/+MduR8cp9lUd0NCwpI7zAANq7Mj6gLDFdKEnA8pe730sHZB9I4G9
vZl961FqzFUMwMttl8KxTzMKnbH/u5Tsvybh/dsv0lcV10irDuCoGGIM/MP42Hul
9CO3bAs69KXA30r/711ooAL3cpw5J5CeMvV2N5GnE656Cl9wRl6rCOSNoaRNJG4t
KtfNZeDd6na6+fABFnOYzzG/kd1+OcmfCFK79ljtL92b7cJzSkoOXfLYvM+V7UN4
AohH8Lmon4MzGjieBFitHOOUMQy80hBEhuliajtFTv6JB4wS1K5U0NzNKjvLbUhQ
e+iabtChSAtYr3/liDALdROXyrEzAHYxK8Q5ZWdE9wUIz2HcQpHiFt0L33Y4lA8V
Dm26fi02svgHg5SBGGwQ65hSlzIQgmASaogoW3cYPOqVveibcGlM0bxM+0MN3QR6
0T98PmqcdUV6S+xUkR1LI+5bj7ObzOusc0UGM8m4GQ+DdY46UqInc4yFrgLPzoj4
QZPwn7aMRFbBF8YSTh7Cr4XvAx8CP2Abau8Sm6YHxXaausKRKaT4eKQlxryGkKdD
sQO3K/PaBWu5Ag0EYqIwNwEQAMaVJMN/2qrJUQnZgoOTcAmjKKUxphnGR27jqVKh
wTT3JW0qEap4ZUF0o6dJTHA0Ni2FltsGMddfyE++ipDgpW/+q9pFE6rs/eUufBX2
yeYpf/4CSh1rZ6zqXqBQeifEflhEC1PXI+LGFOUyjuR5DV7cHw/i74UWXpUy8zT6
RGyExSecmqNu9/6zCMnlNsfCAIfurwtrS6RdsYbvxSGWkNOnqkJ6zxOKgmtlOkeL
eNTxk4Oq+o7vPVh0zK/o5owMGpJzef1myMbB5H1aWeM5ReHf4y0VYCpR/IKhVCMm
qrgg70iGDLeeGaB3KFrCyhkFz/hBEcaL4juglQUq9CsfT+bbHWEoQS8jVBekRJi6
iObupFIibC+W26p4/d5IYmYfU5gKMxPkfFoSokFGeICb8i35Rshv17vvt/Z/MXvk
RcGLAM2ydDtl5VG/h2dH1Dk9CLE3xa6AgtpIyUot+Y5VU1PC5p6gyD7WEo/dSVw7
AJlgFKIx/UM3wx9MlVm5rB7sHvwnjaUcCTuBRtVitsmWsY/5N0K+qxGj/S1hKWQX
rmT+0K4/sRHfwv3lnFdeocq4hKfcmfhJJXoGXDL/jn/2ml2Oi+0hl0Mtds85MeJR
FwHETjhi/F5xAu9IgKJv/ewomKo8hwk0yHiNm46CCjHb7XmoIzz3e08z73pODzin
2WX7ABEBAAGJAjUEGAEIACkFAmKiMDcJECF2xKXQHqUkAhsMBQkeEzgABAsHCQMF
FQgKAgMEFgABAgAAT1cP/RStJ3oBrHGWB0fjPCfyossmgSeUKo4it+dHqNPTumIj
Zyy5p4FAhFsYeSQwoqlrNgZgt0MZxWQjvV6vNKqx0DXVR5S+xilPI8vpRSfnJhkI
vVdVY8qMj4I0/cyYqrasiR7YVIKepmEZe4aQTzhs/ifMooeY1+ZIwwLYollN91se
Nf3JqWmhY5Q7lPhUZXiyFNyE87geM1P4aOgwZm4EikEadzBFcoHzAXczSCpBwRxM
u53EQbz7Oq2xnFLORPAAwz9yJjCO/0N9HzH2o2Du6GeRccMeHZ65U8tQLvDO79Od
iWDZsU1h56BkDMhqTOsymHnv/QX4vO/X0tShhZXzLwe97++U+HUDobjcHmAySVNy
OugeGdFyYExMNM6Jd/GoS7Xo+RecBSP1yeDnweZgupCmzHVbrfRf7Vdesf6rY7hl
81amRIjdMlhWjOX8OxE4/u+npiQH+wT0VLOwTbxDNvGAAqzYzuETdNROiqqHGNXR
nc3pdm9EUvG/ur4AABDKllnsa0OP0oTOh+FqMQSlTEHwxPhlE11lyIIh2kkuNMmq
Vr7qNeOq3i6dA6EvGn2bikTsvHDw/kF0h08xZRTuy1I0Fcb6GYStM6Qskt4Hhrsa
xwuUTBELdLnf2nLk7sAoUl269juuWXTELTGC40olQh0m8bEXDinknhu6Jug3d0uW
=d05p
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=lTCd
-----END PGP PUBLIC KEY BLOCK-----

View File

@ -2,6 +2,16 @@
ARCHIVE := $(shell basename {{.Backend.BinaryURL}}) ARCHIVE := $(shell basename {{.Backend.BinaryURL}})
all: 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}} wget {{.Backend.BinaryURL}}
{{- if eq .Backend.VerificationType "gpg"}} {{- if eq .Backend.VerificationType "gpg"}}
wget {{.Backend.VerificationSource}} -O checksum wget {{.Backend.VerificationSource}} -O checksum
@ -13,8 +23,8 @@ all:
{{- else if eq .Backend.VerificationType "sha256"}} {{- else if eq .Backend.VerificationType "sha256"}}
[ "$$(sha256sum ${ARCHIVE} | cut -d ' ' -f 1)" = "{{.Backend.VerificationSource}}" ] [ "$$(sha256sum ${ARCHIVE} | cut -d ' ' -f 1)" = "{{.Backend.VerificationSource}}" ]
{{- end}} {{- end}}
mkdir backend
{{.Backend.ExtractCommand}} ${ARCHIVE} {{.Backend.ExtractCommand}} ${ARCHIVE}
{{- end}}
{{- if .Backend.ExcludeFiles}} {{- if .Backend.ExcludeFiles}}
# generated from exclude_files # generated from exclude_files
{{- range $index, $name := .Backend.ExcludeFiles}} {{- range $index, $name := .Backend.ExcludeFiles}}

View File

@ -12,8 +12,12 @@ zmqpubhashblock={{template "IPC.MessageQueueBindingTemplate" .}}
rpcworkqueue=1100 rpcworkqueue=1100
maxmempool=4096 maxmempool=4096
mempoolexpiry=8760 mempoolexpiry=8760
mempoolfullrbf=1
dbcache=1000 dbcache=1000
deprecatedrpc=warnings
{{- if .Backend.AdditionalParams}} {{- if .Backend.AdditionalParams}}
# generated from additional_params # generated from additional_params
{{- range $name, $value := .Backend.AdditionalParams}} {{- range $name, $value := .Backend.AdditionalParams}}

View File

@ -12,6 +12,8 @@ rpcworkqueue=1100
maxmempool=2000 maxmempool=2000
dbcache=1000 dbcache=1000
deprecatedrpc=warnings
{{- if .Backend.AdditionalParams}} {{- if .Backend.AdditionalParams}}
# generated from additional_params # generated from additional_params
{{- range $name, $value := .Backend.AdditionalParams}} {{- range $name, $value := .Backend.AdditionalParams}}

View File

@ -13,6 +13,8 @@ rpcworkqueue=1100
maxmempool=2000 maxmempool=2000
dbcache=1000 dbcache=1000
deprecatedrpc=warnings
{{- if .Backend.AdditionalParams}} {{- if .Backend.AdditionalParams}}
# generated from additional_params # generated from additional_params
{{- range $name, $value := .Backend.AdditionalParams}} {{- range $name, $value := .Backend.AdditionalParams}}

View File

@ -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}}

View File

@ -19,7 +19,7 @@ Type=simple
{{template "Backend.ServiceAdditionalParamsTemplate" .}} {{template "Backend.ServiceAdditionalParamsTemplate" .}}
# Resource limits # Resource limits
LimitNOFILE=500000 LimitNOFILE=2000000
# Hardening measures # Hardening measures
#################### ####################

View File

@ -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}}

View File

@ -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}}

View File

@ -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}}

View File

@ -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}}

View File

@ -0,0 +1,44 @@
#!/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://r2-snapshots.fastnode.io/op/$(curl -s https://r2-snapshots.fastnode.io/op/latest-mainnet)
if [ ! -d "$CHAINDATA_DIR" ]; then
wget -c $SNAPSHOT -O - | lz4 -cd | tar xf - -C $DATA_DIR
fi
$GETH_BIN \
--op-network op-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 \
--txlookuplimit 0 \
--cache 4096 \
--syncmode full \
--maxpeers 0 \
--nodiscover
{{end}}

View File

@ -0,0 +1,48 @@
#!/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://datadirs.optimism.io/latest
if [ ! -d "$CHAINDATA_DIR" ]; then
wget -c $(curl -sL $SNAPSHOT | grep -oP '(?<=url=)[^"]*') -O - | zstd -cd | tar xf - -C $DATA_DIR
fi
$GETH_BIN \
--op-network op-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.historicalrpc http://127.0.0.1:8304 \
--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}}

View File

@ -0,0 +1,40 @@
#!/bin/sh
{{define "main" -}}
set -e
export USING_OVM=true
export ETH1_SYNC_SERVICE_ENABLE=false
GETH_BIN={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth
DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend
CHAINDATA_DIR=$DATA_DIR/geth/chaindata
SNAPSHOT=https://datadirs.optimism.io/mainnet-legacy-archival.tar.zst
if [ ! -d "$CHAINDATA_DIR" ]; then
wget -c $SNAPSHOT -O - | zstd -cd | tar xf - -C $DATA_DIR
fi
$GETH_BIN \
--networkid 10 \
--datadir $DATA_DIR \
--port {{.Ports.BackendP2P}} \
--rpc \
--rpcport {{.Ports.BackendHttp}} \
--rpcaddr 127.0.0.1 \
--rpcapi eth,rollup,net,web3,debug \
--rpcvhosts "*" \
--rpccorsdomain "*" \
--ws \
--wsport {{.Ports.BackendRPC}} \
--wsaddr 0.0.0.0 \
--wsapi eth,rollup,net,web3,debug \
--wsorigins "*" \
--nousb \
--ipcdisable \
--nat=none \
--nodiscover
{{end}}

View File

@ -0,0 +1,24 @@
#!/bin/sh
{{define "main" -}}
set -e
BIN={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/op-node
PATH={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend
$BIN \
--network op-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:8402 \
--rpc.addr 127.0.0.1 \
--rpc.port {{.Ports.BackendRPC}} \
--l2.jwt-secret {{.Env.BackendDataPath}}/optimism_archive/backend/jwtsecret \
--p2p.priv.path $PATH/opnode_p2p_priv.txt \
--p2p.peerstore.path $PATH/opnode_peerstore_db \
--p2p.discovery.path $PATH/opnode_discovery_db
{{end}}

View File

@ -0,0 +1,24 @@
#!/bin/sh
{{define "main" -}}
set -e
BIN={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/op-node
PATH={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend
$BIN \
--network op-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:8400 \
--rpc.addr 127.0.0.1 \
--rpc.port {{.Ports.BackendRPC}} \
--l2.jwt-secret {{.Env.BackendDataPath}}/optimism/backend/jwtsecret \
--p2p.priv.path $PATH/opnode_p2p_priv.txt \
--p2p.peerstore.path $PATH/opnode_peerstore_db \
--p2p.discovery.path $PATH/opnode_discovery_db
{{end}}

View File

@ -0,0 +1,35 @@
#!/bin/sh
{{define "main" -}}
set -e
INSTALL_DIR={{.Env.BackendInstallPath}}/{{.Coin.Alias}}
DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend
BOR_BIN=$INSTALL_DIR/bor
# --bor.heimdall = backend-polygon-heimdall-archive ports.backend_http
$BOR_BIN server \
--chain $INSTALL_DIR/genesis.json \
--syncmode full \
--datadir $DATA_DIR \
--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 \
--port {{.Ports.BackendP2P}} \
--http \
--http.addr 0.0.0.0 \
--http.port {{.Ports.BackendHttp}} \
--http.api eth,net,web3,debug,txpool,bor \
--http.vhosts '*' \
--http.corsdomain '*' \
--ws \
--ws.addr 0.0.0.0 \
--ws.port {{.Ports.BackendRPC}} \
--ws.api eth,net,web3,debug,txpool,bor \
--ws.origins '*' \
--gcmode archive \
--txlookuplimit 0 \
--cache 4096
{{end}}

View File

@ -0,0 +1,36 @@
#!/bin/sh
{{define "main" -}}
set -e
INSTALL_DIR={{.Env.BackendInstallPath}}/{{.Coin.Alias}}
DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend
HEIMDALL_BIN=$INSTALL_DIR/heimdalld
HOME_DIR=$DATA_DIR/heimdalld
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
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}} \
--bor_rpc_url http://127.0.0.1:8172 \
--eth_rpc_url http://127.0.0.1:8116 \
--rest-server
{{end}}

View File

@ -0,0 +1,35 @@
#!/bin/sh
{{define "main" -}}
set -e
INSTALL_DIR={{.Env.BackendInstallPath}}/{{.Coin.Alias}}
DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend
BOR_BIN=$INSTALL_DIR/bor
# --bor.heimdall = backend-polygon-heimdall ports.backend_http
$BOR_BIN server \
--chain $INSTALL_DIR/genesis.json \
--syncmode full \
--datadir $DATA_DIR \
--bor.heimdall http://127.0.0.1:8171 \
--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 \
--port {{.Ports.BackendP2P}} \
--http \
--http.addr 127.0.0.1 \
--http.port {{.Ports.BackendHttp}} \
--http.api eth,net,web3,debug,txpool,bor \
--http.vhosts '*' \
--http.corsdomain '*' \
--ws \
--ws.addr 127.0.0.1 \
--ws.port {{.Ports.BackendRPC}} \
--ws.api eth,net,web3,debug,txpool,bor \
--ws.origins '*' \
--txlookuplimit 0 \
--cache 4096
{{end}}

View File

@ -0,0 +1,36 @@
#!/bin/sh
{{define "main" -}}
set -e
INSTALL_DIR={{.Env.BackendInstallPath}}/{{.Coin.Alias}}
DATA_DIR={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend
HEIMDALL_BIN=$INSTALL_DIR/heimdalld
HOME_DIR=$DATA_DIR/heimdalld
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
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}} \
--bor_rpc_url http://127.0.0.1:8170 \
--eth_rpc_url http://127.0.0.1:8136 \
--rest-server
{{end}}

View File

@ -7,6 +7,8 @@
{{end}} {{end}}
"coin_name": "{{.Coin.Name}}", "coin_name": "{{.Coin.Name}}",
"coin_shortcut": "{{.Coin.Shortcut}}", "coin_shortcut": "{{.Coin.Shortcut}}",
{{- if .Coin.Network}}
"network": "{{.Coin.Network}}",{{end}}
"coin_label": "{{.Coin.Label}}", "coin_label": "{{.Coin.Label}}",
"rpc_url": "{{template "IPC.RPCURLTemplate" .}}", "rpc_url": "{{template "IPC.RPCURLTemplate" .}}",
"rpc_user": "{{.IPC.RPCUser}}", "rpc_user": "{{.IPC.RPCUser}}",

View File

@ -21,6 +21,7 @@ type Backend struct {
SystemUser string `json:"system_user"` SystemUser string `json:"system_user"`
Version string `json:"version"` Version string `json:"version"`
BinaryURL string `json:"binary_url"` BinaryURL string `json:"binary_url"`
DockerImage string `json:"docker_image"`
VerificationType string `json:"verification_type"` VerificationType string `json:"verification_type"`
VerificationSource string `json:"verification_source"` VerificationSource string `json:"verification_source"`
ExtractCommand string `json:"extract_command"` ExtractCommand string `json:"extract_command"`
@ -44,6 +45,7 @@ type Config struct {
Coin struct { Coin struct {
Name string `json:"name"` Name string `json:"name"`
Shortcut string `json:"shortcut"` Shortcut string `json:"shortcut"`
Network string `json:"network,omitempty"`
Label string `json:"label"` Label string `json:"label"`
Alias string `json:"alias"` Alias string `json:"alias"`
} `json:"coin"` } `json:"coin"`
@ -203,6 +205,7 @@ func LoadConfig(configsDir, coin string) (*Config, error) {
case "gpg": case "gpg":
case "sha256": case "sha256":
case "gpg-sha256": case "gpg-sha256":
case "docker":
default: default:
return nil, fmt.Errorf("Invalid verification type: %s", config.Backend.VerificationType) return nil, fmt.Errorf("Invalid verification type: %s", config.Backend.VerificationType)
} }

View File

@ -1,4 +1,4 @@
//usr/bin/go run $0 $@ ; exit // usr/bin/go run $0 $@ ; exit
package main package main
import ( import (

View File

@ -45,6 +45,8 @@ func main() {
t.Add(server.WsBlockHashReq{}) t.Add(server.WsBlockHashReq{})
t.Add(server.WsBlockHashRes{}) t.Add(server.WsBlockHashRes{})
t.Add(server.WsBlockReq{}) t.Add(server.WsBlockReq{})
t.Add(server.WsBlockFilterReq{})
t.Add(server.WsBlockFiltersBatchReq{})
t.Add(server.WsAccountUtxoReq{}) t.Add(server.WsAccountUtxoReq{})
t.Add(server.WsBalanceHistoryReq{}) t.Add(server.WsBalanceHistoryReq{})
t.Add(server.WsTransactionReq{}) t.Add(server.WsTransactionReq{})
@ -58,6 +60,8 @@ func main() {
t.Add(server.WsFiatRatesForTimestampsReq{}) t.Add(server.WsFiatRatesForTimestampsReq{})
t.Add(server.WsFiatRatesTickersListReq{}) t.Add(server.WsFiatRatesTickersListReq{})
t.Add(server.WsMempoolFiltersReq{}) t.Add(server.WsMempoolFiltersReq{})
t.Add(server.WsRpcCallReq{})
t.Add(server.WsRpcCallRes{})
t.Add(bchain.MempoolTxidFilterEntries{}) t.Add(bchain.MempoolTxidFilterEntries{})
err := t.ConvertToFile("blockbook-api.ts") err := t.ConvertToFile("blockbook-api.ts")

42
common/config.go Normal file
View File

@ -0,0 +1,42 @@
package common
import (
"encoding/json"
"os"
"github.com/juju/errors"
)
// Config struct
type Config struct {
CoinName string `json:"coin_name"`
CoinShortcut string `json:"coin_shortcut"`
CoinLabel string `json:"coin_label"`
Network string `json:"network"`
FourByteSignatures string `json:"fourByteSignatures"`
FiatRates string `json:"fiat_rates"`
FiatRatesParams string `json:"fiat_rates_params"`
FiatRatesVsCurrencies string `json:"fiat_rates_vs_currencies"`
BlockGolombFilterP uint8 `json:"block_golomb_filter_p"`
BlockFilterScripts string `json:"block_filter_scripts"`
BlockFilterUseZeroedKey bool `json:"block_filter_use_zeroed_key"`
}
// GetConfig loads and parses the config file and returns Config struct
func GetConfig(configFile string) (*Config, error) {
if configFile == "" {
return nil, errors.New("Missing blockchaincfg configuration parameter")
}
configFileContent, err := os.ReadFile(configFile)
if err != nil {
return nil, errors.Errorf("Error reading file %v, %v", configFile, err)
}
var cn Config
err = json.Unmarshal(configFileContent, &cn)
if err != nil {
return nil, errors.Annotatef(err, "Error parsing config file ")
}
return &cn, nil
}

View File

@ -57,6 +57,7 @@ type InternalState struct {
CoinShortcut string `json:"coinShortcut"` CoinShortcut string `json:"coinShortcut"`
CoinLabel string `json:"coinLabel"` CoinLabel string `json:"coinLabel"`
Host string `json:"host"` Host string `json:"host"`
Network string `json:"network,omitempty"`
DbState uint32 `json:"dbState"` DbState uint32 `json:"dbState"`
ExtendedIndex bool `json:"extendedIndex"` ExtendedIndex bool `json:"extendedIndex"`
@ -92,6 +93,15 @@ type InternalState struct {
// database migrations // database migrations
UtxoChecked bool `json:"utxoChecked"` UtxoChecked bool `json:"utxoChecked"`
SortedAddressContracts bool `json:"sortedAddressContracts"` SortedAddressContracts bool `json:"sortedAddressContracts"`
// golomb filter settings
BlockGolombFilterP uint8 `json:"block_golomb_filter_p"`
BlockFilterScripts string `json:"block_filter_scripts"`
BlockFilterUseZeroedKey bool `json:"block_filter_use_zeroed_key"`
// allowed number of fetched accounts over websocket
WsGetAccountInfoLimit int `json:"-"`
WsLimitExceedingIPs map[string]int `json:"-"`
} }
// StartedSync signals start of synchronization // StartedSync signals start of synchronization
@ -296,6 +306,15 @@ func (is *InternalState) computeAvgBlockPeriod() {
is.AvgBlockPeriod = (is.BlockTimes[last] - is.BlockTimes[first]) / avgBlockPeriodSample is.AvgBlockPeriod = (is.BlockTimes[last] - is.BlockTimes[first]) / avgBlockPeriodSample
} }
// GetNetwork returns network. If not set returns the same value as CoinShortcut
func (is *InternalState) GetNetwork() string {
network := is.Network
if network == "" {
return is.CoinShortcut
}
return network
}
// SetBackendInfo sets new BackendInfo // SetBackendInfo sets new BackendInfo
func (is *InternalState) SetBackendInfo(bi *BackendInfo) { func (is *InternalState) SetBackendInfo(bi *BackendInfo) {
is.mux.Lock() is.mux.Lock()
@ -336,3 +355,15 @@ func SetInShutdown() {
func IsInShutdown() bool { func IsInShutdown() bool {
return atomic.LoadInt32(&inShutdown) != 0 return atomic.LoadInt32(&inShutdown) != 0
} }
func (is *InternalState) AddWsLimitExceedingIP(ip string) {
is.mux.Lock()
defer is.mux.Unlock()
is.WsLimitExceedingIPs[ip] = is.WsLimitExceedingIPs[ip] + 1
}
func (is *InternalState) ResetWsLimitExceedingIPs() {
is.mux.Lock()
defer is.mux.Unlock()
is.WsLimitExceedingIPs = make(map[string]int)
}

View File

@ -6,7 +6,9 @@ import (
) )
// JSONNumber is used instead of json.Number after upgrade to go 1.14 // 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 // see https://github.com/golang/go/issues/37308
type JSONNumber string type JSONNumber string

View File

@ -72,7 +72,7 @@ func GetMetrics(coin string) (*Metrics, error) {
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Name: "blockbook_socketio_req_duration", Name: "blockbook_socketio_req_duration",
Help: "Socketio request duration by method (in microseconds)", Help: "Socketio request duration by method (in microseconds)",
Buckets: []float64{1, 5, 10, 25, 50, 75, 100, 250}, Buckets: []float64{10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_0000_000},
ConstLabels: Labels{"coin": coin}, ConstLabels: Labels{"coin": coin},
}, },
[]string{"method"}, []string{"method"},
@ -104,7 +104,7 @@ func GetMetrics(coin string) (*Metrics, error) {
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Name: "blockbook_websocket_req_duration", Name: "blockbook_websocket_req_duration",
Help: "Websocket request duration by method (in microseconds)", Help: "Websocket request duration by method (in microseconds)",
Buckets: []float64{1, 5, 10, 25, 50, 75, 100, 250}, Buckets: []float64{10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_0000_000},
ConstLabels: Labels{"coin": coin}, ConstLabels: Labels{"coin": coin},
}, },
[]string{"method"}, []string{"method"},
@ -113,7 +113,7 @@ func GetMetrics(coin string) (*Metrics, error) {
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Name: "blockbook_index_resync_duration", Name: "blockbook_index_resync_duration",
Help: "Duration of index resync operation (in milliseconds)", Help: "Duration of index resync operation (in milliseconds)",
Buckets: []float64{50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 600, 700, 1000, 2000, 5000}, Buckets: []float64{10, 100, 500, 1000, 2000, 5000, 10000},
ConstLabels: Labels{"coin": coin}, ConstLabels: Labels{"coin": coin},
}, },
) )
@ -121,7 +121,7 @@ func GetMetrics(coin string) (*Metrics, error) {
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Name: "blockbook_mempool_resync_duration", Name: "blockbook_mempool_resync_duration",
Help: "Duration of mempool resync operation (in milliseconds)", Help: "Duration of mempool resync operation (in milliseconds)",
Buckets: []float64{10, 25, 50, 75, 100, 150, 250, 500, 750, 1000, 2000, 5000}, Buckets: []float64{10, 100, 500, 1000, 2000, 5000, 10000},
ConstLabels: Labels{"coin": coin}, ConstLabels: Labels{"coin": coin},
}, },
) )

View File

@ -0,0 +1,66 @@
{
"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": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"arbitrum-one\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}"
}
}
},
"meta": {
"package_maintainer": "IT",
"package_maintainer_email": "it@satoshilabs.com"
}
}

View File

@ -0,0 +1,68 @@
{
"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": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"arbitrum-one\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}",
"fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/"
}
}
},
"meta": {
"package_maintainer": "IT",
"package_maintainer_email": "it@satoshilabs.com"
}
}

View File

@ -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": "{\"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}"
}
}
},
"meta": {
"package_maintainer": "IT",
"package_maintainer_email": "it@satoshilabs.com"
}
}

View File

@ -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": "{\"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"
}
}

View File

@ -19,10 +19,10 @@
"package_name": "backend-avalanche", "package_name": "backend-avalanche",
"package_revision": "satoshilabs-1", "package_revision": "satoshilabs-1",
"system_user": "avalanche", "system_user": "avalanche",
"version": "1.9.11", "version": "1.12.1",
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz", "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_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", "extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [], "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 ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMEtmUT09IgogIH0KfQ==",
@ -36,8 +36,8 @@
"client_config_file": "", "client_config_file": "",
"platforms": { "platforms": {
"arm64": { "arm64": {
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz", "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.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz.sig" "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz.sig"
} }
} }
}, },

View File

@ -19,10 +19,10 @@
"package_name": "backend-avalanche-archive", "package_name": "backend-avalanche-archive",
"package_revision": "satoshilabs-1", "package_revision": "satoshilabs-1",
"system_user": "avalanche", "system_user": "avalanche",
"version": "1.9.11", "version": "1.12.1",
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz", "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_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", "extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [], "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 ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMHNDaUFnSW5CeWRXNXBibWN0Wlc1aFlteGxaQ0k2Wm1Gc2MyVUtmUT09IgogIH0KfQ==",
@ -36,8 +36,8 @@
"client_config_file": "", "client_config_file": "",
"platforms": { "platforms": {
"arm64": { "arm64": {
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz", "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.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz.sig" "verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz.sig"
} }
} }
}, },

View File

@ -22,10 +22,10 @@
"package_name": "backend-bcash", "package_name": "backend-bcash",
"package_revision": "satoshilabs-1", "package_revision": "satoshilabs-1",
"system_user": "bcash", "system_user": "bcash",
"version": "26.1.0", "version": "28.0.1",
"binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v26.1.0/bitcoin-cash-node-26.1.0-x86_64-linux-gnu.tar.gz", "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_type": "sha256",
"verification_source": "68cb24d57898d5b47a1162a9683d0b0e36c6701b5a16b93edc94bbd82113c04b", "verification_source": "d69ee632147f886ca540cecdff5b1b85512612b4c005e86b09083a63c35b64fa",
"extract_command": "tar -C backend --strip 1 -xf", "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", "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",

View File

@ -1,66 +1,64 @@
{ {
"coin": { "coin": {
"name": "Bcash Testnet", "name": "Bcash Testnet",
"shortcut": "TBCH", "shortcut": "TBCH",
"label": "Bitcoin Cash Testnet", "label": "Bitcoin Cash Testnet",
"alias": "bcash_testnet" "alias": "bcash_testnet"
}, },
"ports": { "ports": {
"backend_rpc": 18031, "backend_rpc": 18031,
"backend_message_queue": 48331, "backend_message_queue": 48331,
"blockbook_internal": 19031, "blockbook_internal": 19031,
"blockbook_public": 19131 "blockbook_public": 19131
}, },
"ipc": { "ipc": {
"rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}", "rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}",
"rpc_user": "rpc", "rpc_user": "rpc",
"rpc_pass": "rpc", "rpc_pass": "rpc",
"rpc_timeout": 25, "rpc_timeout": 25,
"message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}" "message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}"
}, },
"backend": { "backend": {
"package_name": "backend-bcash-testnet", "package_name": "backend-bcash-testnet",
"package_revision": "satoshilabs-1", "package_revision": "satoshilabs-1",
"system_user": "bcash", "system_user": "bcash",
"version": "26.1.0", "version": "28.0.1",
"binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v26.1.0/bitcoin-cash-node-26.1.0-x86_64-linux-gnu.tar.gz", "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_type": "sha256",
"verification_source": "68cb24d57898d5b47a1162a9683d0b0e36c6701b5a16b93edc94bbd82113c04b", "verification_source": "d69ee632147f886ca540cecdff5b1b85512612b4c005e86b09083a63c35b64fa",
"extract_command": "tar -C backend --strip 1 -xf", "extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [ "exclude_files": ["bin/bitcoin-qt"],
"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",
"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", "postinst_script_template": "",
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/testnet3/*.log", "service_type": "forking",
"postinst_script_template": "", "service_additional_params_template": "",
"service_type": "forking", "protect_memory": true,
"service_additional_params_template": "", "mainnet": false,
"protect_memory": true, "server_config_file": "bcash.conf",
"mainnet": false, "client_config_file": "bitcoin_client.conf"
"server_config_file": "bitcoin.conf", },
"client_config_file": "bitcoin_client.conf" "blockbook": {
}, "package_name": "blockbook-bcash-testnet",
"blockbook": { "system_user": "blockbook-bcash",
"package_name": "blockbook-bcash-testnet", "internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"system_user": "blockbook-bcash", "public_binding_template": ":{{.Ports.BlockbookPublic}}",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}", "explorer_url": "",
"public_binding_template": ":{{.Ports.BlockbookPublic}}", "additional_params": "",
"explorer_url": "", "block_chain": {
"additional_params": "", "parse": true,
"block_chain": { "subversion": "/Bitcoin ABC Cash Node:22.1.0/",
"parse": true, "address_format": "cashaddr",
"subversion": "/Bitcoin ABC Cash Node:22.1.0/", "mempool_workers": 8,
"address_format": "cashaddr", "mempool_sub_workers": 2,
"mempool_workers": 8, "block_addresses_to_keep": 300,
"mempool_sub_workers": 2, "xpub_magic": 70617039,
"block_addresses_to_keep": 300, "slip44": 1,
"xpub_magic": 70617039, "additional_params": {}
"slip44": 1, }
"additional_params": {} },
"meta": {
"package_maintainer": "IT",
"package_maintainer_email": "it@satoshilabs.com"
} }
},
"meta": {
"package_maintainer": "IT",
"package_maintainer_email": "it@satoshilabs.com"
}
} }

View File

@ -22,10 +22,10 @@
"package_name": "backend-bitcoin", "package_name": "backend-bitcoin",
"package_revision": "satoshilabs-1", "package_revision": "satoshilabs-1",
"system_user": "bitcoin", "system_user": "bitcoin",
"version": "25.0", "version": "28.0",
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-x86_64-linux-gnu.tar.gz", "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256", "verification_type": "sha256",
"verification_source": "33930d432593e49d58a9bff4c30078823e9af5d98594d2935862788ce8a20aec", "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc",
"extract_command": "tar -C backend --strip 1 -xf", "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", "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",
@ -38,12 +38,13 @@
"server_config_file": "bitcoin.conf", "server_config_file": "bitcoin.conf",
"client_config_file": "bitcoin_client.conf", "client_config_file": "bitcoin_client.conf",
"additional_params": { "additional_params": {
"deprecatedrpc": "estimatefee" "deprecatedrpc": "estimatefee",
"addnode": ["ove.palatinus.cz"]
}, },
"platforms": { "platforms": {
"arm64": { "arm64": {
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-aarch64-linux-gnu.tar.gz", "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz",
"verification_source": "3a7bdd959a0b426624f63f394f25e5b7769a5a2f96f8126dcc2ea53f3fa5212b" "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e"
} }
} }
}, },
@ -63,13 +64,17 @@
"xpub_magic_segwit_p2sh": 77429938, "xpub_magic_segwit_p2sh": 77429938,
"xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_native": 78792518,
"additional_params": { "additional_params": {
"alternative_estimate_fee": "whatthefee-disabled", "alternative_estimate_fee": "mempoolspace",
"alternative_estimate_fee_params": "{\"url\": \"https://whatthefee.io/data.json\", \"periodSeconds\": 60}", "alternative_estimate_fee_params": "{\"url\": \"https://mempool.space/api/v1/fees/recommended\", \"periodSeconds\": 20}",
"fiat_rates": "coingecko", "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_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\": 900}", "fiat_rates_params": "{\"coin\": \"bitcoin\", \"periodSeconds\": 60}",
"golomb_filter_p": 20, "block_golomb_filter_p": 20,
"mempool_filter_scripts": "taproot" "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
} }
} }
}, },

View File

@ -22,10 +22,10 @@
"package_name": "backend-bitcoin-regtest", "package_name": "backend-bitcoin-regtest",
"package_revision": "satoshilabs-1", "package_revision": "satoshilabs-1",
"system_user": "bitcoin", "system_user": "bitcoin",
"version": "25.0", "version": "28.0",
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-x86_64-linux-gnu.tar.gz", "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256", "verification_type": "sha256",
"verification_source": "33930d432593e49d58a9bff4c30078823e9af5d98594d2935862788ce8a20aec", "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc",
"extract_command": "tar -C backend --strip 1 -xf", "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", "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": { "platforms": {
"arm64": { "arm64": {
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-aarch64-linux-gnu.tar.gz", "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz",
"verification_source": "3a7bdd959a0b426624f63f394f25e5b7769a5a2f96f8126dcc2ea53f3fa5212b" "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e"
} }
} }
}, },
@ -63,7 +63,14 @@
"xpub_magic_segwit_p2sh": 71979618, "xpub_magic_segwit_p2sh": 71979618,
"xpub_magic_segwit_native": 73342198, "xpub_magic_segwit_native": 73342198,
"slip44": 1, "slip44": 1,
"additional_params": {} "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": { "meta": {

View File

@ -22,10 +22,10 @@
"package_name": "backend-bitcoin-signet", "package_name": "backend-bitcoin-signet",
"package_revision": "satoshilabs-1", "package_revision": "satoshilabs-1",
"system_user": "bitcoin", "system_user": "bitcoin",
"version": "25.0", "version": "28.0",
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-x86_64-linux-gnu.tar.gz", "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256", "verification_type": "sha256",
"verification_source": "33930d432593e49d58a9bff4c30078823e9af5d98594d2935862788ce8a20aec", "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc",
"extract_command": "tar -C backend --strip 1 -xf", "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", "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": "", "service_additional_params_template": "",
"protect_memory": true, "protect_memory": true,
"mainnet": false, "mainnet": false,
"server_config_file": "bitcoin-signet.conf", "server_config_file": "bitcoin_signet.conf",
"client_config_file": "bitcoin_client.conf", "client_config_file": "bitcoin_client.conf",
"additional_params": { "additional_params": {
"deprecatedrpc": "estimatefee" "deprecatedrpc": "estimatefee"
}, },
"platforms": { "platforms": {
"arm64": { "arm64": {
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-aarch64-linux-gnu.tar.gz", "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz",
"verification_source": "3a7bdd959a0b426624f63f394f25e5b7769a5a2f96f8126dcc2ea53f3fa5212b" "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e"
} }
} }
}, },

View File

@ -22,10 +22,10 @@
"package_name": "backend-bitcoin-testnet", "package_name": "backend-bitcoin-testnet",
"package_revision": "satoshilabs-1", "package_revision": "satoshilabs-1",
"system_user": "bitcoin", "system_user": "bitcoin",
"version": "25.0", "version": "28.0",
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-x86_64-linux-gnu.tar.gz", "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256", "verification_type": "sha256",
"verification_source": "33930d432593e49d58a9bff4c30078823e9af5d98594d2935862788ce8a20aec", "verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc",
"extract_command": "tar -C backend --strip 1 -xf", "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", "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": { "platforms": {
"arm64": { "arm64": {
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-aarch64-linux-gnu.tar.gz", "binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz",
"verification_source": "3a7bdd959a0b426624f63f394f25e5b7769a5a2f96f8126dcc2ea53f3fa5212b" "verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e"
} }
} }
}, },
@ -58,14 +58,18 @@
"parse": true, "parse": true,
"mempool_workers": 8, "mempool_workers": 8,
"mempool_sub_workers": 2, "mempool_sub_workers": 2,
"block_addresses_to_keep": 300, "block_addresses_to_keep": 10000,
"xpub_magic": 70617039, "xpub_magic": 70617039,
"xpub_magic_segwit_p2sh": 71979618, "xpub_magic_segwit_p2sh": 71979618,
"xpub_magic_segwit_native": 73342198, "xpub_magic_segwit_native": 73342198,
"slip44": 1, "slip44": 1,
"additional_params": { "additional_params": {
"golomb_filter_p": 20, "block_golomb_filter_p": 20,
"mempool_filter_scripts": "taproot" "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
} }
} }
}, },

View File

@ -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"
}
}

View File

@ -2,6 +2,7 @@
"coin": { "coin": {
"name": "BNB Smart Chain", "name": "BNB Smart Chain",
"shortcut": "BNB", "shortcut": "BNB",
"network": "BSC",
"label": "BNB Smart Chain", "label": "BNB Smart Chain",
"alias": "bsc" "alias": "bsc"
}, },

View File

@ -2,6 +2,7 @@
"coin": { "coin": {
"name": "BNB Smart Chain Archive", "name": "BNB Smart Chain Archive",
"shortcut": "BNB", "shortcut": "BNB",
"network": "BSC",
"label": "BNB Smart Chain", "label": "BNB Smart Chain",
"alias": "bsc_archive" "alias": "bsc_archive"
}, },

View File

@ -22,10 +22,10 @@
"package_name": "backend-dash", "package_name": "backend-dash",
"package_revision": "satoshilabs-1", "package_revision": "satoshilabs-1",
"system_user": "dash", "system_user": "dash",
"version": "19.2.0", "version": "22.0.0",
"binary_url": "https://github.com/dashpay/dash/releases/download/v19.2.0/dashcore-19.2.0-x86_64-linux-gnu.tar.gz", "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_type": "gpg-sha256",
"verification_source": "https://github.com/dashpay/dash/releases/download/v19.2.0/SHA256SUMS.asc", "verification_source": "https://github.com/dashpay/dash/releases/download/v22.0.0/SHA256SUMS.asc",
"extract_command": "tar -C backend --strip 1 -xf", "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", "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",

View File

@ -22,10 +22,10 @@
"package_name": "backend-dash-testnet", "package_name": "backend-dash-testnet",
"package_revision": "satoshilabs-1", "package_revision": "satoshilabs-1",
"system_user": "dash", "system_user": "dash",
"version": "19.2.0", "version": "22.0.0",
"binary_url": "https://github.com/dashpay/dash/releases/download/v19.2.0/dashcore-19.2.0-x86_64-linux-gnu.tar.gz", "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_type": "gpg-sha256",
"verification_source": "https://github.com/dashpay/dash/releases/download/v19.2.0/SHA256SUMS.asc", "verification_source": "https://github.com/dashpay/dash/releases/download/v22.0.0/SHA256SUMS.asc",
"extract_command": "tar -C backend --strip 1 -xf", "extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [ "exclude_files": [
"bin/dash-qt" "bin/dash-qt"

View File

@ -22,10 +22,10 @@
"package_name": "backend-dogecoin", "package_name": "backend-dogecoin",
"package_revision": "satoshilabs-1", "package_revision": "satoshilabs-1",
"system_user": "dogecoin", "system_user": "dogecoin",
"version": "1.14.6", "version": "1.14.9",
"binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.6/dogecoin-1.14.6-x86_64-linux-gnu.tar.gz", "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_type": "sha256",
"verification_source": "fe9c9cdab946155866a5bd5a5127d2971a9eed3e0b65fb553fe393ad1daaebb0", "verification_source": "4f227117b411a7c98622c970986e27bcfc3f547a72bef65e7d9e82989175d4f8",
"extract_command": "tar -C backend --strip 1 -xf", "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", "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": { "platforms": {
"arm64": { "arm64": {
"binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.6/dogecoin-1.14.6-aarch64-linux-gnu.tar.gz", "binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.9/dogecoin-1.14.9-aarch64-linux-gnu.tar.gz",
"verification_source": "87419c29607b2612746fccebd694037e4be7600fc32198c4989f919be20952db", "verification_source": "6928c895a20d0bcb6d5c7dcec753d35c884a471aaf8ad4242a89a96acb4f2985",
"exclude_files": [] "exclude_files": []
} }
} }

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