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
tags:
- blockbook
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
script: ./contrib/scripts/backend-deploy-and-test.sh zcash_testnet zcash-testnet zcash=test testnet3/debug.log

View File

@ -1,9 +1,10 @@
BIN_IMAGE = blockbook-build
DEB_IMAGE = blockbook-build-deb
PACKAGER = $(shell id -u):$(shell id -g)
DOCKER_VERSION = $(shell docker version --format '{{.Client.Version}}')
BASE_IMAGE = $$(awk -F= '$$1=="ID" { print $$2 ;}' /etc/os-release):$$(awk -F= '$$1=="VERSION_ID" { print $$2 ;}' /etc/os-release | tr -d '"')
NO_CACHE = false
TCMALLOC =
TCMALLOC =
PORTABLE = 0
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)"
deb-backend-%: .deb-image
docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh backend $* $(ARGS)
docker run -t --rm -e PACKAGER=$(PACKAGER) -v /var/run/docker.sock:/var/run/docker.sock -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh backend $* $(ARGS)
deb-blockbook-%: .deb-image
docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh blockbook $* $(ARGS)
@ -55,7 +56,7 @@ build-images: clean-images
.deb-image: .bin-image
@if [ $$(build/tools/image_status.sh $(DEB_IMAGE):latest build/docker) != "ok" ]; then \
echo "Building image $(DEB_IMAGE)..."; \
docker build --no-cache=$(NO_CACHE) -t $(DEB_IMAGE) build/docker/deb; \
docker build --no-cache=$(NO_CACHE) --build-arg DOCKER_VERSION=$(DOCKER_VERSION) -t $(DEB_IMAGE) build/docker/deb; \
else \
echo "Image $(DEB_IMAGE) is up to date"; \
fi
@ -79,3 +80,6 @@ clean-bin-image:
clean-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)
[![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** 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
- 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:
- 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).
@ -74,3 +220,7 @@ Blockbook stores data the key-value store RocksDB. Database format is described
## API
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 {
blockSpecificData, _ = block.CoinSpecificData.(*bchain.EthereumBlockSpecificData)
}
if err != nil || block == nil || blockSpecificData == nil || blockSpecificData.InternalDataError != "" {
if err != nil || block == nil || (blockSpecificData != nil && blockSpecificData.InternalDataError != "") {
glog.Errorf("Refetching internal data for %d %s, error %v, retrying", ie.Height, ie.Hash, err)
// try for second time to fetch the data - the 2nd attempt after the first unsuccessful has many times higher probability of success
// probably something to do with data preloaded to cache on the backend

View File

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

View File

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

View File

@ -36,6 +36,9 @@ type Worker struct {
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
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{
@ -169,9 +172,18 @@ func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliases
}
for a := range addresses {
if w.chainType == bchain.ChainEthereumType {
ci, err := w.db.GetContractInfoForAddress(a)
if err == nil && ci != nil && ci.Name != "" {
aliases[a] = AddressAlias{Type: "Contract", Alias: ci.Name}
addrDesc, err := w.chainParser.GetAddrDescFromAddress(a)
if err != nil || addrDesc == nil {
continue
}
ci, err := w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType)
if err == nil && ci != nil {
if ci.Type == bchain.UnhandledTokenType {
ci, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenType)
}
if err == nil && ci != nil && ci.Name != "" {
aliases[a] = AddressAlias{Type: "Contract", Alias: ci.Name}
}
}
}
n := w.db.GetAddressAlias(a)
@ -187,7 +199,7 @@ func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliases
// GetTransaction reads transaction data from txid
func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) {
addresses := w.newAddressesMapForAliases()
tx, err := w.getTransaction(txid, spendingTxs, specificJSON, addresses)
tx, err := w.getTransaction(txid, spendingTxs, /*specificJSON*/ true, addresses)
if err != nil {
return nil, err
}
@ -195,6 +207,11 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool
return tx, nil
}
// GetRawTransaction gets raw transaction data in hex format from txid
func (w *Worker) GetRawTransaction(txid string) (string, error) {
return w.chain.EthereumTypeGetRawTransaction(txid)
}
// getTransaction reads transaction data from txid
func (w *Worker) getTransaction(txid string, spendingTxs bool, specificJSON bool, addresses map[string]struct{}) (*Tx, error) {
bchainTx, height, err := w.txCache.GetTransaction(txid)
@ -426,18 +443,25 @@ func (w *Worker) getTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
// mempool txs do not have fees yet
if ethTxData.GasUsed != nil {
feesSat.Mul(ethTxData.GasPrice, ethTxData.GasUsed)
if ethTxData.L1Fee != nil {
feesSat.Add(&feesSat, ethTxData.L1Fee)
}
}
if len(bchainTx.Vout) > 0 {
valOutSat = bchainTx.Vout[0].ValueSat
}
ethSpecific = &EthereumSpecific{
GasLimit: ethTxData.GasLimit,
GasPrice: (*Amount)(ethTxData.GasPrice),
GasUsed: ethTxData.GasUsed,
Nonce: ethTxData.Nonce,
Status: ethTxData.Status,
Data: ethTxData.Data,
ParsedData: parsedInputData,
GasLimit: ethTxData.GasLimit,
GasPrice: (*Amount)(ethTxData.GasPrice),
GasUsed: ethTxData.GasUsed,
L1Fee: ethTxData.L1Fee,
L1FeeScalar: ethTxData.L1FeeScalar,
L1GasPrice: (*Amount)(ethTxData.L1GasPrice),
L1GasUsed: ethTxData.L1GasUsed,
Nonce: ethTxData.Nonce,
Status: ethTxData.Status,
Data: ethTxData.Data,
ParsedData: parsedInputData,
}
if internalData != nil {
ethSpecific.Type = internalData.Type
@ -598,7 +622,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx,
return r, nil
}
func (w *Worker) getContractInfo(contract string, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) {
func (w *Worker) GetContractInfo(contract string, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) {
cd, err := w.chainParser.GetAddrDescFromAddress(contract)
if err != nil {
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)
}
}
} else if (len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) {
} else if (contractInfo.Type == bchain.UnhandledTokenType || len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) {
// fix contract name/symbol that was parsed as a string consisting of zeroes
blockchainContractInfo, err := w.chain.GetContractInfo(cd)
if err != nil {
@ -657,6 +681,10 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom
if blockchainContractInfo != nil {
contractInfo.Decimals = blockchainContractInfo.Decimals
}
if contractInfo.Type == bchain.UnhandledTokenType {
glog.Infof("Contract %v %v [%s] handled", cd, typeFromContext, contractInfo.Name)
contractInfo.Type = typeFromContext
}
if err = w.db.StoreContractInfo(contractInfo); err != nil {
glog.Errorf("StoreContractInfo error %v, contract %v", err, cd)
}
@ -666,39 +694,49 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom
}
func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, addresses map[string]struct{}) []TokenTransfer {
sort.Sort(transfers)
tokens := make([]TokenTransfer, len(transfers))
for i := range transfers {
t := transfers[i]
typeName := bchain.EthereumTokenTypeMap[t.Type]
contractInfo, _, err := w.getContractInfo(t.Contract, typeName)
if err != nil {
glog.Errorf("getContractInfo error %v, contract %v", err, t.Contract)
continue
}
var value *Amount
var values []MultiTokenValue
if t.Type == bchain.MultiToken {
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)
if len(transfers) > 0 {
sort.Sort(transfers)
contractCache := make(contractInfoCache)
for i := range transfers {
t := transfers[i]
typeName := bchain.EthereumTokenTypeMap[t.Type]
var contractInfo *bchain.ContractInfo
if info, ok := contractCache[t.Contract]; ok {
contractInfo = info
} else {
info, _, err := w.GetContractInfo(t.Contract, typeName)
if err != nil {
glog.Errorf("getContractInfo error %v, contract %v", err, t.Contract)
continue
}
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
@ -1045,6 +1083,7 @@ type ethereumTypeAddressData struct {
totalResults int
tokensBaseValue float64
tokensSecondaryValue float64
stakingPools []StakingPool
}
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]
sort.Sort(d.tokens)
}
d.contractInfo, err = w.db.GetContractInfo(addrDesc, "")
d.contractInfo, err = w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType)
if err != nil {
return nil, nil, err
}
if d.contractInfo != nil && d.contractInfo.Type == bchain.UnhandledTokenType {
d.contractInfo, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenType)
if err != nil {
return nil, nil, err
}
}
if filter.FromHeight == 0 && filter.ToHeight == 0 {
// compute total results for paging
if filter.Vout == AddressFilterVoutOff {
@ -1144,9 +1189,41 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
filter.Vout = AddressFilterVoutQueryNotNecessary
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
}
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) {
var tx *Tx
var err error
@ -1159,7 +1236,7 @@ func (w *Worker) txFromTxid(txid string, bestHeight uint32, option AccountDetail
if ta == nil {
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
// as fallback, get tx from backend
tx, err = w.getTransaction(txid, false, false, addresses)
tx, err = w.getTransaction(txid, false, true, addresses)
if err != nil {
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)
}
} else {
tx, err = w.getTransaction(txid, false, false, addresses)
tx, err = w.getTransaction(txid, false, true, addresses)
if err != nil {
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 {
txids = append(txids, txid)
} else {
tx, err := w.txFromTxid(txid, bestheight, option, nil, addresses)
tx, err := w.txFromTxid(txid, bestheight, AccountDetailsTxHistory, nil, addresses)
if err != nil {
return nil, err
}
@ -1388,12 +1465,13 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
ContractInfo: ed.contractInfo,
Nonce: ed.nonce,
AddressAliases: w.getAddressAliases(addresses),
StakingPools: ed.stakingPools,
}
// keep address backward compatible, set deprecated Erc20Contract value if ERC20 token
if ed.contractInfo != nil && ed.contractInfo.Type == bchain.ERC20TokenType {
r.Erc20Contract = ed.contractInfo
}
glog.Info("GetAddress ", address, ", ", time.Since(start))
glog.Info("GetAddress-", option, " ", address, ", ", time.Since(start))
return r, nil
}
@ -1603,6 +1681,17 @@ func (w *Worker) GetBalanceHistory(address string, fromTimestamp, toTimestamp in
if err != nil {
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)
if fromHeight >= toHeight {
return bhs, nil
@ -1880,7 +1969,11 @@ func (w *Worker) GetCurrentFiatRates(currencies []string, token string) (*FiatTi
ticker := w.fiatRates.GetCurrentTicker(vsCurrency, token)
var err error
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 {
return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false)
} else if ticker == nil {
@ -2146,7 +2239,7 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
txi := 0
addresses := w.newAddressesMapForAliases()
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 {
return nil, err
}
@ -2200,6 +2293,48 @@ func (w *Worker) GetBlockRaw(bid string) (*BlockRaw, error) {
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
func (w *Worker) ComputeFeeStats(blockFrom, blockTo int, stopCompute chan os.Signal) error {
bestheight, _, err := w.db.GetBestBlock()
@ -2302,6 +2437,7 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
}
blockbookInfo := &BlockbookInfo{
Coin: w.is.Coin,
Network: w.is.GetNetwork(),
Host: w.is.Host,
Version: vi.Version,
GitCommit: vi.GitCommit,
@ -2320,6 +2456,7 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
CurrentFiatRatesTime: nonZeroTime(currentFiatRatesTime),
HistoricalFiatRatesTime: nonZeroTime(w.is.HistoricalFiatRatesTime),
HistoricalTokenFiatRatesTime: nonZeroTime(w.is.HistoricalTokenFiatRatesTime),
SupportedStakingPools: w.chain.EthereumTypeGetSupportedStakingPools(),
DbSize: w.db.DatabaseSizeOnDisk(),
DbSizeFromColumns: internalDBSize,
DbColumns: columnStats,

View File

@ -556,7 +556,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
usedTokens++
}
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 ||
filter.TokensToReturn == TokensToReturnUsed && ad.balance != nil ||
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
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
func (b *BaseChain) EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error) {
return 0, errors.New("Not supported")
return 0, errors.New("not supported")
}
// EthereumTypeEstimateGas is not supported
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
func (b *BaseChain) GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error) {
return nil, errors.New("Not supported")
return nil, errors.New("not supported")
}
// EthereumTypeGetErc20ContractBalance is not supported
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) {
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) {
ci, err := b.EthereumRPC.GetChainInfo()
if err != nil {
fmt.Println(err)
return nil, err
}

View File

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

View File

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

View File

@ -4,8 +4,6 @@ import (
"context"
"encoding/json"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
"github.com/golang/glog"
"github.com/juju/errors"
"github.com/trezor/blockbook/bchain"
@ -40,21 +38,14 @@ func NewBNBSmartChainRPC(config json.RawMessage, pushHandler func(bchain.Notific
s := &BNBSmartChainRPC{
EthereumRPC: c.(*eth.EthereumRPC),
}
s.Parser.EnsSuffix = ".bnb"
return s, nil
}
// Initialize bnb smart chain rpc interface
func (b *BNBSmartChainRPC) Initialize() error {
b.OpenRPC = func(url string) (bchain.EVMRPCClient, bchain.EVMClient, error) {
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
}
b.OpenRPC = eth.OpenRPC
rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL)
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,
Sequence: in.Sequence,
ScriptSig: s,
Witness: in.Witness,
}
}
vout := make([]bchain.Vout, len(t.TxOut))

View File

@ -4,11 +4,28 @@ import (
"encoding/json"
"math/big"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg"
"github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/common"
)
// temp params for signet(wait btcd commit)
// magic numbers
const (
Testnet4Magic wire.BitcoinNet = 0x283f161c
)
// chain parameters
var (
TestNet4Params chaincfg.Params
)
func init() {
TestNet4Params = chaincfg.TestNet3Params
TestNet4Params.Net = Testnet4Magic
}
// BitcoinParser handle
type BitcoinParser struct {
*BitcoinLikeParser
@ -33,6 +50,8 @@ func GetChainParams(chain string) *chaincfg.Params {
switch chain {
case "test":
return &chaincfg.TestNet3Params
case "testnet4":
return &TestNet4Params
case "regtest":
return &chaincfg.RegressionNetParams
case "signet":

View File

@ -467,11 +467,12 @@ func TestGetAddressesFromAddrDescTestnet(t *testing.T) {
}
var (
testTx1, testTx2, testTx3 bchain.Tx
testTx1, testTx2, testTx3, testTx4 bchain.Tx
testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700"
testTxPacked2 = "0007c91a899ab7da6a010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000"
testTxPacked3 = "00003d818bfda9aa3e02000000000102deb1999a857ab0a13d6b12fbd95ea75b409edde5f2ff747507ce42d9986a8b9d0000000000fdffffff9fd2d3361e203b2375eba6438efbef5b3075531e7e583c7cc76b7294fe7f22980000000000fdffffff02a0860100000000001600148091746745464e7555c31e9a5afceac14a02978ae7fc1c0000000000160014565ea9ff4589d3e05ba149ae6e257752bfdc2a1e0247304402207d67d320a8e813f986b35e9791935fcb736754812b7038686f5de6cfdcda99cd02201c3bb2c178e0056016437ecfe365a7eef84aa9d293ebdc566177af82e22fcdd3012103abb30c1bbe878b07b58dc169b1d061d48c60be8107f632a59778b38bf7ceea5a02473044022044f54a478cfe086e870cb026c9dcd4e14e63778bef569a4d55a6332725cd9a9802202f0e94c04e6f328fc64ad9efe552888c299750d1b8d033324825a3ff29920e030121036fcd433428aa7dc65c4f5408fa31f208c54fe4b4c6c1ae9c39a825ed4f1ac039813d0000"
testTxPacked4 = "0000a2b98ced82b6400300000000010148f8f93ebb12407809920d2ab9cc1bf01289b314eb23028c83fdab21e5fefa690100000000fdffffff0150c3000000000000160014cb888de3c89670a3061fb6ef6590f187649cca060247304402206a9db8d7157e4b0a06a1f090b9de88cdc616028b431b80617a055117877e479a02202937d6d1658d4a8afde86b245325c3bb0e769a87cb09d802bcefaa21550065e201210374aa8f312de4ebccbef55609700a39764387aa4ff5d76f1ccb4d2382e454f05b00000000"
)
func init() {
@ -595,6 +596,37 @@ func init() {
},
},
}
testTx4 = bchain.Tx{
Hex: "0300000000010148f8f93ebb12407809920d2ab9cc1bf01289b314eb23028c83fdab21e5fefa690100000000fdffffff0150c3000000000000160014cb888de3c89670a3061fb6ef6590f187649cca060247304402206a9db8d7157e4b0a06a1f090b9de88cdc616028b431b80617a055117877e479a02202937d6d1658d4a8afde86b245325c3bb0e769a87cb09d802bcefaa21550065e201210374aa8f312de4ebccbef55609700a39764387aa4ff5d76f1ccb4d2382e454f05b00000000",
Blocktime: 1724927392,
Txid: "8e3f38bf6854dd3c358be8d4f9a40a6dccc50de49616125d27af9fdbe65287eb",
LockTime: 0,
VSize: 110,
Version: 3,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "",
},
Txid: "69fafee521abfd838c0223eb14b38912f01bccb92a0d9209784012bb3ef9f848",
Vout: 1,
Sequence: 4294967293,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(50000),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "0014cb888de3c89670a3061fb6ef6590f187649cca06",
Addresses: []string{
"tb1qewygmc7gjec2xpslkmhkty83sajfejsxqmy5dq",
},
},
},
},
}
}
func TestPackTx(t *testing.T) {
@ -643,6 +675,17 @@ func TestPackTx(t *testing.T) {
want: testTxPacked3,
wantErr: false,
},
{
name: "testnet4-1",
args: args{
tx: testTx4,
height: 41657,
blockTime: 1724927392,
parser: NewBitcoinParser(GetChainParams("testnet4"), &Configuration{}),
},
want: testTxPacked4,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -701,6 +744,16 @@ func TestUnpackTx(t *testing.T) {
want1: 15745,
wantErr: false,
},
{
name: "testnet4-1",
args: args{
packedTx: testTxPacked4,
parser: NewBitcoinParser(GetChainParams("testnet4"), &Configuration{}),
},
want: &testTx4,
want1: 41657,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -710,6 +763,10 @@ func TestUnpackTx(t *testing.T) {
t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr)
return
}
// ignore witness unpacking
for i := range got.Vin {
got.Vin[i].Witness = nil
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("unpackTx() got = %v, want %v", got, tt.want)
}

View File

@ -6,7 +6,6 @@ import (
"encoding/hex"
"encoding/json"
"io"
"io/ioutil"
"math/big"
"net"
"net/http"
@ -23,18 +22,20 @@ import (
// BitcoinRPC is an interface to JSON-RPC bitcoind service.
type BitcoinRPC struct {
*bchain.BaseChain
client http.Client
rpcURL string
user string
password string
Mempool *bchain.MempoolBitcoinType
ParseBlocks bool
pushHandler func(bchain.NotificationType)
mq *bchain.MQ
ChainConfig *Configuration
RPCMarshaler RPCMarshaler
golombFilterP uint8
mempoolFilterScripts string
client http.Client
rpcURL string
user string
password string
Mempool *bchain.MempoolBitcoinType
ParseBlocks bool
pushHandler func(bchain.NotificationType)
mq *bchain.MQ
ChainConfig *Configuration
RPCMarshaler RPCMarshaler
mempoolGolombFilterP uint8
mempoolFilterScripts string
mempoolUseZeroedKey bool
alternativeFeeProvider alternativeFeeProviderInterface
}
// Configuration represents json config file
@ -62,8 +63,9 @@ type Configuration struct {
AlternativeEstimateFee string `json:"alternative_estimate_fee,omitempty"`
AlternativeEstimateFeeParams string `json:"alternative_estimate_fee_params,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"`
MempoolFilterUseZeroedKey bool `json:"mempool_filter_use_zeroed_key,omitempty"`
}
// NewBitcoinRPC returns new BitcoinRPC instance.
@ -109,8 +111,9 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationT
ChainConfig: &c,
pushHandler: pushHandler,
RPCMarshaler: JSONMarshalerV2{},
golombFilterP: c.GolombFilterP,
mempoolGolombFilterP: c.MempoolGolombFilterP,
mempoolFilterScripts: c.MempoolFilterScripts,
mempoolUseZeroedKey: c.MempoolFilterUseZeroedKey,
}
return s, nil
@ -143,10 +146,16 @@ func (b *BitcoinRPC) Initialize() error {
glog.Info("rpc: block chain ", params.Name)
if b.ChainConfig.AlternativeEstimateFee == "whatthefee" {
if err = InitWhatTheFee(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil {
glog.Error("InitWhatTheFee error ", err, " Reverting to default estimateFee functionality")
if b.alternativeFeeProvider, err = NewWhatTheFee(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil {
glog.Error("NewWhatTheFee error ", err, " Reverting to default estimateFee functionality")
// disable AlternativeEstimateFee logic
b.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
func (b *BitcoinRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) {
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
}
@ -772,8 +781,7 @@ func (b *BitcoinRPC) getRawTransaction(txid string) (json.RawMessage, error) {
return res.Result, nil
}
// EstimateSmartFee returns fee estimation
func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
func (b *BitcoinRPC) blockchainEstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
// use EstimateFee if EstimateSmartFee is not supported
if !b.ChainConfig.SupportsEstimateSmartFee && b.ChainConfig.SupportsEstimateFee {
return b.EstimateFee(blocks)
@ -790,7 +798,6 @@ func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, e
req.Params.EstimateMode = "ECONOMICAL"
}
err := b.Call(&req, &res)
var r big.Int
if err != nil {
return r, err
@ -805,8 +812,31 @@ func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, e
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.
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
if !b.ChainConfig.SupportsEstimateFee && b.ChainConfig.SupportsEstimateSmartFee {
return b.EstimateSmartFee(blocks, true)
@ -817,9 +847,8 @@ func (b *BitcoinRPC) EstimateFee(blocks int) (big.Int, error) {
res := ResEstimateFee{}
req := CmdEstimateFee{Method: "estimatefee"}
req.Params.Blocks = blocks
err := b.Call(&req, &res)
err = b.Call(&req, &res)
var r big.Int
if err != nil {
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 {
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 (
"bytes"
"encoding/json"
"fmt"
"math"
"net/http"
"strconv"
"sync"
"time"
"github.com/golang/glog"
@ -34,49 +32,40 @@ type whatTheFeeParams struct {
PeriodSeconds int `periodSeconds:"url"`
}
type whatTheFeeFee struct {
blocks int
feesPerKB []int
}
type whatTheFeeData struct {
type whatTheFeeProvider struct {
*alternativeFeeProvider
params whatTheFeeParams
probabilities []string
fees []whatTheFeeFee
lastSync time.Time
chain bchain.BlockChain
mux sync.Mutex
}
var whatTheFee whatTheFeeData
// InitWhatTheFee initializes https://whatthefee.io handler
func InitWhatTheFee(chain bchain.BlockChain, params string) error {
err := json.Unmarshal([]byte(params), &whatTheFee.params)
// NewWhatTheFee initializes https://whatthefee.io provider
func NewWhatTheFee(chain bchain.BlockChain, params string) (alternativeFeeProviderInterface, error) {
var p whatTheFeeProvider
err := json.Unmarshal([]byte(params), &p.params)
if err != nil {
return err
return nil, err
}
if whatTheFee.params.URL == "" || whatTheFee.params.PeriodSeconds == 0 {
return errors.New("Missing parameters")
if p.params.URL == "" || p.params.PeriodSeconds == 0 {
return nil, errors.New("NewWhatTheFee: Missing parameters")
}
whatTheFee.chain = chain
go whatTheFeeDownloader()
return nil
p.chain = chain
go p.whatTheFeeDownloader()
return &p, nil
}
func whatTheFeeDownloader() {
period := time.Duration(whatTheFee.params.PeriodSeconds) * time.Second
func (p *whatTheFeeProvider) whatTheFeeDownloader() {
period := time.Duration(p.params.PeriodSeconds) * time.Second
timer := time.NewTimer(period)
counter := 0
for {
var data whatTheFeeServiceResult
err := whatTheFeeGetData(&data)
err := p.whatTheFeeGetData(&data)
if err != nil {
glog.Error("whatTheFeeGetData ", err)
} else {
if whatTheFeeProcessData(&data) {
if p.whatTheFeeProcessData(&data) {
if counter%60 == 0 {
whatTheFeeCompareToDefault()
p.compareToDefault()
}
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 {
glog.Errorf("invalid data %+v", data)
return false
}
whatTheFee.mux.Lock()
defer whatTheFee.mux.Unlock()
whatTheFee.probabilities = data.Columns
whatTheFee.fees = make([]whatTheFeeFee, len(data.Index))
p.mux.Lock()
defer p.mux.Unlock()
p.probabilities = data.Columns
p.fees = make([]alternativeFeeProviderFee, len(data.Index))
for i, blocks := range data.Index {
if len(data.Columns) != len(data.Data[i]) {
glog.Errorf("invalid data %+v", data)
@ -104,19 +93,19 @@ func whatTheFeeProcessData(data *whatTheFeeServiceResult) bool {
for j, l := range data.Data[i] {
fees[j] = int(1000 * math.Exp(float64(l)/100))
}
whatTheFee.fees[i] = whatTheFeeFee{
blocks: blocks,
feesPerKB: fees,
p.fees[i] = alternativeFeeProviderFee{
blocks: blocks,
feePerKB: fees[len(fees)/2],
}
}
whatTheFee.lastSync = time.Now()
glog.Infof("%+v", whatTheFee.fees)
p.lastSync = time.Now()
glog.Infof("whatTheFees: %+v", p.fees)
return true
}
func whatTheFeeGetData(res interface{}) error {
func (p *whatTheFeeProvider) whatTheFeeGetData(res interface{}) error {
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 {
return err
}
@ -132,25 +121,3 @@ func whatTheFeeGetData(res interface{}) error {
}
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 (
"fmt"
"github.com/pirk/ecashutil"
"github.com/martinboehm/btcutil"
"github.com/martinboehm/btcutil/chaincfg"
"github.com/martinboehm/btcutil/txscript"
"github.com/pirk/ecashaddr-converter/address"
"github.com/pirk/ecashutil"
"github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/bchain/coins/btc"
)

View File

@ -342,6 +342,10 @@ func Test_UnpackTx(t *testing.T) {
t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr)
return
}
// ignore witness unpacking
for i := range got.Vin {
got.Vin[i].Witness = nil
}
if !reflect.DeepEqual(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
}
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)
defer cancel()
var r string
err := b.RPC.CallContext(ctx, &r, "eth_call", map[string]interface{}{
args := map[string]interface{}{
"data": data,
"to": to,
}, "latest")
}
if from != "" {
args["from"] = from
}
err := b.RPC.CallContext(ctx, &r, "eth_call", args, "latest")
if err != nil {
return "", err
}
@ -289,7 +294,7 @@ func (b *EthereumRPC) ethCall(data, to string) (string, error) {
func (b *EthereumRPC) fetchContractInfo(address string) (*bchain.ContractInfo, error) {
var contract bchain.ContractInfo
data, err := b.ethCall(contractNameSignature, address)
data, err := b.EthereumTypeRpcCall(contractNameSignature, address, "")
if err != nil {
// ignore the error from the eth_call - since geth v1.9.15 they changed the behavior
// and returning error "execution reverted" for some non contract addresses
@ -300,14 +305,14 @@ func (b *EthereumRPC) fetchContractInfo(address string) (*bchain.ContractInfo, e
}
name := strings.TrimSpace(parseSimpleStringProperty(data))
if name != "" {
data, err = b.ethCall(contractSymbolSignature, address)
data, err = b.EthereumTypeRpcCall(contractSymbolSignature, address, "")
if err != nil {
// glog.Warning(errors.Annotatef(err, "Contract SymbolSignature %v", address))
return nil, nil
// return nil, errors.Annotatef(err, "erc20SymbolSignature %v", address)
}
symbol := strings.TrimSpace(parseSimpleStringProperty(data))
data, _ = b.ethCall(contractDecimalsSignature, address)
data, _ = b.EthereumTypeRpcCall(contractDecimalsSignature, address, "")
// if err != nil {
// glog.Warning(errors.Annotatef(err, "Contract DecimalsSignature %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
func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) {
addr := hexutil.Encode(addrDesc)
addr := hexutil.Encode(addrDesc)[2:]
contract := hexutil.Encode(contractDesc)
req := contractBalanceOfSignature + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr)-2:] + addr[2:]
data, err := b.ethCall(req, contract)
req := contractBalanceOfSignature + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr):] + addr
data, err := b.EthereumTypeRpcCall(req, contract, "")
if err != nil {
return nil, err
}
@ -351,7 +356,7 @@ func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc
return r, nil
}
// GetContractInfo returns URI of non fungible or multi token defined by token id
// GetTokenURI returns URI of non fungible or multi token defined by token id
func (b *EthereumRPC) GetTokenURI(contractDesc bchain.AddressDescriptor, tokenID *big.Int) (string, error) {
address := hexutil.Encode(contractDesc)
// CryptoKitties do not fully support ERC721 standard, do not have tokenURI method
@ -364,7 +369,7 @@ func (b *EthereumRPC) GetTokenURI(contractDesc bchain.AddressDescriptor, tokenID
}
// try ERC721 tokenURI method and ERC1155 uri method
for _, method := range []string{erc721TokenURIMethodSignature, erc1155URIMethodSignature} {
data, err := b.ethCall(method+id, address)
data, err := b.EthereumTypeRpcCall(method+id, address, "")
if err == nil && data != "" {
uri := parseSimpleStringProperty(data)
// 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"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/protobuf/proto"
"github.com/juju/errors"
"github.com/trezor/blockbook/bchain"
"golang.org/x/crypto/sha3"
"google.golang.org/protobuf/proto"
)
// EthereumTypeAddressDescriptorLen - the AddressDescriptor of EthereumType has fixed length
@ -25,15 +25,19 @@ const EtherAmountDecimalPoint = 18
// EthereumParser handle
type EthereumParser struct {
*bchain.BaseParser
EnsSuffix string
}
// NewEthereumParser returns new EthereumParser instance
func NewEthereumParser(b int, addressAliases bool) *EthereumParser {
return &EthereumParser{&bchain.BaseParser{
BlockAddressesToKeep: b,
AmountDecimalPoint: EtherAmountDecimalPoint,
AddressAliases: addressAliases,
}}
return &EthereumParser{
BaseParser: &bchain.BaseParser{
BlockAddressesToKeep: b,
AmountDecimalPoint: EtherAmountDecimalPoint,
AddressAliases: addressAliases,
},
EnsSuffix: ".eth",
}
}
type rpcHeader struct {
@ -331,6 +335,24 @@ func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) (
}
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)
}
@ -359,27 +381,37 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
}
var rr *bchain.RpcReceipt
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 {
topics := make([]string, len(l.Topics))
for j, t := range l.Topics {
topics[j] = hexutil.Encode(t)
}
logs[i] = &bchain.RpcLog{
rr.Logs[i] = &bchain.RpcLog{
Address: EIP55Address(l.Address),
Data: hexutil.Encode(l.Data),
Topics: topics,
}
}
status := ""
// handle a special value []byte{'U'} as unknown state
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{
GasUsed: hexEncodeBig(pt.Receipt.GasUsed),
Status: status,
Logs: logs,
if len(pt.Receipt.L1Fee) > 0 {
rr.L1Fee = hexEncodeBig(pt.Receipt.L1Fee)
}
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
@ -461,7 +493,7 @@ func (p *EthereumParser) EthereumTypeGetTokenTransfersFromTx(tx *bchain.Tx) (bch
// FormatAddressAlias adds .eth to a name alias
func (p *EthereumParser) FormatAddressAlias(address string, name string) string {
return name + ".eth"
return name + p.EnsSuffix
}
// TxStatus is status of transaction
@ -477,12 +509,16 @@ const (
// EthereumTxData contains ethereum specific transaction data
type EthereumTxData struct {
Status TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown
Nonce uint64 `json:"nonce"`
GasLimit *big.Int `json:"gaslimit"`
GasUsed *big.Int `json:"gasused"`
GasPrice *big.Int `json:"gasprice"`
Data string `json:"data"`
Status TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown
Nonce uint64 `json:"nonce"`
GasLimit *big.Int `json:"gaslimit"`
GasUsed *big.Int `json:"gasused"`
GasPrice *big.Int `json:"gasprice"`
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
@ -511,6 +547,10 @@ func GetEthereumTxDataFromSpecificData(coinSpecificData interface{}) *EthereumTx
etd.Status = TxStatusFailure
}
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

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

View File

@ -1,261 +1,530 @@
// 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
/*
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
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ProtoCompleteTransaction struct {
BlockNumber uint32 `protobuf:"varint,1,opt,name=BlockNumber" json:"BlockNumber,omitempty"`
BlockTime uint64 `protobuf:"varint,2,opt,name=BlockTime" json:"BlockTime,omitempty"`
Tx *ProtoCompleteTransaction_TxType `protobuf:"bytes,3,opt,name=Tx" json:"Tx,omitempty"`
Receipt *ProtoCompleteTransaction_ReceiptType `protobuf:"bytes,4,opt,name=Receipt" json:"Receipt,omitempty"`
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
BlockNumber uint32 `protobuf:"varint,1,opt,name=BlockNumber,proto3" json:"BlockNumber,omitempty"`
BlockTime uint64 `protobuf:"varint,2,opt,name=BlockTime,proto3" json:"BlockTime,omitempty"`
Tx *ProtoCompleteTransaction_TxType `protobuf:"bytes,3,opt,name=Tx,proto3" json:"Tx,omitempty"`
Receipt *ProtoCompleteTransaction_ReceiptType `protobuf:"bytes,4,opt,name=Receipt,proto3" json:"Receipt,omitempty"`
}
func (m *ProtoCompleteTransaction) Reset() { *m = ProtoCompleteTransaction{} }
func (m *ProtoCompleteTransaction) String() string { return proto.CompactTextString(m) }
func (*ProtoCompleteTransaction) ProtoMessage() {}
func (*ProtoCompleteTransaction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (x *ProtoCompleteTransaction) Reset() {
*x = ProtoCompleteTransaction{}
if protoimpl.UnsafeEnabled {
mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (m *ProtoCompleteTransaction) GetBlockNumber() uint32 {
if m != nil {
return m.BlockNumber
func (x *ProtoCompleteTransaction) String() string {
return protoimpl.X.MessageStringOf(x)
}
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
}
func (m *ProtoCompleteTransaction) GetBlockTime() uint64 {
if m != nil {
return m.BlockTime
func (x *ProtoCompleteTransaction) GetBlockTime() uint64 {
if x != nil {
return x.BlockTime
}
return 0
}
func (m *ProtoCompleteTransaction) GetTx() *ProtoCompleteTransaction_TxType {
if m != nil {
return m.Tx
func (x *ProtoCompleteTransaction) GetTx() *ProtoCompleteTransaction_TxType {
if x != nil {
return x.Tx
}
return nil
}
func (m *ProtoCompleteTransaction) GetReceipt() *ProtoCompleteTransaction_ReceiptType {
if m != nil {
return m.Receipt
func (x *ProtoCompleteTransaction) GetReceipt() *ProtoCompleteTransaction_ReceiptType {
if x != nil {
return x.Receipt
}
return nil
}
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"`
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"`
Payload []byte `protobuf:"bytes,5,opt,name=Payload,proto3" json:"Payload,omitempty"`
Hash []byte `protobuf:"bytes,6,opt,name=Hash,proto3" json:"Hash,omitempty"`
To []byte `protobuf:"bytes,7,opt,name=To,proto3" json:"To,omitempty"`
From []byte `protobuf:"bytes,8,opt,name=From,proto3" json:"From,omitempty"`
TransactionIndex uint32 `protobuf:"varint,9,opt,name=TransactionIndex" json:"TransactionIndex,omitempty"`
TransactionIndex uint32 `protobuf:"varint,9,opt,name=TransactionIndex,proto3" json:"TransactionIndex,omitempty"`
}
func (m *ProtoCompleteTransaction_TxType) Reset() { *m = ProtoCompleteTransaction_TxType{} }
func (m *ProtoCompleteTransaction_TxType) String() string { return proto.CompactTextString(m) }
func (*ProtoCompleteTransaction_TxType) ProtoMessage() {}
func (x *ProtoCompleteTransaction_TxType) Reset() {
*x = ProtoCompleteTransaction_TxType{}
if protoimpl.UnsafeEnabled {
mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
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) {
return fileDescriptor0, []int{0, 0}
return file_bchain_coins_eth_ethtx_proto_rawDescGZIP(), []int{0, 0}
}
func (m *ProtoCompleteTransaction_TxType) GetAccountNonce() uint64 {
if m != nil {
return m.AccountNonce
func (x *ProtoCompleteTransaction_TxType) GetAccountNonce() uint64 {
if x != nil {
return x.AccountNonce
}
return 0
}
func (m *ProtoCompleteTransaction_TxType) GetGasPrice() []byte {
if m != nil {
return m.GasPrice
func (x *ProtoCompleteTransaction_TxType) GetGasPrice() []byte {
if x != nil {
return x.GasPrice
}
return nil
}
func (m *ProtoCompleteTransaction_TxType) GetGasLimit() uint64 {
if m != nil {
return m.GasLimit
func (x *ProtoCompleteTransaction_TxType) GetGasLimit() uint64 {
if x != nil {
return x.GasLimit
}
return 0
}
func (m *ProtoCompleteTransaction_TxType) GetValue() []byte {
if m != nil {
return m.Value
func (x *ProtoCompleteTransaction_TxType) GetValue() []byte {
if x != nil {
return x.Value
}
return nil
}
func (m *ProtoCompleteTransaction_TxType) GetPayload() []byte {
if m != nil {
return m.Payload
func (x *ProtoCompleteTransaction_TxType) GetPayload() []byte {
if x != nil {
return x.Payload
}
return nil
}
func (m *ProtoCompleteTransaction_TxType) GetHash() []byte {
if m != nil {
return m.Hash
func (x *ProtoCompleteTransaction_TxType) GetHash() []byte {
if x != nil {
return x.Hash
}
return nil
}
func (m *ProtoCompleteTransaction_TxType) GetTo() []byte {
if m != nil {
return m.To
func (x *ProtoCompleteTransaction_TxType) GetTo() []byte {
if x != nil {
return x.To
}
return nil
}
func (m *ProtoCompleteTransaction_TxType) GetFrom() []byte {
if m != nil {
return m.From
func (x *ProtoCompleteTransaction_TxType) GetFrom() []byte {
if x != nil {
return x.From
}
return nil
}
func (m *ProtoCompleteTransaction_TxType) GetTransactionIndex() uint32 {
if m != nil {
return m.TransactionIndex
func (x *ProtoCompleteTransaction_TxType) GetTransactionIndex() uint32 {
if x != nil {
return x.TransactionIndex
}
return 0
}
type ProtoCompleteTransaction_ReceiptType struct {
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" json:"Log,omitempty"`
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
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 (m *ProtoCompleteTransaction_ReceiptType) String() string { return proto.CompactTextString(m) }
func (*ProtoCompleteTransaction_ReceiptType) ProtoMessage() {}
func (x *ProtoCompleteTransaction_ReceiptType) Reset() {
*x = ProtoCompleteTransaction_ReceiptType{}
if protoimpl.UnsafeEnabled {
mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
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) {
return fileDescriptor0, []int{0, 1}
return file_bchain_coins_eth_ethtx_proto_rawDescGZIP(), []int{0, 1}
}
func (m *ProtoCompleteTransaction_ReceiptType) GetGasUsed() []byte {
if m != nil {
return m.GasUsed
func (x *ProtoCompleteTransaction_ReceiptType) GetGasUsed() []byte {
if x != nil {
return x.GasUsed
}
return nil
}
func (m *ProtoCompleteTransaction_ReceiptType) GetStatus() []byte {
if m != nil {
return m.Status
func (x *ProtoCompleteTransaction_ReceiptType) GetStatus() []byte {
if x != nil {
return x.Status
}
return nil
}
func (m *ProtoCompleteTransaction_ReceiptType) GetLog() []*ProtoCompleteTransaction_ReceiptType_LogType {
if m != nil {
return m.Log
func (x *ProtoCompleteTransaction_ReceiptType) GetLog() []*ProtoCompleteTransaction_ReceiptType_LogType {
if x != nil {
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
}
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"`
Data []byte `protobuf:"bytes,2,opt,name=Data,proto3" json:"Data,omitempty"`
Topics [][]byte `protobuf:"bytes,3,rep,name=Topics,proto3" json:"Topics,omitempty"`
}
func (m *ProtoCompleteTransaction_ReceiptType_LogType) Reset() {
*m = ProtoCompleteTransaction_ReceiptType_LogType{}
func (x *ProtoCompleteTransaction_ReceiptType_LogType) Reset() {
*x = ProtoCompleteTransaction_ReceiptType_LogType{}
if protoimpl.UnsafeEnabled {
mi := &file_bchain_coins_eth_ethtx_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
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 (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) {
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 {
if m != nil {
return m.Address
func (x *ProtoCompleteTransaction_ReceiptType_LogType) GetAddress() []byte {
if x != nil {
return x.Address
}
return nil
}
func (m *ProtoCompleteTransaction_ReceiptType_LogType) GetData() []byte {
if m != nil {
return m.Data
func (x *ProtoCompleteTransaction_ReceiptType_LogType) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
func (m *ProtoCompleteTransaction_ReceiptType_LogType) GetTopics() [][]byte {
if m != nil {
return m.Topics
func (x *ProtoCompleteTransaction_ReceiptType_LogType) GetTopics() [][]byte {
if x != nil {
return x.Topics
}
return nil
}
func init() {
proto.RegisterType((*ProtoCompleteTransaction)(nil), "eth.ProtoCompleteTransaction")
proto.RegisterType((*ProtoCompleteTransaction_TxType)(nil), "eth.ProtoCompleteTransaction.TxType")
proto.RegisterType((*ProtoCompleteTransaction_ReceiptType)(nil), "eth.ProtoCompleteTransaction.ReceiptType")
proto.RegisterType((*ProtoCompleteTransaction_ReceiptType_LogType)(nil), "eth.ProtoCompleteTransaction.ReceiptType.LogType")
var File_bchain_coins_eth_ethtx_proto protoreflect.FileDescriptor
var file_bchain_coins_eth_ethtx_proto_rawDesc = []byte{
0x0a, 0x1c, 0x62, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x2f, 0x65,
0x74, 0x68, 0x2f, 0x65, 0x74, 0x68, 0x74, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdd,
0x06, 0x0a, 0x18, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65,
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{
// 409 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xdf, 0x8a, 0xd4, 0x30,
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,
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,
0x86, 0x24, 0xca, 0x55, 0xe7, 0xdf, 0x67, 0x40, 0x77, 0x1e, 0x37, 0xa6, 0x6e, 0x0f, 0xca, 0x29,
0xde, 0x89, 0xc6, 0x0a, 0xe9, 0xb4, 0x69, 0x48, 0x0e, 0xcb, 0xb7, 0x07, 0x23, 0xbf, 0xdd, 0xf4,
0xf5, 0x5e, 0x75, 0x34, 0xca, 0xa3, 0xf5, 0x23, 0x76, 0x2c, 0x91, 0x33, 0x38, 0x45, 0xe4, 0xba,
0x56, 0x34, 0xce, 0xa3, 0x75, 0xca, 0x7e, 0x0b, 0xe4, 0x0d, 0xc4, 0x7c, 0xa0, 0x49, 0x1e, 0xad,
0x97, 0x97, 0xcf, 0x0b, 0xe5, 0xaa, 0xe2, 0x5f, 0x5b, 0x15, 0x7c, 0xe0, 0x77, 0xad, 0x62, 0x31,
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,
0x8d, 0xbb, 0x31, 0x8d, 0x54, 0x58, 0x23, 0x65, 0x7f, 0x68, 0x64, 0x05, 0x27, 0xd7, 0xc2, 0xee,
0x3a, 0x2d, 0xc7, 0x1a, 0x19, 0xbb, 0xe7, 0xf0, 0x6e, 0xab, 0x6b, 0xed, 0xb0, 0x4b, 0xca, 0xee,
0x99, 0x3c, 0x85, 0xd9, 0x57, 0x71, 0xe8, 0x15, 0x26, 0xcd, 0xd8, 0x08, 0x84, 0xc2, 0x62, 0x27,
0xee, 0x0e, 0x46, 0x94, 0x74, 0x86, 0xfa, 0x84, 0x84, 0x40, 0xfa, 0x41, 0xd8, 0x8a, 0xce, 0x51,
0xc6, 0x35, 0x79, 0x0c, 0x31, 0x37, 0x74, 0x81, 0x4a, 0xcc, 0x8d, 0x9f, 0x79, 0xdf, 0x99, 0x9a,
0x9e, 0x8c, 0x33, 0x7e, 0x4d, 0x5e, 0xc2, 0x93, 0xa3, 0xca, 0x1f, 0x9b, 0x52, 0x0d, 0xf4, 0x14,
0xaf, 0xe3, 0x2f, 0x7d, 0xf5, 0x23, 0x82, 0xe5, 0xd1, 0x99, 0xf8, 0x34, 0xd7, 0xc2, 0x7e, 0xb1,
0xaa, 0xc4, 0xea, 0x19, 0x9b, 0x90, 0x3c, 0x83, 0xf9, 0x67, 0x27, 0x5c, 0x6f, 0x43, 0xe7, 0x40,
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,
0x96, 0x16, 0x03, 0x64, 0x2c, 0xd0, 0x7e, 0x8e, 0xbf, 0xed, 0xeb, 0x5f, 0x01, 0x00, 0x00, 0xff,
0xff, 0xc2, 0x69, 0x8d, 0xdf, 0xd6, 0x02, 0x00, 0x00,
func file_bchain_coins_eth_ethtx_proto_rawDescGZIP() []byte {
file_bchain_coins_eth_ethtx_proto_rawDescOnce.Do(func() {
file_bchain_coins_eth_ethtx_proto_rawDescData = protoimpl.X.CompressGZIP(file_bchain_coins_eth_ethtx_proto_rawDescData)
})
return file_bchain_coins_eth_ethtx_proto_rawDescData
}
var file_bchain_coins_eth_ethtx_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_bchain_coins_eth_ethtx_proto_goTypes = []interface{}{
(*ProtoCompleteTransaction)(nil), // 0: ProtoCompleteTransaction
(*ProtoCompleteTransaction_TxType)(nil), // 1: ProtoCompleteTransaction.TxType
(*ProtoCompleteTransaction_ReceiptType)(nil), // 2: ProtoCompleteTransaction.ReceiptType
(*ProtoCompleteTransaction_ReceiptType_LogType)(nil), // 3: ProtoCompleteTransaction.ReceiptType.LogType
}
var file_bchain_coins_eth_ethtx_proto_depIdxs = []int32{
1, // 0: ProtoCompleteTransaction.Tx:type_name -> ProtoCompleteTransaction.TxType
2, // 1: ProtoCompleteTransaction.Receipt:type_name -> ProtoCompleteTransaction.ReceiptType
3, // 2: ProtoCompleteTransaction.ReceiptType.Log:type_name -> ProtoCompleteTransaction.ReceiptType.LogType
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_bchain_coins_eth_ethtx_proto_init() }
func file_bchain_coins_eth_ethtx_proto_init() {
if File_bchain_coins_eth_ethtx_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_bchain_coins_eth_ethtx_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ProtoCompleteTransaction); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_bchain_coins_eth_ethtx_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ProtoCompleteTransaction_TxType); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_bchain_coins_eth_ethtx_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ProtoCompleteTransaction_ReceiptType); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_bchain_coins_eth_ethtx_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ProtoCompleteTransaction_ReceiptType_LogType); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_bchain_coins_eth_ethtx_proto_msgTypes[2].OneofWrappers = []interface{}{}
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";
package eth;
message ProtoCompleteTransaction {
message TxType {
uint64 AccountNonce = 1;
bytes GasPrice = 2;
uint64 GasLimit = 3;
bytes Value = 4;
bytes Payload = 5;
bytes Hash = 6;
bytes To = 7;
bytes From = 8;
uint32 TransactionIndex = 9;
}
message ReceiptType {
message LogType {
bytes Address = 1;
bytes Data = 2;
repeated bytes Topics = 3;
}
bytes GasUsed = 1;
bytes Status = 2;
repeated LogType Log = 3;
}
uint32 BlockNumber = 1;
uint64 BlockTime = 2;
TxType Tx = 3;
ReceiptType Receipt = 4;
}
option go_package = "bchain/coins/eth";
message ProtoCompleteTransaction {
message TxType {
uint64 AccountNonce = 1;
bytes GasPrice = 2;
uint64 GasLimit = 3;
bytes Value = 4;
bytes Payload = 5;
bytes Hash = 6;
bytes To = 7;
bytes From = 8;
uint32 TransactionIndex = 9;
}
message ReceiptType {
message LogType {
bytes Address = 1;
bytes Data = 2;
repeated bytes Topics = 3;
}
bytes GasUsed = 1;
bytes Status = 2;
repeated LogType Log = 3;
optional bytes L1Fee = 4;
optional bytes L1FeeScalar = 5;
optional bytes L1GasPrice = 6;
optional bytes L1GasUsed = 7;
}
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 (
OpZeroCoinMint = 0xc1
OpZeroCoinSpend = 0xc2
OpSigmaMint = 0xc3
OpSigmaSpend = 0xc4
OpLelantusMint = 0xc5
OpLelantusJMint = 0xc6
OpLelantusJoinSplit = 0xc7
OpZeroCoinMint = 0xc1
OpZeroCoinSpend = 0xc2
OpSigmaMint = 0xc3
OpSigmaSpend = 0xc4
OpLelantusMint = 0xc5
OpLelantusJMint = 0xc6
OpLelantusJoinSplit = 0xc7
OpLelantusJoinSplitPayload = 0xc9
MainnetMagic wire.BitcoinNet = 0xe3d9fef1
@ -194,7 +194,6 @@ func (p *FiroParser) ParseBlock(b []byte) (*bchain.Block, error) {
break
}
}
if !isAllZero {
// hash data
@ -344,7 +343,7 @@ type MTPHashDataRoot struct {
}
type MTPHashData struct {
BlockMTP [128][128]uint64
BlockMTP [128][128]uint64
}
type MTPBlockHeader struct {

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 (
"bytes"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"math/big"
@ -13,7 +15,6 @@ import (
"github.com/martinboehm/btcutil/chaincfg"
"github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/bchain/coins/btc"
"github.com/trezor/blockbook/bchain/coins/utils"
)
// magic numbers
@ -100,7 +101,12 @@ func (p *PivXParser) ParseBlock(b []byte) (*bchain.Block, error) {
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 {
return nil, errors.Annotatef(err, "DecodeTransactions")
}
@ -255,6 +261,90 @@ func (p *PivXParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain
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
func isZeroCoinMintScript(signatureScript []byte) bool {
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)
return
}
// ignore witness unpacking
for i := range got.Vin {
got.Vin[i].Witness = nil
}
if !reflect.DeepEqual(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 (
"encoding/hex"
"errors"
"fmt"
"math/big"
"time"
"github.com/golang/glog"
"github.com/martinboehm/btcutil/gcs"
"github.com/juju/errors"
)
type chanInputPayload struct {
@ -16,14 +14,6 @@ type chanInputPayload struct {
index int
}
type filterScriptsType int
const (
filterScriptsInvalid = filterScriptsType(iota)
filterScriptsAll
filterScriptsTaproot
)
// MempoolBitcoinType is mempool handle.
type MempoolBitcoinType struct {
BaseMempool
@ -31,19 +21,13 @@ type MempoolBitcoinType struct {
chanAddrIndex chan txidio
AddrDescForOutpoint AddrDescForOutpointFunc
golombFilterP uint8
golombFilterM uint64
filterScripts filterScriptsType
filterScripts string
useZeroedKey bool
}
// 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
func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golombFilterP uint8, filterScripts string) *MempoolBitcoinType {
filterScriptsType := filterScriptsToScriptsType(filterScripts)
if filterScriptsType == filterScriptsInvalid {
glog.Error("Invalid filterScripts ", filterScripts, ", switching off golomb filter")
golombFilterP = 0
}
golombFilterM := uint64(1 << golombFilterP)
func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golombFilterP uint8, filterScripts string, useZeroedKey bool) *MempoolBitcoinType {
m := &MempoolBitcoinType{
BaseMempool: BaseMempool{
chain: chain,
@ -53,8 +37,8 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golomb
chanTxid: make(chan string, 1),
chanAddrIndex: make(chan txidio, 1),
golombFilterP: golombFilterP,
golombFilterM: golombFilterM,
filterScripts: filterScriptsType,
filterScripts: filterScripts,
useZeroedKey: useZeroedKey,
}
for i := 0; i < workers; i++ {
go func(i int) {
@ -81,16 +65,6 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golomb
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 {
var addrDesc AddressDescriptor
var value *big.Int
@ -125,56 +99,21 @@ func (m *MempoolBitcoinType) getInputAddress(payload *chanInputPayload) *addrInd
}
func isTaproot(addrDesc AddressDescriptor) bool {
if len(addrDesc) == 34 && addrDesc[0] == 0x51 && addrDesc[1] == 0x20 {
return true
func (m *MempoolBitcoinType) computeGolombFilter(mtx *MempoolTx, tx *Tx) string {
gf, _ := NewGolombFilter(m.golombFilterP, m.filterScripts, mtx.Txid, m.useZeroedKey)
if gf == nil || !gf.Enabled {
return ""
}
return false
}
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 _, vin := range mtx.Vin {
gf.AddAddrDesc(vin.AddrDesc, tx)
}
for i := range mtx.Vout {
vout := &mtx.Vout[i]
for _, vout := range mtx.Vout {
b, err := hex.DecodeString(vout.ScriptPubKey.Hex)
if err == nil {
if m.filterScripts == filterScriptsAll || (m.filterScripts == filterScriptsTaproot && isTaproot(b)) {
s := string(b)
if _, found := uniqueScripts[s]; !found {
filterData = append(filterData, b)
uniqueScripts[s] = struct{}{}
}
}
gf.AddAddrDesc(b, tx)
}
}
if len(filterData) == 0 {
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 ""
}
fb := gf.Compute()
return hex.EncodeToString(fb)
}
@ -231,7 +170,7 @@ func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan chanInputPay
}
var golombFilter string
if m.golombFilterP > 0 {
golombFilter = m.computeGolombFilter(mtx)
golombFilter = m.computeGolombFilter(mtx, tx)
}
if m.OnNewTx != nil {
m.OnNewTx(mtx)
@ -301,8 +240,8 @@ func (m *MempoolBitcoinType) Resync() (int, error) {
// GetTxidFilterEntries returns all mempool entries with golomb filter from
func (m *MempoolBitcoinType) GetTxidFilterEntries(filterScripts string, fromTimestamp uint32) (MempoolTxidFilterEntries, error) {
if m.filterScripts != filterScriptsToScriptsType(filterScripts) {
return MempoolTxidFilterEntries{}, errors.New(fmt.Sprint("Unsupported script filter ", filterScripts))
if m.filterScripts != filterScripts {
return MempoolTxidFilterEntries{}, errors.Errorf("Unsupported script filter %s", filterScripts)
}
m.mux.Lock()
entries := make(map[string]string)
@ -312,5 +251,5 @@ func (m *MempoolBitcoinType) GetTxidFilterEntries(filterScripts string, fromTime
}
}
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")
m := &MempoolBitcoinType{
golombFilterP: 20,
golombFilterM: uint64(1 << 20),
filterScripts: filterScriptsTaproot,
filterScripts: "taproot",
}
golombFilterM := GetGolombParamM(m.golombFilterP)
tests := []struct {
name string
mtx MempoolTx
@ -194,13 +194,13 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := m.computeGolombFilter(&tt.mtx)
got := m.computeGolombFilter(&tt.mtx, nil)
if got != tt.want {
t.Errorf("MempoolBitcoinType.computeGolombFilter() = %v, want %v", got, tt.want)
}
if got != "" {
// 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 {
t.Errorf("gcs.BuildGCSFilter() unexpected error %v", err)
}
@ -211,8 +211,8 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
if err != nil {
t.Errorf("filter.Match vin[%d] unexpected error %v", i, err)
}
if match != isTaproot(tt.mtx.Vin[i].AddrDesc) {
t.Errorf("filter.Match vin[%d] got %v, want %v", i, 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, tt.mtx.Vin[i].AddrDesc.IsTaproot())
}
}
// check that the vout scripts match the filter
@ -222,8 +222,8 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
if err != nil {
t.Errorf("filter.Match vout[%d] unexpected error %v", i, err)
}
if match != isTaproot(s) {
t.Errorf("filter.Match vout[%d] got %v, want %v", i, match, isTaproot(s))
if match != AddressDescriptor(s).IsTaproot() {
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
@ -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
}
// AddTransactionToMempool adds transactions to mempool
func (m *MempoolEthereumType) AddTransactionToMempool(txid string) {
// 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) bool {
m.mux.Lock()
_, exists := m.txEntries[txid]
m.mux.Unlock()
@ -143,7 +143,7 @@ func (m *MempoolEthereumType) AddTransactionToMempool(txid string) {
if !exists {
entry, ok := m.createTxEntry(txid, uint32(time.Now().Unix()))
if !ok {
return
return false
}
m.mux.Lock()
m.txEntries[txid] = entry
@ -152,6 +152,7 @@ func (m *MempoolEthereumType) AddTransactionToMempool(txid string) {
}
m.mux.Unlock()
}
return !exists
}
// RemoveTransactionFromMempool removes transaction from mempool

View File

@ -57,6 +57,7 @@ type Vin struct {
ScriptSig ScriptSig `json:"scriptSig"`
Sequence uint32 `json:"sequence"`
Addresses []string `json:"addresses"`
Witness [][]byte `json:"-"`
}
// ScriptPubKey contains data about output script
@ -130,7 +131,8 @@ type TokenTypeName string
// Token types
const (
UnknownTokenType TokenTypeName = ""
UnknownTokenType TokenTypeName = ""
UnhandledTokenType TokenTypeName = "-"
// XPUBAddressTokenType is address derived from xpub
XPUBAddressTokenType TokenTypeName = "XPUBAddress"
@ -226,6 +228,13 @@ func (ad AddressDescriptor) String() string {
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
func AddressDescriptorFromString(s string) (AddressDescriptor, error) {
if len(s) > 3 && s[0:3] == "ad:" {
@ -266,8 +275,10 @@ type XpubDescriptor struct {
type MempoolTxidEntries []MempoolTxidEntry
// MempoolTxidFilterEntries is a map of txids to mempool golomb filters
// Also contains a flag whether constant zeroed key was used when calculating the filters
type MempoolTxidFilterEntries struct {
Entries map[string]string `json:"entries,omitempty"`
Entries map[string]string `json:"entries,omitempty"`
UsedZeroedKey bool `json:"usedZeroedKey,omitempty"`
}
// OnNewBlockFunc is used to send notification about a new block
@ -323,6 +334,10 @@ type BlockChain interface {
EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error)
EthereumTypeEstimateGas(params map[string]interface{}) (uint64, 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)
}

View File

@ -122,9 +122,13 @@ type RpcLog struct {
// RpcLog is returned by eth_getTransactionReceipt
type RpcReceipt struct {
GasUsed string `json:"gasUsed"`
Status string `json:"status"`
Logs []*RpcLog `json:"logs"`
GasUsed string `json:"gasUsed"`
Status string `json:"status"`
Logs []*RpcLog `json:"logs"`
L1Fee string `json:"l1Fee,omitempty"`
L1FeeScalar string `json:"l1FeeScalar,omitempty"`
L1GasPrice string `json:"l1GasPrice,omitempty"`
L1GasUsed string `json:"l1GasUsed,omitempty"`
}
// EthereumSpecificData contains data specific to Ethereum transactions
@ -146,3 +150,16 @@ type EthereumBlockSpecificData struct {
AddressAliasRecords []AddressAliasRecord
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;
gasLimit: number;
gasUsed?: number;
gasPrice: string;
gasPrice?: string;
l1Fee?: number;
l1FeeScalar?: string;
l1GasPrice?: string;
l1GasUsed?: number;
data?: string;
parsedData?: EthereumParsedInputData;
internalTransfers?: EthereumInternalTransfer[];
@ -46,9 +50,9 @@ export interface TokenTransfer {
from: string;
to: string;
contract: string;
name: string;
symbol: string;
decimals: number;
name?: string;
symbol?: string;
decimals?: number;
value?: string;
multiTokenValues?: MultiTokenValue[];
}
@ -109,6 +113,17 @@ export interface FeeStats {
averageFeePerKb: 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 {
type: string;
contract: string;
@ -161,6 +176,7 @@ export interface Address {
contractInfo?: ContractInfo;
erc20Contract?: ContractInfo;
addressAliases?: { [key: string]: AddressAlias };
stakingPools?: StakingPool[];
}
export interface Utxo {
txid: string;
@ -245,6 +261,7 @@ export interface InternalStateColumn {
}
export interface BlockbookInfo {
coin: string;
network: string;
host: string;
version: string;
gitCommit: string;
@ -264,6 +281,7 @@ export interface BlockbookInfo {
currentFiatRatesTime?: string;
historicalFiatRatesTime?: string;
historicalTokenFiatRatesTime?: string;
supportedStakingPools?: string[];
dbSizeFromColumns?: number;
dbColumns?: InternalStateColumn[];
about: string;
@ -338,6 +356,7 @@ export interface WsBackendInfo {
export interface WsInfoRes {
name: string;
shortcut: string;
network: string;
decimals: number;
version: string;
bestHeight: number;
@ -357,6 +376,17 @@ export interface WsBlockReq {
pageSize?: 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 {
descriptor: string;
}
@ -416,7 +446,17 @@ export interface WsFiatRatesTickersListReq {
export interface WsMempoolFiltersReq {
scriptType: string;
fromTimestamp: number;
M?: number;
}
export interface WsRpcCallReq {
from?: string;
to: string;
data: string;
}
export interface WsRpcCallRes {
data: string;
}
export interface MempoolTxidFilterEntries {
entries?: { [key: string]: string };
usedZeroedKey?: boolean;
}

View File

@ -2,7 +2,6 @@ package main
import (
"context"
"encoding/json"
"flag"
"log"
"math/rand"
@ -11,6 +10,7 @@ import (
"os"
"os/signal"
"runtime/debug"
"strconv"
"strings"
"syscall"
"time"
@ -152,30 +152,19 @@ func mainWithExitCode() int {
return exitCodeOK
}
if *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)
config, err := common.GetConfig(*configFile)
if err != nil {
glog.Error("config: ", err)
return exitCodeFatal
}
metrics, err = common.GetMetrics(coin)
metrics, err = common.GetMetrics(config.CoinName)
if err != nil {
glog.Error("metrics: ", err)
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)
return exitCodeFatal
}
@ -187,7 +176,7 @@ func mainWithExitCode() int {
}
defer index.Close()
internalState, err = newInternalState(coin, coinShortcut, coinLabel, index, *enableSubNewTx)
internalState, err = newInternalState(config, index, *enableSubNewTx)
if err != nil {
glog.Error("internalState: ", err)
return exitCodeFatal
@ -279,7 +268,7 @@ func mainWithExitCode() int {
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)
return exitCodeFatal
}
@ -368,7 +357,7 @@ func mainWithExitCode() int {
if internalServer != nil || publicServer != nil || chain != nil {
// 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)
}
@ -501,16 +490,12 @@ func blockbookAppInfoMetric(db *db.RocksDB, chain bchain.BlockChain, txCache *db
return nil
}
func newInternalState(coin, coinShortcut, coinLabel string, d *db.RocksDB, enableSubNewTx bool) (*common.InternalState, error) {
is, err := d.LoadInternalState(coin)
func newInternalState(config *common.Config, d *db.RocksDB, enableSubNewTx bool) (*common.InternalState, error) {
is, err := d.LoadInternalState(config)
if err != nil {
return nil, err
}
is.CoinShortcut = coinShortcut
if coinLabel == "" {
coinLabel = coin
}
is.CoinLabel = coinLabel
is.EnableSubNewTx = enableSubNewTx
name, err := os.Hostname()
if err != nil {
@ -521,6 +506,12 @@ func newInternalState(coin, coinShortcut, coinLabel string, d *db.RocksDB, enabl
}
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
}
@ -702,21 +693,11 @@ func computeFeeStats(stopCompute chan os.Signal, blockFrom, blockTo int, db *db.
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 {
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 {
fbsd, err := fourbyte.NewFourByteSignaturesDownloader(db, config.FourByteSignatures)
if err != nil {

View File

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

View File

@ -38,5 +38,4 @@ prepare-sources:
mkdir -p $(BLOCKBOOK_BASE)
cp -r /src $(BLOCKBOOK_SRC)
cd $(BLOCKBOOK_SRC) && go mod download
sed -i 's/wsMessageSizeLimit\ =\ 15\ \*\ 1024\ \*\ 1024/wsMessageSizeLimit = 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

View File

@ -6,9 +6,19 @@ ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
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
# install docker cli
ARG DOCKER_VERSION
RUN if [ -z "$DOCKER_VERSION" ]; then echo "DOCKER_VERSION is a required build arg" && exit 1; fi
RUN wget -O docker.tgz "https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz" && \
tar -xzf docker.tgz --strip 1 -C /usr/local/bin/ && \
rm docker.tgz && \
docker --version
ADD gpg-keys /tmp/gpg-keys
RUN gpg --batch --import /tmp/gpg-keys/*

View File

@ -1,5 +1,103 @@
-----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
CRBSUnvtq+h5hKMFD/9zrGMZh6da8RBO1+cU4LZi0KDcFPd0dMHIpnvJ0w1oI3aY
WBmtKbLm5lQZ9OqgRp3MTFZPXbnMrfjqNwmRkEW5V1RjA24MMXjCb5wdD7ZMQ3VN
sXMi4WEJ61o1uVobrBSowmtBJMXyx3tGcHOXOpIXzG+HVx2gnlqFytK621PmSjlA
If498EpqQriIqoEuVkeoyQ0fhSl1d5/gnfP629i1ERnyRN8htJ+J6CJUuHNRPfST
pqvfyrLQTvPSDC7tTNuTY47EKEy3QP1s+R6hLFVbBTxBK1lJVrxBpBqLFCdRQswX
7Xv2p6syn9ia3DmBpw2Bfh8ySPmgVwgonZODXTRAo0uYV3hdeJgblVt9XhSa9C9z
DYgrjXR3EGT+N3GYkjdXqdoOnZzsaUD7CQLnobW4ZIjM+EtwP7QBXv89liqW0ppK
RuZOJ8Zycbiqa+ThK0r2gFm8j7HZWBNE/osVuschQ89d1FmwUKmcMCba/IbNDDHG
JdTr6fJvbXdyF183GZhvSlXdOMPNhcX4dRUcxkooMcUjbnERHKb6q1AKvoIYceb+
/WaO/RUzCWCRbIEdYKxqYFuKRvuMHcR/F0fGeUUNsujLBuL5xSdZmNDpOrefTH0R
ZDLdTtKATr4GbkVZGBtXvWmd6c5NdJLCMO/n1V6j2ZdpbRBsvB/tl0emdXUvr7kB
DQRlqfCJAQgAqVzAtdH5r5+WezUAbKxwxYopkMJauEhjSE08CLFr8MHiImcIKY2S
rtUTKA+bJYdaaTE1HqIhPTg18wo166/HKdvRR2vi7ACvb8sunAg0/H1Vq6d+y262
4mLYqoRMQqBBJds0TIC4IDawJFjrkNT/S36jLtaEifENgskTQgashamRFYnwSgKv
BKyobdiRMh26GGoxZLRiZVehCR0FQqchd8GpFOJsSANyX2Hlyi9i8ZhU+Ld2PcPK
nmfkFsS35Dqjm7IkDLpMx7kwjr5YlTcIpQhENbJ68dAzzG9A3mV7Wojfv3Dzpz3j
9wXvoj2EYDYPvNAyftQlfrWKe3r8wcjBKQARAQABiQE2BBgBCAAgFiEEYKz3C/cS
ZFBJ7m8V7+rxZoYiX2QFAmWp8IkCGyAACgkQ7+rxZoYiX2ThTAf/cNb4kEhk+Wjj
FzRHNUinzwA/7+YT5gbEnVh/1x+IpeYpnnuVEdOhNFxz76SL3dtDF8ciIhWxsE4b
v6hpdqcps1Hnq2dkbZ+z9T1r8+IZ03eyYXOo7kZtCwX4UODFwFHi2WaZpCCgOvLX
pA8tKJ04VfIBjp3shlUo+vCROgMouOpJgaLs80LQpoHEB8enHIuNByqWhHl+D4DV
z2l4TPL3HQaCMcW2KCexVz1+9pnPT2hf8DQXrxmchC1CnJVgV64yDzmjhND9C2Hw
OPS0JcBhAzB1FqtVZGYfQSkE5FAA7FLN/IYcCDhxYKVzdKay6m/JL8cbcSpQqLWO
/MR86YndjbkBDQRlqfCJAQgArkCO/giMQ8ReApeP/B4GoNiWlax5bFqMQVPevVix
QfAJ7IQ+8W/JxFmV2F0U2CQU38u9c0kAhYtFk/H/0cC/aEnqKPT6SGpZ4+W7Ehmp
ngSx+1r0sVV1cuZcUncetQeK2IZsBYCCf9XjZIqgFMDygnfM5TvPUyj5qiATxIxV
9bRjI/oNYVPngfnot7VZafVq/yW5+JlYx8u0rKsn5ikpzSDV8IrHmehydrHUUhYj
6/y6ChDzs2ZAq+qoCgFov5z7VzczzEybfPTbAwXpDahCHxF2V6k81c5ZeKEr9l3K
l8Kcc2ybwRe2MbePYCSDHle4GRaYExTXjYnkgyOKtr5YgwARAQABiQE2BBgBCAAg
FiEEYKz3C/cSZFBJ7m8V7+rxZoYiX2QFAmWp8IkCGwwACgkQ7+rxZoYiX2Rx4gf+
MmibxLDOnVrMv2joky9DJajtZow8ayipXjU1AgIjuvcoMV/GBn8OMx3IAXHVGpyV
16jJ00X8Q+MAwVxd8+7OUoOSFECBqECv5iD4q0OqcZqFx7EyC7iDVUfY9IG0EKjV
4AOzP/azJgT916t3OqcXXDJ2wIUbDIvUQUwTMjX0Fw7OQNGYlHS709UF3y0DwBdq
pCxj1y74D9XzjvWHYxlKI5X8Lt2QW+xsGKkaRp5aIXn6MUnpmdIFZEcTj8s553+m
iqlYokmTvkTa4cQsgwC6RqkVsYopJrYsKnDs/l4/m+4TrPdforaD6mKNKzlsLJSj
gZfWLfoIul+B10SwJHXuoQ==
=/A3N
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBF1ULyUBEADFFliU0Hr+PRCQNT9/9ZEhZtLmMMu7tai3VCxhmrHrOpNJJHqX
f1/CeUyBhmCvXpKIpAAbH66l/Uc9GH5UgMZ19gMyGa3q3QJn9A6RR9ud4ALRg60P
fmYTAci+6Luko7bqTzkS+fYOUSy/LY57s5ANTpveE+iTsBd5grXczCxaYYnthKKA
@ -11,55 +109,317 @@ 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
96I=
=85Kq
-----END PGP PUBLIC KEY BLOCK-----
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
+TwNIGJdDeJ+ywhhFXIoGmeRm1YZferx1u5PCphiDLVkDDlLEolbp3bxKnN+l4wC
OUvhabciX46H3sM6KGMSoDRjh5n0UPr2+67qBq/rNJRCkALEFrG46i/+mNrYiQEz
BBABCAAdFiEEYKz3C/cSZFBJ7m8V7+rxZoYiX2QFAmWp9dIACgkQ7+rxZoYiX2Se
cQf+IKiMpD8+D93HtmmwG0twBbPMOVta0NU90Gvjxkw/v/JIDEWlZECClUW6Se8Z
Icq+WRZeDP6UZharGAg2GfRpfrKIwVt/aP16LsCqq+SiP4xaohmpcXQxacS5u813
G9FFuxmHud3x7/sXtxKSVQRkhgQlq+RRG/s5CodNvjliM5OQiiXGr+q1tWy5QhRs
xCXj4CTc2CiV0ycWB36Cx9tkx+/s0pf7X4778wCrhzT6Ds5fT0W9uZifcglfI/p5
jYYQkGpOrnOiHkBU3F80iFowIGsiv8pfaSqBP8yBAOtNBSVo5ksqSaH+TpVeIb0/
pfGrM1BOzpTVfTmEj77qSE2tvrkCDQRdVC8lARAAu64IaLWAvMStxVsC/+NnwBBx
YPef4Iq5gB5P1NgkmkD+tyohWVnzdN/hwVDX3BAXevF8M+y6MouUA9IxJRt2W9PK
06ArTdwhFpiam2NAO5OOUhuJ1F8eAhRQ5VvI8MbVttZKSk3LiCmXGSj5UUXEFKS1
B7WztZVwqG6YswoAPwbNerZuwYbH2gfa9LK+av1cdZ8tnDaVmZWL8z1xSCyfRa/U
AtZht/CEoTvAwXJ6CxVUBngIlqVnK0KvOrNzol2m5x4NgPcdtdDlrTQE+SpqTKjy
roRe27D+atiO6pFG/TOTkx4TWXR07YTeZQJT/fntV409daIxEgShD0md7nJ7rVYy
8u+9Z4JLlt2mtnsUKHezo1Axrlri05cewPVYQLuJND/5e2X9UzSTpY3NubQAtkD1
PpM5JeCbslT9PcMnRuUydZbhn7ieW0b57uWpOpE11s2eIJ5ixSci4mSJE9kW+IcC
ic/PPoD1Rh2CvFTBPl/bsw6Bzw64LMflPjgWkR7NVQb1DETfXo5C2A/QU6Z/o7O4
JaAeAoGki/sCmeAi5W+F1kcjPk/L/TXM6ZccMytVQOECYBOYVUxZ2VbhknKOcSFQ
cpk8bj2xsD1xX2EYhkXcCQkvutIgHGz/dt6dtvcaaL85krWD/y8h68TTFjQXK0+g
8gcpexfqTMcLnF7pqEEAEQEAAYkCPAQYAQgAJhYhBClZA2Lsh4qB/TwgK1JSe+2r
6HmEBQJdVC8lAhsMBQkDw8drAAoJEFJSe+2r6HmEDzEP/A8H3JkeSa/03kWvudFl
oVbGbfvP+XkKvGnAZPGHz3ne/SV2tcXljNgU15xHvLktI4GluEfJxRPUqvUal1zO
R9hqpas0vX8gsf0r0d3om2DHCyMY8GscfDF05Y8fqf0nU5/oLDlwwp11IyW8BDLS
wwANsTLZ1ysukfYc4hoopU71/wdAl85fae7I2QRduImWlMADfUtc9Orfb1tAhPta
CJVZj5vgfUNSZOTUJ73RGbdL3Z2dc42lO3mRMyDkPdykkq0EgOo6zZLuHZQFhxTz
WIWeUT8vWNjpkdTeRHLvv3cwPRx1k1atrM+pE9YkhCg0EOMTcmN+FMekgnU+ee0c
ibn5wWOvE05zwRKYROx34va2U6TUU6KkV3fFuq3qqkXaiMFauhI1lSFGgccg7BCN
MhbBpOBkfGI3croFGSm2pTydJ87/+P9C9ecOZSqCE7Zt5IfDs/xV7DjxBK99Z5+R
GxtsIpNlxpsUvlMSsxUNhOWyiCKr6NIOfOzdLYDkhHcKMqWGmc1zC3HHHuZvX5u6
orTyYXWqc8X5p3Kh7Qjf/ChtN2P6SCOUQquEvpiY5J1TdmQSuoqHzg3ZrN+7EOKd
nUH7y1KB7iTvgQ07lcHnAMbkFDcpQA+tAMd99LVNSXh8urXhJ/AtxaJbNbCSvpkO
GB4WHLy/V+JdomFC9Pb3oPeiiQI8BBgBCAAmAhsMFiEEKVkDYuyHioH9PCArUlJ7
7avoeYQFAmEb0RAFCQ0to2sACgkQUlJ77avoeYRHuxAAigKlhF2q7RYOxcCIsA+z
Af4jJCCkpdOWwWhjqgjtbFrS/39/FoRSC9TClO2CU4j5FIAkPKdv7EFiAXaMIDur
tpN4Ps+l6wUX/tS+xaGDVseRoAdhVjp7ilG9WIvmV3UMqxge6hbam3H5JhiVlmS+
DAxG07dbHiFrdqeHrVZU/3649K8JOO9/xSs7Qzf6XJqepfzCjQ4ZRnGy4A/0hhYT
yzGeJOcTNigSjsPHl5PNipG0xbnAn7mxFm2i5XdVmTMCqsThkH6Ac3OBbLgRBvBh
VRWUR1Fbod7ypLTjOrXFW3Yvm7mtbZU8oqLKgcaACyXaIvwAoBY9dIXgrws6Z1dg
wvFH+1N7V2A+mVkbjPzS7Iko9lC1e5WBAJ7VkW20/5Ki08JXpLmd7UyglCcioQTM
d7YyE/Aho3zQbo/9A10REC4kOsl/Ou6IeEURa+mfb9MYPgoVGTcKZnaX0d40auRJ
ptosuoYLenXciRdUmfsADAb2pVdm5b2H3+NLXf+TnbyY/zm24ZFGPXBRSj7tQgaV
6kn9NPSg32Z1WcR+pAn3Jwqts3f1PNuYCrZvWv66NohJRrdCZc1wV4dkYvl2M1s+
zf8iTVti4IifNjn57slXtEsH36miQy2vN6Cp9I3A7m5WeL07i27P8bvhxOg9q6r3
NAgNcAK3mOfpQ/ej25jgI5w=
=LIEu
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=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}})
all:
mkdir backend
{{- if ne .Backend.DockerImage "" }}
docker container inspect extract > /dev/null 2>&1 && docker rm extract || true
docker create --name extract {{.Backend.DockerImage}}
{{- if eq .Backend.VerificationType "docker"}}
[ "$$(docker inspect --format='{{`{{index .RepoDigests 0}}`}}' {{.Backend.DockerImage}} | sed 's/.*@sha256://')" = "{{.Backend.VerificationSource}}" ]
{{- end}}
{{.Backend.ExtractCommand}}
docker rm extract
{{- else }}
wget {{.Backend.BinaryURL}}
{{- if eq .Backend.VerificationType "gpg"}}
wget {{.Backend.VerificationSource}} -O checksum
@ -13,8 +23,8 @@ all:
{{- else if eq .Backend.VerificationType "sha256"}}
[ "$$(sha256sum ${ARCHIVE} | cut -d ' ' -f 1)" = "{{.Backend.VerificationSource}}" ]
{{- end}}
mkdir backend
{{.Backend.ExtractCommand}} ${ARCHIVE}
{{- end}}
{{- if .Backend.ExcludeFiles}}
# generated from exclude_files
{{- range $index, $name := .Backend.ExcludeFiles}}

View File

@ -12,8 +12,12 @@ 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}}

View File

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

View File

@ -13,6 +13,8 @@ rpcworkqueue=1100
maxmempool=2000
dbcache=1000
deprecatedrpc=warnings
{{- if .Backend.AdditionalParams}}
# generated from additional_params
{{- 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" .}}
# Resource limits
LimitNOFILE=500000
LimitNOFILE=2000000
# 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}}
"coin_name": "{{.Coin.Name}}",
"coin_shortcut": "{{.Coin.Shortcut}}",
{{- if .Coin.Network}}
"network": "{{.Coin.Network}}",{{end}}
"coin_label": "{{.Coin.Label}}",
"rpc_url": "{{template "IPC.RPCURLTemplate" .}}",
"rpc_user": "{{.IPC.RPCUser}}",

View File

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

View File

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

View File

@ -45,6 +45,8 @@ func main() {
t.Add(server.WsBlockHashReq{})
t.Add(server.WsBlockHashRes{})
t.Add(server.WsBlockReq{})
t.Add(server.WsBlockFilterReq{})
t.Add(server.WsBlockFiltersBatchReq{})
t.Add(server.WsAccountUtxoReq{})
t.Add(server.WsBalanceHistoryReq{})
t.Add(server.WsTransactionReq{})
@ -58,6 +60,8 @@ func main() {
t.Add(server.WsFiatRatesForTimestampsReq{})
t.Add(server.WsFiatRatesTickersListReq{})
t.Add(server.WsMempoolFiltersReq{})
t.Add(server.WsRpcCallReq{})
t.Add(server.WsRpcCallRes{})
t.Add(bchain.MempoolTxidFilterEntries{})
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"`
CoinLabel string `json:"coinLabel"`
Host string `json:"host"`
Network string `json:"network,omitempty"`
DbState uint32 `json:"dbState"`
ExtendedIndex bool `json:"extendedIndex"`
@ -92,6 +93,15 @@ type InternalState struct {
// database migrations
UtxoChecked bool `json:"utxoChecked"`
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
@ -296,6 +306,15 @@ func (is *InternalState) computeAvgBlockPeriod() {
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
func (is *InternalState) SetBackendInfo(bi *BackendInfo) {
is.mux.Lock()
@ -336,3 +355,15 @@ func SetInShutdown() {
func IsInShutdown() bool {
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
// to handle data which can be numbers in double quotes or possibly not numbers at all
//
// to handle data which can be numbers in double quotes or possibly not numbers at all
//
// see https://github.com/golang/go/issues/37308
type JSONNumber string

View File

@ -72,7 +72,7 @@ func GetMetrics(coin string) (*Metrics, error) {
prometheus.HistogramOpts{
Name: "blockbook_socketio_req_duration",
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},
},
[]string{"method"},
@ -104,7 +104,7 @@ func GetMetrics(coin string) (*Metrics, error) {
prometheus.HistogramOpts{
Name: "blockbook_websocket_req_duration",
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},
},
[]string{"method"},
@ -113,7 +113,7 @@ func GetMetrics(coin string) (*Metrics, error) {
prometheus.HistogramOpts{
Name: "blockbook_index_resync_duration",
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},
},
)
@ -121,7 +121,7 @@ func GetMetrics(coin string) (*Metrics, error) {
prometheus.HistogramOpts{
Name: "blockbook_mempool_resync_duration",
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},
},
)

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_revision": "satoshilabs-1",
"system_user": "avalanche",
"version": "1.9.11",
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz",
"version": "1.12.1",
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz",
"verification_type": "gpg",
"verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz.sig",
"verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz.sig",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMEtmUT09IgogIH0KfQ==",
@ -36,8 +36,8 @@
"client_config_file": "",
"platforms": {
"arm64": {
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz",
"verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz.sig"
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz",
"verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz.sig"
}
}
},

View File

@ -19,10 +19,10 @@
"package_name": "backend-avalanche-archive",
"package_revision": "satoshilabs-1",
"system_user": "avalanche",
"version": "1.9.11",
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz",
"version": "1.12.1",
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz",
"verification_type": "gpg",
"verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-amd64-v1.9.11.tar.gz.sig",
"verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-amd64-v1.12.1.tar.gz.sig",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/avalanchego --data-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --log-dir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --http-port {{.Ports.BackendRPC}} --staking-port {{.Ports.BackendP2P}} --public-ip 127.0.0.1 --staking-ephemeral-cert-enabled --chain-config-content ewogICJDIjp7CiAgICAiY29uZmlnIjoiZXdvZ0lDSmxkR2d0WVhCcGN5STZXd29nSUNBZ0ltVjBhQ0lzQ2lBZ0lDQWlaWFJvTFdacGJIUmxjaUlzQ2lBZ0lDQWlibVYwSWl3S0lDQWdJQ0prWldKMVp5MTBjbUZqWlhJaUxBb2dJQ0FnSW5kbFlqTWlMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXVjBhQ0lzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RZbXh2WTJ0amFHRnBiaUlzQ2lBZ0lDQWlhVzUwWlhKdVlXd3RkSEpoYm5OaFkzUnBiMjRpTEFvZ0lDQWdJbWx1ZEdWeWJtRnNMWFI0TFhCdmIyd2lMQW9nSUNBZ0ltbHVkR1Z5Ym1Gc0xXUmxZblZuSWdvZ0lGMHNDaUFnSW5CeWRXNXBibWN0Wlc1aFlteGxaQ0k2Wm1Gc2MyVUtmUT09IgogIH0KfQ==",
@ -36,8 +36,8 @@
"client_config_file": "",
"platforms": {
"arm64": {
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz",
"verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.9.11/avalanchego-linux-arm64-v1.9.11.tar.gz.sig"
"binary_url": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz",
"verification_source": "https://github.com/ava-labs/avalanchego/releases/download/v1.12.1/avalanchego-linux-arm64-v1.12.1.tar.gz.sig"
}
}
},

View File

@ -22,10 +22,10 @@
"package_name": "backend-bcash",
"package_revision": "satoshilabs-1",
"system_user": "bcash",
"version": "26.1.0",
"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",
"version": "28.0.1",
"binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v28.0.1/bitcoin-cash-node-28.0.1-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "68cb24d57898d5b47a1162a9683d0b0e36c6701b5a16b93edc94bbd82113c04b",
"verification_source": "d69ee632147f886ca540cecdff5b1b85512612b4c005e86b09083a63c35b64fa",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": ["bin/bitcoin-qt"],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",

View File

@ -1,66 +1,64 @@
{
"coin": {
"name": "Bcash Testnet",
"shortcut": "TBCH",
"label": "Bitcoin Cash Testnet",
"alias": "bcash_testnet"
},
"ports": {
"backend_rpc": 18031,
"backend_message_queue": 48331,
"blockbook_internal": 19031,
"blockbook_public": 19131
},
"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-bcash-testnet",
"package_revision": "satoshilabs-1",
"system_user": "bcash",
"version": "26.1.0",
"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",
"verification_type": "sha256",
"verification_source": "68cb24d57898d5b47a1162a9683d0b0e36c6701b5a16b93edc94bbd82113c04b",
"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/testnet3/*.log",
"postinst_script_template": "",
"service_type": "forking",
"service_additional_params_template": "",
"protect_memory": true,
"mainnet": false,
"server_config_file": "bitcoin.conf",
"client_config_file": "bitcoin_client.conf"
},
"blockbook": {
"package_name": "blockbook-bcash-testnet",
"system_user": "blockbook-bcash",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
"subversion": "/Bitcoin ABC Cash Node:22.1.0/",
"address_format": "cashaddr",
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
"xpub_magic": 70617039,
"slip44": 1,
"additional_params": {}
"coin": {
"name": "Bcash Testnet",
"shortcut": "TBCH",
"label": "Bitcoin Cash Testnet",
"alias": "bcash_testnet"
},
"ports": {
"backend_rpc": 18031,
"backend_message_queue": 48331,
"blockbook_internal": 19031,
"blockbook_public": 19131
},
"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-bcash-testnet",
"package_revision": "satoshilabs-1",
"system_user": "bcash",
"version": "28.0.1",
"binary_url": "https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v28.0.1/bitcoin-cash-node-28.0.1-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "d69ee632147f886ca540cecdff5b1b85512612b4c005e86b09083a63c35b64fa",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": ["bin/bitcoin-qt"],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/testnet3/*.log",
"postinst_script_template": "",
"service_type": "forking",
"service_additional_params_template": "",
"protect_memory": true,
"mainnet": false,
"server_config_file": "bcash.conf",
"client_config_file": "bitcoin_client.conf"
},
"blockbook": {
"package_name": "blockbook-bcash-testnet",
"system_user": "blockbook-bcash",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
"subversion": "/Bitcoin ABC Cash Node:22.1.0/",
"address_format": "cashaddr",
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
"xpub_magic": 70617039,
"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_revision": "satoshilabs-1",
"system_user": "bitcoin",
"version": "25.0",
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-x86_64-linux-gnu.tar.gz",
"version": "28.0",
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "33930d432593e49d58a9bff4c30078823e9af5d98594d2935862788ce8a20aec",
"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",
@ -38,12 +38,13 @@
"server_config_file": "bitcoin.conf",
"client_config_file": "bitcoin_client.conf",
"additional_params": {
"deprecatedrpc": "estimatefee"
"deprecatedrpc": "estimatefee",
"addnode": ["ove.palatinus.cz"]
},
"platforms": {
"arm64": {
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-aarch64-linux-gnu.tar.gz",
"verification_source": "3a7bdd959a0b426624f63f394f25e5b7769a5a2f96f8126dcc2ea53f3fa5212b"
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz",
"verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e"
}
}
},
@ -63,13 +64,17 @@
"xpub_magic_segwit_p2sh": 77429938,
"xpub_magic_segwit_native": 78792518,
"additional_params": {
"alternative_estimate_fee": "whatthefee-disabled",
"alternative_estimate_fee_params": "{\"url\": \"https://whatthefee.io/data.json\", \"periodSeconds\": 60}",
"alternative_estimate_fee": "mempoolspace",
"alternative_estimate_fee_params": "{\"url\": \"https://mempool.space/api/v1/fees/recommended\", \"periodSeconds\": 20}",
"fiat_rates": "coingecko",
"fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH",
"fiat_rates_params": "{\"coin\": \"bitcoin\", \"periodSeconds\": 900}",
"golomb_filter_p": 20,
"mempool_filter_scripts": "taproot"
"fiat_rates_params": "{\"coin\": \"bitcoin\", \"periodSeconds\": 60}",
"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
}
}
},

View File

@ -22,10 +22,10 @@
"package_name": "backend-bitcoin-regtest",
"package_revision": "satoshilabs-1",
"system_user": "bitcoin",
"version": "25.0",
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-x86_64-linux-gnu.tar.gz",
"version": "28.0",
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "33930d432593e49d58a9bff4c30078823e9af5d98594d2935862788ce8a20aec",
"verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": ["bin/bitcoin-qt"],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
@ -42,8 +42,8 @@
},
"platforms": {
"arm64": {
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-aarch64-linux-gnu.tar.gz",
"verification_source": "3a7bdd959a0b426624f63f394f25e5b7769a5a2f96f8126dcc2ea53f3fa5212b"
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz",
"verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e"
}
}
},
@ -63,7 +63,14 @@
"xpub_magic_segwit_p2sh": 71979618,
"xpub_magic_segwit_native": 73342198,
"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": {

View File

@ -22,10 +22,10 @@
"package_name": "backend-bitcoin-signet",
"package_revision": "satoshilabs-1",
"system_user": "bitcoin",
"version": "25.0",
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-x86_64-linux-gnu.tar.gz",
"version": "28.0",
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "33930d432593e49d58a9bff4c30078823e9af5d98594d2935862788ce8a20aec",
"verification_source": "7fe294b02b25b51acb8e8e0a0eb5af6bbafa7cd0c5b0e5fcbb61263104a82fbc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": ["bin/bitcoin-qt"],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
@ -35,15 +35,15 @@
"service_additional_params_template": "",
"protect_memory": true,
"mainnet": false,
"server_config_file": "bitcoin-signet.conf",
"server_config_file": "bitcoin_signet.conf",
"client_config_file": "bitcoin_client.conf",
"additional_params": {
"deprecatedrpc": "estimatefee"
},
"platforms": {
"arm64": {
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-aarch64-linux-gnu.tar.gz",
"verification_source": "3a7bdd959a0b426624f63f394f25e5b7769a5a2f96f8126dcc2ea53f3fa5212b"
"binary_url": "https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-aarch64-linux-gnu.tar.gz",
"verification_source": "7fa582d99a25c354d23e371a5848bd9e6a79702870f9cbbf1292b86e647d0f4e"
}
}
},

View File

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

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": {
"name": "BNB Smart Chain",
"shortcut": "BNB",
"network": "BSC",
"label": "BNB Smart Chain",
"alias": "bsc"
},

View File

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

View File

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

View File

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

View File

@ -22,10 +22,10 @@
"package_name": "backend-dogecoin",
"package_revision": "satoshilabs-1",
"system_user": "dogecoin",
"version": "1.14.6",
"binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.6/dogecoin-1.14.6-x86_64-linux-gnu.tar.gz",
"version": "1.14.9",
"binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.9/dogecoin-1.14.9-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "fe9c9cdab946155866a5bd5a5127d2971a9eed3e0b65fb553fe393ad1daaebb0",
"verification_source": "4f227117b411a7c98622c970986e27bcfc3f547a72bef65e7d9e82989175d4f8",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": ["bin/dogecoin-qt"],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/dogecoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
@ -45,8 +45,8 @@
},
"platforms": {
"arm64": {
"binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.6/dogecoin-1.14.6-aarch64-linux-gnu.tar.gz",
"verification_source": "87419c29607b2612746fccebd694037e4be7600fc32198c4989f919be20952db",
"binary_url": "https://github.com/dogecoin/dogecoin/releases/download/v1.14.9/dogecoin-1.14.9-aarch64-linux-gnu.tar.gz",
"verification_source": "6928c895a20d0bcb6d5c7dcec753d35c884a471aaf8ad4242a89a96acb4f2985",
"exclude_files": []
}
}

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