Repository navigation
Sync Trezor upstream while preserving Syscoin support - #32
Merged
Merged
Conversation
…ndex EthereumRPC.GetTransaction relied solely on eth_getTransactionByHash, which returns null for mined transactions older than the backend's transaction-by-hash retention window on some archive nodes (observed on QuikNode Base, now the Base backend). Such transactions are still fully retained and reachable via eth_getBlockByHash and eth_getTransactionReceipt, but blockbook reported them as ErrTxNotFound, failing both production lookups and the base=main/rpc/GetTransaction integration test. Fall back to reconstructing the transaction from its receipt (for the block hash) plus the full block body when eth_getTransactionByHash returns null. Genuinely unknown transactions still return ErrTxNotFound, and the primary path is unchanged for backends that retain the full index. Fixes trezor#1609 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
recoverMinedTransaction reconstructed a pruned-index transaction by fetching the entire block body (eth_getBlockByHash, fullTxs=true) and scanning it for the hash. On endpoints that call GetTransaction per tx (address/xpub/block/balancehistory), a cold-cache page of transactions from the pruned window fetched and re-parsed the full block body once per tx - the same ~592 KB block up to 1000x on a block page. Fetch the transaction directly by its receipt's (blockHash, transactionIndex) via eth_getTransactionByBlockHashAndIndex - an O(1), ~900x smaller lookup - and reuse the already-fetched receipt for EthTxToTx instead of fetching it a second time. Verified against the QuikNode Base archive backend: the by-index result is byte-identical to the block-body scan across transactions 64-926 days old. Fall back to the block-body scan when the positional lookup is unavailable, so the change is strictly non-regressive for any backend, and give each recovery RPC its own timeout budget. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add <network>_ALLOWED_EVM_CALL_METHODS, a comma-separated list of 4-byte method selectors that permits websocket rpcCall requests to any address when the calldata starts with an allowed selector (e.g. 0xdd62ed3e for ERC-20 allowance). Complements ALLOWED_RPC_CALL_TO: a call passes when either its target address or its selector is allowed; with neither set rpcCall stays unrestricted. Malformed calldata never matches (full hex decode, fail closed) and malformed or empty selector config fails startup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Turn ALLOWED_RPC_CALL_TO and ALLOWED_EVM_CALL_METHODS into runtime settings: the environment variables remain the startup defaults, while an override written through the authenticated internal API GET/POST/DELETE /admin/runtime-settings/<KEY> is persisted in RocksDB (own cfDefault rows, independent of the periodically stored internalState blob and invisible to older versions), survives restarts, takes precedence over the environment and takes effect on websocket rpcCall immediately, without a restart. The live allowlists are an immutable snapshot behind an atomic pointer on InternalState, so the rpcCall hot path stays lock-free and both servers (public websocket, internal admin) share one view regardless of which of them a deployment runs. Writes are strictly ordered: validate (400 on invalid input) -> store to DB (500 and untouched live state on failure) -> publish snapshot -> log the change with old/new value, source and client address. POST of an exactly empty value explicitly unconfigures a dimension; whitespace- or separator-only values are rejected so a botched input cannot silently un-restrict rpcCall; DELETE reverts to the environment default and validates it first so a malformed environment cannot brick the next restart. The TO-list parser is unified with the selector parser: entries are trimmed, empty entries skipped and a set-but-empty value fails startup (previously "," allowlisted the empty string, which matched rpcCall requests with an empty to field). A minimal /admin/runtime-settings page lists the current values and their sources with curl hints. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Avoids allocating a decoded copy of the full calldata (up to 4 MiB) on every rpcCall allowlist check. The full hex encoding is still validated byte-by-byte to preserve the existing fail-closed behavior for malformed calldata.
Addresses the API unification review on PR trezor#1597: - POST /admin/contract-info/ returns a typed {"updated":N} object instead of a pre-serialized string that jsonHandler double-encoded on the wire; a malformed body is now a 400 (public APIError) rather than a 500 - apiContractInfo dispatches on method like apiRuntimeSetting: POST/PUT write the collection path only (an address segment is rejected instead of silently ignored), unknown methods are a 400 instead of falling into the GET path - new DELETE /admin/contract-info/<address> purges cached contract metadata (DB row + LRU entry) so it is re-fetched from the backend on the next read; idempotent, missing row reports deleted:false - shared urlPathSegment helper unifies path-segment extraction; contract addresses get no case normalization (the chain parser is the authority), runtime-setting keys keep their uppercasing at the call site - runtime-settings routes and initRpcCallAllowlists move out of the ChainEthereumType guard (the mechanism is chain-generic and the init already runs unconditionally in NewWebsocketServer); contract-info and internal-data-errors stay EVM-gated, nav links follow the same split Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Smaller items from the PR trezor#1597 review: - lowercase the "invalid EVM call method selector" parse error to match the sibling parser's message convention - trim environment values wherever they are resolved (startup and the DELETE fallback), so stray whitespace from an env file never surfaces in values; a set but whitespace-only env value is a configuration error rather than unset — treating it as unset would silently un-restrict rpcCall - document the differing To/Methods map key formats on RpcCallAllowlists so a future allowlist dimension picks a convention deliberately - runtimeSettingStore gains GetRuntimeSetting; resolveRuntimeSetting reads through the interface instead of requiring *db.RocksDB Admin runtime-settings page gets an inline editing UI (edit/save/delete per setting, replacing the curl-only instructions) and the page/nav/ routes are consistently available on all chain types. Hardening found by review of the new DELETE /admin/contract-info/: - bump protocolGen before the cache purge in DeleteContractInfoForAddress so a concurrent GetContractInfo cannot re-insert the deleted row into the LRU (same idiom as SetErcProtocol); without it the purge could be silently undone until eviction or restart - DELETE returns (and logs) the purged record: the backend re-fetch restores only name/symbol/decimals, not the sync-owned createdInBlock/destructedInBlock, so the response gives the operator a POST-back restore path and the docs now state exactly what is discarded - a failed save in the admin UI keeps the row in edit state instead of stranding an orphaned input, and error responses are unwrapped to their message Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Supports the bbctl counterpart (trezor/bbctl#10) of the runtime-settings admin API in a two-replica deployment where each replica owns its DB: - GET /admin/runtime-settings/ (bare collection path) returns every setting with its effective value and source as a JSON array, so a management tool reads the whole state per replica in one request; other methods on the bare path stay a 400 - initRpcCallAllowlists warns at startup when a stored override shadows a different (or malformed) environment value, making env/DB drift — a replica that missed an admin update, or an env change rolled out while an override exists — visible; the shadowed value is only compared, never parsed, so drift cannot fail a restart - docs/env.md explains the deployment roles of the two sources: the env var is the deploy-managed baseline that survives a replica resync (the DB is wiped, and unset allowlists would silently un-restrict rpcCall), the stored override is the runtime layer that survives restarts until the next deploy ships an updated environment Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GET /admin/contract-info/ (bare collection path) lists the stored
records, mirroring the runtime-settings list convention. Unlike runtime
settings the collection is unbounded — sync stores a record per contract
creation, millions on a busy chain — so the list is paginated:
?limit=<1..10000> (default 1000) with a from cursor, and the response
{"contracts":[...],"next":"<address>"} carries the from of the next
page. ListContractInfos iterates cfContracts in address-descriptor
order (the CF holds only plain addrDesc keys; protocol rows live in
their own CF).
The former 400 for GET of the bare path moves to the list; DELETE and
POST semantics on the bare path are unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Post-deploy step in the e2e-tests job exercising the admin API (/admin/runtime-settings/, /admin/contract-info/) of the deployed base_archive dev instance, leaving its database and live rpcCall policy exactly as found — at the end of a run and at every instant during it: - runtime settings use a zero-policy-impact roundtrip: the POSTed value is always the current effective value (or the explicit-empty override when unset), so the parsed allowlist never changes; the final DELETE restores the original source; db-sourced settings are only rewritten in place, since their env fallback is unknown from outside - the contract roundtrip uses a reserved never-real address, pre-cleans it (healing a crashed run) and deletes it at the end; the cleanup and restore flags are raised before the POSTs, so a response lost after a server-side commit still gets cleaned up - negative tests: 401 without credentials (503 = admin disabled fails loudly), unknown key, invalid values proven to change nothing, POST to an address path, unsupported method, list pagination limits - target URL derives from BB_DEV_API_URL_HTTP_<coin> plus the coin config's ports.blockbook_internal (BB_ADMIN_E2E_URL overrides for local runs); the coin gate compares against coins_csv deploy aliases (test_coins_csv carries test names and would never match); skips are annotated (::warning:: for a missing BB_RUNTIME_ENV, ::notice:: for coin-not-deployed) so a dead signal cannot look like a pass - credentials are parsed from BB_ENV inside the script (surrounding quotes stripped like systemd EnvironmentFile) and never printed; transient network errors are retried once, then fail with a clean pipeline error; helpers reuse runner.py and wait_for_sync.py Verified live against blockbook-dev3: two consecutive runs green via the derived URL, instance state identical before and after. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The BB_DEV_API_URL_HTTP_<coin> repo variables follow the tests.json test-name convention (BB_DEV_API_URL_HTTP_base for base_archive), so resolving them by the deploy alias failed with "missing BB_DEV_API_URL_HTTP_base_archive". Map the deploy alias through runner.load_test_coin_name (the same helper deploy_plan.py uses) before the lookup; the coin gate keeps comparing deploy aliases. Unit test pins the mapping against the real base_archive.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The paginated contract listing discarded the error from GetAddressesFromAddrDesc and fell through to return next="" for a boundary row whose key did not decode to an address. That silent truncation is indistinguishable from a completed listing and drops the remaining rows; in-page rows with no decoded address were likewise returned with an empty Contract. A corrupt key now returns an error, matching the unpackContractInfo error handling in the same loop. Unreachable for well-formed EVM/Tron contract descriptors, but a visible failure beats a confusing partial page. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(tron-testnet): upgrade backend to 4.8.2 * fix(tron-testnet): path to the .jar backend file * fix(tron-testnet): update backend version
…esilliency (trezor#1582) * feat(fiat-api): add cap limits for fiat rate endpoints * feat(evm-contract): verify contract logs to avoid possible OOM * feat(websocket): add lower concurrency cap on getMempoolFilters * fix(fiat-api): apply currency count cap to raw selector list length The MaxFiatRatesCurrencies check fired only when a unique normalized code was appended, so an all-duplicate list bypassed the cap while still costing a full trim/lowercase/dedup pass per element. Reject over-long lists up front so the normalization work itself is bounded by the cap; this also makes the in-loop unique-count check unreachable, so drop it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(fiat-api): normalize currency selectors once at entry points setFiatRateToBalanceHistories re-normalized a slice that both of its callers (GetBalanceHistory, GetXpubBalanceHistory) had already passed through normalizeFiatCurrencies, and getFiatRatesResult re-lowercased selectors already lowercased by its callers. Drop the redundant passes and document the pre-normalized precondition instead. Normalization was the only error path of setFiatRateToBalanceHistories, so it no longer returns an error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(evm-contract): drop unreachable ERC-1155 count overflow guard The endValues <= len(data) check already bounds countValues to len(data)/evmWordHex, far below maxInt, so the truncation guard on the int conversion could never fire (the load-bearing overflow check lives in erc1155BatchArrayEnd). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(websocket): clarify scope of getMempoolFilters response cap The slot is deliberately held from before the handler computes the response until it is written to the websocket (or drained on close): the cap exists to bound peak memory in computed-but-unwritten filter responses, so it covers compute, queueing, and write together rather than compute concurrency alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(eth-parsing): skip unparseable log with a warning and continue with next logs * test(websocket): cover mempool filter slot release after successful write Adds TestWebsocketOutputLoopReleasesMempoolFilterSlotAfterWrite, which drives a release-bearing WsRes through outputLoop over a live websocket connection and asserts the mempool-filters slot is freed after WriteJSON. This pins the primary release path (outputLoop's c.finalize(m)) that the existing semaphore and drain-on-close tests do not exercise; without it, removing that call would leak a slot on every successful response and go undetected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
getEnsRecord read an attacker-controlled 32-byte word from an ENS NameRegistered log and computed a slice end as 194+64+(int(c)<<1). For c in [0x7FFFFFFFFFFFFF7F, 0x7FFFFFFFFFFFFFFF] the signed left shift wraps and de lands in [0, 256], slipping the `de < 0` guard, so l.Data[194+64:de] became a low>high slice expression and panicked. The panic runs in the GetBlock log-processing goroutine, which has no recover(), so it crashes the whole process before the block is committed -> deterministic crash-loop on restart. - getEnsRecord: guard the slice start (de < 194+64) instead of de < 0. - processParam: same fix (de < d) for the sibling data[d:de] slice (already under ParseInputData's recover(); correctness only). - GetBlock: recover() the log goroutine as defense-in-depth, returning an error instead of letting the process die (adds runtime/debug). - tests: regression cases for both overflow paths (de=0, de=256, and the processParam variant), verified to panic without the fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tart in getEnsRecord Address two review comments: - Add d+64 < d overflow guard at line 159 in processParam, same class as the existing de < d fix. - Replace magic 194+64 with a local const nameStart in getEnsRecord.
TestGetTickersForTimestamps_ConcurrentReadersAndWriters and TestLogTickersInfo_ConcurrentWithCacheWriters wait `waitTimeout` for their reader/writer/logger goroutines to drain after `stop` is closed. The deadline was 3s. That is only a deadlock safety-net, but it is too tight on a CPU-starved CI runner (the Unit Tests job compiles RocksDB in parallel), so `wg.Wait()` occasionally misses it and the test fails with "concurrent ... did not finish in time". Raise it to 30s: still fails fast on a genuine deadlock, but tolerant of scheduling delays under load. Test-only change; the concurrency exercise (driven by testDuration) is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GetTransactionFromBchainTx resolved each input's previous output by calling GetTxAddresses, which unpacks an entire TxAddresses record (all inputs and outputs) just to read a single output. For transactions with many inputs that reference large previous transactions this allocates on the order of inputs x record size, driving heap usage up sharply on high-fan-in addresses. Add db.GetTxAddressesOutput, which returns a single Outputs[vout] by skipping over the height, inputs and preceding outputs by length without allocating them, and use it in the vin-resolution loop. Allocation now scales with the number of inputs rather than inputs x record size. Adds TestGetTxAddressesOutput (both index modes) and BenchmarkTxInputResolution covering the input fan-in scaling.
…on self-hosted runners The actions/setup-node built-in npm cache saves ~/.npm with paths relative to the workspace. On runners where the home directory lives outside the workspace tree, tar cannot restore these paths. The explicit actions/cache for tests/openapi/node_modules already handles caching and skips npm ci on hit, making the global npm cache redundant.
actions/checkout v4.3.1 -> v7.0.0 actions/cache v4.3.0 -> v6.1.0 actions/setup-node v4.4.0 -> v6.4.0 actions/upload-artifact v4.6.2 -> v7.0.1 All four actions were targeting Node.js 20, which is now deprecated on GitHub Actions runners running Node.js 24. The new major versions bundle a compatible runtime.
Instead of setup-node's built-in cache:npm (which saves ~/.npm with paths relative to the workspace, causing tar failures when home is outside the workspace tree), cache a local .npm-cache directory inside the workspace via explicit actions/cache, and pass --cache .npm-cache to npm ci. On node_modules cache miss, the local cache avoids re-downloading packages from the registry. The restore-keys fallback preserves partial cache benefit even across lockfile changes.
On node_modules cache hit, npm ci is skipped and .npm-cache was never created, causing the post-job cache save to warn: Path Validation Error: Path(s) specified for caching do not exist Pre-create the directory with mkdir -p so the path always exists for the save phase. On cache hit the directory is empty and the save is a no-op.
…ng chains The getSampleIndexedHeight probe searches from bestHeight-2 backward for an indexed block hash. At 12 blocks (txSearchWindow), chains with slower indexers (ethereum-classic on core-geth/blockbook-dev2) sometimes have zero indexed blocks in the window, causing flaky failures. 20 blocks covers ~4.5 minutes of ETC chain history, ample for any realistic indexing lag. The loop stops on the first hit, so healthy runs are unaffected.
* fix(ws): cap subscribeFiatRates tokens array to prevent DoS The subscribeFiatRates handler stored the request's tokens slice whole for the connection lifetime and re-iterated it on every fiat-rate broadcast (per subscribed currency) while holding the global fiatRatesSubscriptionsLock. It was the only WebSocket list parameter without a length cap: a single message up to the 4 MiB read limit could carry ~10^6 token strings, enabling amplified heap retention and periodic global-lock contention that stalls fiat-rate subscription handling. Reject requests whose tokens array exceeds maxWebsocketSubscribeFiatRatesTokens (1000) with a public API error, matching the existing caps on addresses, currencies, timestamps, and estimateFee blocks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(websocket): cleanup comment --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(eth): prevent duplicate ERC-721 holdings * chore(contrib-script): move the erc-721 holdings check to a gist * refactor(eth): address PR review comments on NFT self-transfer handling - derive the NFT self-transfer invariant from a shared isNFTSelfTransfer helper on both the connect and the disconnect side so they cannot drift - document that addToContract callers pass mutateHoldings=false only for ERC721 self-transfers - add a ConnectBlock -> DisconnectBlock round-trip test verifying that an ERC721 self-transfer keeps holdings untouched and tx counts balanced Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…urable The cap on concurrently executing requests per connection is a hard-coded const (48). Internal batch clients that pipeline many requests on a single connection (e.g. bulk getAccountInfo address scans by an exchange backend) trip it and get disconnected mid-batch, with no way to tune the limit -- unlike the adjacent websocket message-rate and getAccountInfo limits, which are already env-configurable. Read the limit from <NETWORK>_WS_PENDING_REQUESTS_LIMIT following the existing WS limiter env pattern; the default stays 48 and 0 disables the cap (the server-wide work limit remains as a backstop). Document the variable in docs/env.md and cover the override with a test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…enders EthereumTypeGetNonces routed eth_getTransactionCount to the alternative send-tx provider on a bare nil-check, i.e. for every address detail request. On instances with *_ALTERNATIVE_SENDTX_URLS configured this exhausted the provider's rate quota, flooding the error log with 429s and - worse - forcing the fallback to the primary RPC exactly for the private-tx senders the feature exists for, handing them a stale pending nonce. Only senders with a privately submitted transaction in flight need the provider's nonce view. Record the sender of every transaction accepted by an alternative provider (decoded from the raw tx hex, so it works regardless of the mempool-fetch mode and survives a failed post-send fetch-back) in a TTL map, and route nonce lookups through the provider only for those senders. Everybody else is served by the primary RPC. The fallback log is demoted to a warning: with the gate in place it only fires when a provider call for an actual private sender fails. Fixes trezor#1627 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A broadcast to alternative providers succeeds if ANY configured URL accepts the transaction, but nonce lookups always queried urls[0]. When the first provider rejects a broadcast that a later one accepts, the sender's nonce reads would hit a provider that never saw the transaction and report a stale pending nonce. Record the accepting URL alongside the sender and route that sender's eth_getTransactionCount lookups to it, falling back to the first configured URL when unknown. Single-URL deployments are unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The nonce-routing entry recorded at send time survived for the full alternativeMempoolTxTimeout even after the sender's transactions were mined, superseded or replaced. Address polling therefore kept consuming the alternative provider's rate-limit quota for minutes after nothing private remained pending. Drop the sender's entry when their last cached transaction leaves the alternative mempool cache. RemoveTransaction is the choke point every eviction funnels through (reconcile, RBF replacement, mined-tx removal via the mempool callback); the read-path TTL eviction in GetTransaction now goes through it too. The entry is kept when it is newer than the evicted transaction, so a follow-up send whose post-send fetch-back failed does not lose its routing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When the alternative-provider nonce lookup fails for a gated sender, the fallback answer comes from the primary RPC, which cannot see the sender's privately submitted pending transactions. Returning it as-is would let a wallet build its next transaction on a stale nonce and accidentally replace the in-flight private transaction. Raise the fallback answer to the floor implied by the alternative mempool cache (highest cached account nonce of the sender + 1), so the reported pending nonce never contradicts the pending view Blockbook itself exposes for those transactions. Without cached transactions there is nothing to raise by and the primary answer stands, which is correct for the common case of a sender whose transaction was just mined. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Actions variables are rendered verbatim in job log step headers, so the BB_DEV_RPC_URL_* / BB_PROD_RPC_URL_* values move into one BB_RPC_URLS secret holding KEY=VALUE lines. The exporter masks each URL before writing it to GITHUB_ENV and fails if those names are still present among repository variables. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add fixture-driven xpub tests to the OpenAPI e2e suite: account totals, tokens filters, txid paging, UTXOs, balance history, an invalid-xpub negative case, and HTTP/WebSocket parity for getAccountInfo and getAccountUtxo. Fixtures are the public Trezor test seed account xpubs for bitcoin, testnet, litecoin, dogecoin, bcash and zcash, with observed tx and used-address counts as floors so a shrinking index fails the run. The negative case exposed that a malformed xpub was answered with HTTP 500; GetXpubAddress now returns a public 400 "Invalid xpub" error. Harness: optional OPENAPI_USER_AGENT for HTTP and WebSocket, 5xx retries with backoff, and a missing fixture file skips instead of failing. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ild1 (Nile) (trezor#1831) * chore(tron): update java-tron 4.8.2.1->4.8.2.3 Both 4.8.2.2 and 4.8.2.3 are mandatory upgrades that change transaction execution rules (block versions 37 and 38). A node on 4.8.2.1 computes different results for the affected transactions and stops following the chain once one is included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(tron-nile): update java-tron 4.8.2-pq1-build1->4.8.2.2-pq1-build1 Nile 4.8.2.2-PQ1-build1 is a mandatory upgrade; there is no Nile build of 4.8.2.3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…lamsterdam (trezor#1830) Sepolia activates Glamsterdam (Amsterdam EL + Gloas CL) at epoch 353024, 2026-10-06 13:53:36 UTC. Prysm 7.2.0 is the first release scheduling Gloas on Sepolia and Erigon 3.7.0 the first with amsterdamTime in its Sepolia chainspec (3.5.x and 3.6.x, incl. release branches, lack it). Without both the backend halts at the fork. Erigon 3.6+ writes snapshot formats 3.5 cannot read, so this upgrade is one-way. No removed startup flags are used by these configs, and the 3.6/3.7 JSON-RPC breaking changes do not touch calls Blockbook makes. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
GetMempoolEntry has never had a caller - outside the per-coin implementations the only reference was the metrics wrapper, and MempoolEntry.FeeSat / ModifiedFeeSat were read nowhere - so no endpoint, mempool tracking or fee estimation path used it. It had also drifted out of sync with the backend: Bitcoin Core 24.0 removed the top-level fee, modifiedfee, ancestorfees and descendantfees fields together with -deprecatedrpc=fees, so a call against a modern node decoded them as zero rather than failing. Dropping the dead path is preferable to teaching it the nested fees object nobody would read. Follow-up to trezor#1634 (items 2 and 3). Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
… batches (trezor#1828) * fix(fiat): keep current tickers updating when CoinGecko rejects token batches Since 2026-09-29 the api.coingecko.com CloudFront WAF answers 403 "Request blocked" to simple/price URLs longer than ~3.1k chars. Token prices were requested in ~6000 char batches, so every token-platform coin failed each cycle and, because one failed batch aborted CurrentTickers, native rates froze too (eth/bsc/base tickers stuck at 2026-09-29 06:1x UTC). - cap the ids parameter at 2000 URL-encoded chars - skip a failed token batch (stop on a Cloudflare ban) and report it as errCoingeckoIncompleteTokenRates alongside the fetched rates; a failed coins/list is reported the same way - on an incomplete ticker keep the previous rates of the missing tokens, publish it and record status="partial" in the update-duration metric Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(fiat): size token batches from CloudFront's 2048-char query limit api.coingecko.com is served by both Cloudflare and CloudFront; only the CloudFront edge blocks long requests, and its limit is exactly 2048 chars of query string (2048 passes, 2049 gets 403, reproducibly). The earlier ~3.1k estimate came from bisecting with responses mixed between the two CDNs. Derive the ids budget from that limit minus a 100-char margin and the vs_currencies overhead, and assert it in the tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(fiat): drop failed token rates instead of carrying them over Carrying a failed batch's previous token rates under the new ticker timestamp mislabelled their age, and on chains priced in the native coin (bnb, eth on L2s) mixed a stale token/native ratio with a fresh native price. Suite treats a missing token as "No tickers found!" and falls back to CoinGecko itself, keeping its own last good rate when that fails too. - publish an incomplete ticker with only the token rates fetched this cycle - retry failed batches once after the others (not after a Cloudflare ban) - stamp the ticker when the native price is fetched, so ts bounds the age of every rate in it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(grafana): plot CoinGecko request counters as per-interval increases The CoinGecko requests and historical range panels plotted the raw cumulative counters, so restarts showed as drops, the daily history download as a permanent step, and the all-status total line flattened the throttle and error series. Plot increase over $__interval as bars with sum/max legend calcs instead, with a 5m minimum interval so the window always spans at least two scrapes. market_chart downloads run in bursts of hundreds of calls and dwarfed the other endpoints, so they move to a separate "CoinGecko historical requests" panel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(metrics): extend fiat update duration buckets to 1 hour Current-ticker runs on token-heavy EVM chains take 7-46 minutes in prod, so every observation fell into +Inf and the p95 panel was capped at 300s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…rezor#1837) A client can send an ad:<hex> descriptor of any length (up to ~2 MiB over WebSocket). GetAddress passed it to GetAddressChainExtraData, which base58-encoded it in quadratic time (64 KiB took 6.6 s, ~1-2 h per max-size message) and forwarded the result to three node endpoints. Only a 20-byte account has chain data, so return early for any other length. Also stop logging the rejected input verbatim at INFO, which wrote up to 4 MiB of log per request. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…or#1838) A side or contract filter that rarely matches walked the whole address index. Stop after 200k non-matching entries and return a public error asking to narrow from/to; matching entries stay bounded by paging. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…tails=basic (trezor#1801) * perf(eth): read only the header of the address-contracts record at details=basic Every Ethereum-type account request decoded the whole token list of the address even at details=basic, which only consumes the three tx counters in the fixed header. For 0xdEaD (~210k contracts) that was ~80 ms of CPU and hundreds of thousands of allocations per request. Add GetAddrDescContractsHeader, a counters-only reader of the cfAddressContracts record, and select it from getEthereumTypeAddressBalances when the contract array cannot be reached: details=basic, no per-contract vout filter and no contract filter. Every other request keeps the full decode. No format change, no migration, no API change. Fixes trezor#1790 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * perf(eth): fold the header-only read into GetAddrDescContracts and pin the record Review follow-up for trezor#1801: - GetAddrDescContracts takes a headerOnly argument instead of a second exported reader, and reads the record with GetPinnedCF so the value is not copied out of the block cache (255 µs → 9 µs per cached 5.6 MB record, 1.44 → 0.82 ms on a cache miss) - details=basic always takes the header path; totalResults is consumed only above basic, so a per-contract filter cannot observe the undecoded contract array - drop the 200k-contract benchmark fixtures, which were built at test binary init on every run Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…er explorer page (trezor#1826) * perf(db): decode only the requested token rate in fiat ticker lookups A daily ticker stores thousands of token rates. Token lookups decoded all of them into a map per visited row just to read one, which on eth-b9 was 80% of all allocated bytes and ~24% of CPU (explorer tokenAmountSpan). FiatRatesFindTicker(s) with a token now decode the fiat rates plus the one token rate (exact key first, then lowercase, as findTokenRate does). FindTicker with 5,000-token rows: 144us -> 16us, 452KB -> 4KB per lookup. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * perf(server): memoize historical token rates per explorer page tokenAmountSpan looked up the historical rate of every token-transfer row. Stored token rates are daily, so rows of the same contract on the same UTC day share one lookup per page. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor(db,server): simplify the token rate lookup and its per-page memo Fold the token-only ticker decoder into unpackCurrencyRatesTicker behind a token filter and share the rates-section loop, so the wire format is decoded in one place and getTickerFromIterator loses its branch. Read keys through a new unpackStringBytes next to unpackString instead of inlining it. Drop the lowerFound/lowerRate state: a lowercase hit is written straight into the result and an exact hit overwrites it. The kept rate is stored under the caller's token so GetTokenRate hits without a second ToLower. Key the explorer memo on fiat.DailyTickerTimestamp, which wraps the existing ceilUnix, instead of a second copy of the day arithmetic in server. Remove the GetTokenRate pre-check that GetContractBaseRate already performs and the one-use func type. Document that token lookups return a single-rate ticker. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(db,server): simplify the token rate memo key and decoder match Key the explorer memo on TxDate, which amountSpan already sets alongside TxTicker for the same day bucket, instead of a fiat helper exported only for this purpose. Return on the first exact-or-lowercase key hit when decoding a single token rate: explorer rows pass EIP-55 addresses while stored keys are lowercase, so waiting for a later exact hit scanned every row to the end. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
trezor#1809) * chore(btc): bump Bitcoin Core 29.2 → 31.1 for mainnet and all testnets 29.x goes EOL when Core 32.0 ships (~10 Oct 2026); 31.1 is current stable and its release notes contain no breaking change on Blockbook's RPC surface. Also drop bitcoind options that are dead on 31.1: mempoolfullrbf (removed in Core 29.0), deprecatedrpc=warnings (coreWarnings decodes both shapes) and deprecatedrpc=estimatefee (RPC removed in Core 0.17). Closes trezor#1805 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore: link the Bitcoin Core 31.1 changelog entry to its PR Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ezor#1847) Private addnode peers can't live in the public coin configs. The build now appends BB_ADDNODE_<alias> (comma/whitespace separated host[:port]) to backend.additional_params.addnode, validating each entry so the value can't inject other bitcoind options. CI reads it from the new BB_BACKEND_PEERS secret in the build job only, masks every peer, and fails if the name is set as an Actions variable. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…) (trezor#1800) Three sampler limits made the post-deploy e2e job fail on every attempt: - REST /api/v2/block/{id} ignored pageSize, so the "3-tx" sample fetch pulled every tx of a BSC block (~38 ms each, cold) and tripped the 25 s client timeout. The endpoint now honours pageSize (default and max 1000), matching the WebSocket getBlock; documented in openapi.yaml. - ethereum-classic has ~73% empty blocks; a 3-block probe found no tx-bearing block 42% of the time. The probe now spans txSearchWindow blocks and the failure names the real cause instead of "missing indexed block hash". - The sample address was the first sender of a random tx, on BSC often a bot with 10^8 txs whose even time-bounded balance history is unservable. The sampler now prefers a participant with at most 10k txs, probing neighbouring txs within a small budget before falling back. Verified live against all 21 CI coins on the dev hosts: 566 ok, 0 fail. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…bytes and hit/miss metrics (trezor#1813) * perf(api): raise the default xpub cache cap from 1024 to 5000 entries On the production Bitcoin instance the xpub cache sits pinned at its 1024-entry cap with ~1850 connected wallets, so hot accounts are evicted and re-derived minutes later. A miss re-derives every address of the account (~50-100 us each) and re-reads every balance, which is why getAccountInfo averages 60-120 ms instead of well under a millisecond. A cache entry costs ~50 KB for a realistic account mix (4 KB empty, ~80 KB for a 100-address account), so a full 5000-entry cache is ~250 MB. The cache only grows with distinct xpubs actually requested, so idle instances pay nothing. Per-coin xpubConfig overrides still apply. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(api): expose xpub cache bytes and hit/miss metrics The only xpub cache metric was the entry count, so the cap could not be sized against memory and thrash could only be inferred from the gauge sitting at the ceiling. Each entry now carries a heap estimate (per address, balance, utxo and txid; within ~7% of runtime.MemStats on synthetic accounts) that is debited on insert and credited on eviction, published as blockbook_xpub_cache_bytes. Lookups are counted as hits, absent misses (full derivation) or stale misses (every address rescanned after a new block). Two Grafana panels show bytes and activity. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(grafana): move xpub cache panels to ids 365-366 to avoid clashing with trezor#1810 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor(api): simplify xpub cache accounting and trim at the raised cap - single putCachedXpubLocked insert path mirrors deleteCachedXpubLocked, so the byte total has exactly two entry points and the test exercises the production insert instead of its own copy - one missReason string decided where absent/stale are known replaces the two booleans and the trailing switch - gauges are set under the lock; both call sites drop their copy-out locals - trim evicts via a linear least-recently-accessed scan instead of allocating and sorting the whole map under cachedXpubsMux; at 5000 entries that was ~2 ms and 123 KB of garbage per miss, blocking hits - one resetXpubCacheLocked test helper resets map and byte total in all four tests that touch the package-global cache - xpub cache activity panel gets the 5m min interval for rate() queries Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(api): cap the xpub cache by bytes and split gap/history cache misses - xpubConfig.maxCacheBytes (default 1 GiB) evicts least recently used entries once the estimated cache size exceeds it; the entry cap alone does not bound memory since an entry holds the xpub's whole txid history. Trims needing more than one eviction sort once instead of rescanning the map per eviction (full 5000-entry eviction 124 ms -> 0.8 ms under the lock). - A cached entry re-derived for a different gap is counted as reason=gap instead of absent, so two clients alternating gaps on one xpub are not mistaken for an undersized cache. - Loading txids from the db for an entry cached at the current tip without history is counted as reason=history instead of a hit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
… (trezor#1797) * fix(grafana): name every legend by replica, never by coin (trezor#1717) The dashboard's single-select $coin dropdown already fixes the coin for every panel, yet 93 legends repeated it ("Polygon Archive - EstimateSmartFee") while 80 others named no replica at all, so neighbouring panels read differently and a misbehaving replica could not be told apart. Every legend now starts with {{instance}} for a per-replica series, or with "all replicas - " where the query sums replicas away on purpose (fleet load, external providers). Fault-oriented panels that used to aggregate by coin (RPC rate/latency, sync errors, reorgs, send-path routing, per-replica caches, fiat fetches) now group by instance so the offending replica is visible; the sync RPC latency p50/p95 series also get distinct suffixes instead of sharing one name. render_grafana.py --check enforces the convention (no {{coin}} under a $coin filter, a replica scope on every legend, and no legend claiming a scope its query lacks), and the README documents it. Closes trezor#1717 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(grafana): add HyperEVM Archive and Robinhood Archive to the coin dropdown Both chains have been deployed for a while but the custom `$coin` variable still ended at Avalanche, so their panels were only reachable by typing the name. Values match the `coin` metric label, i.e. the `coin.name` of each coin config. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…lloc (trezor#1846) * build: link the Blockbook binary against jemalloc instead of glibc malloc RocksDB pushes multi-megabyte record values, memtable blocks and write batches through the C heap on every block. glibc malloc returns memory to the kernel only from the top of an arena heap, so those buffers stay resident after they are freed once small long-lived allocations pin the heaps, and with eight arenas per core the retained memory grows for hours on busy EVM chains (backend3 Optimism reached 136 GiB and was OOM-killed twice in October 2026). jemalloc purges freed extents wherever they sit and returns allocations above 8 MiB immediately, which is also the allocator RocksDB upstream builds and tests with. The build image installs libjemalloc-dev and the Go link pulls in libjemalloc_pic.a by path, so the package stays self-contained and still installs with a bare dpkg -i. RocksDB's own build is told not to detect jemalloc, because that would link ldb and sst_dump against libjemalloc.so. JEMALLOC=0 restores glibc malloc for A/B comparison. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * build(deb): set the jemalloc arena and purge defaults in the packaged unit The trial on Optimism showed the native floor drifting from 3 GB to 10 GB with jemalloc's defaults (four arenas per CPU, no background purging) and staying at 3 GB with narenas:8 and background_thread:true. An override applied as a unit drop-in was lost on the next deploy, so the packaged unit now carries it; blockbook.env can still replace it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* fix(eth): use the node-reported block hash for headers go-ethereum recomputes the header hash from the fields it knows, so headers with fields it does not know yet (Glamsterdam's blockAccessListHash and slotNumber) get a wrong hash. Sepolia sync has been stuck at the fork block since then. Keep the hash from the node's response for HeaderByNumber and newHeads, falling back to the computed hash when it is missing. Tron now uses the same header type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(eth): require the node-reported hash in headers A computed fallback silently returns a wrong hash for headers with fields go-ethereum does not know; fail the decode instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(tron): fold TronRPCClient.HeaderByNumber into TronClient Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Dependabot runs receive only Dependabot secrets, so BB_RPC_URLS is empty there and the connectivity/integration jobs would hit default localhost backends. Skip those jobs when the actor is Dependabot and add a workflow_dispatch trigger so a maintainer can run them on the branch. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ls (trezor#1810) * feat(metrics): export Go GC CPU share and live heap, add Grafana panels Production runs GOGC=100 with no GOMEMLIMIT anywhere, and the default Go collector only exports the memstats gauges, so the one number that says whether GC tuning would help - the share of process CPU the collector burns - was not observable without a pprof session, which packaged builds do not expose. GetMetrics now swaps the stock Go collector for one that also exports the runtime/metrics CPU classes and GC families (live/goal heap, cycles, pauses, scan sizes). The memstats gauges existing panels use stay, the allocs/frees-by-size histograms are excluded (~30 series per instance for no extra insight), and a repeat registration in one process is a no-op so multi-coin test binaries keep working. Net: ~41 series per instance. Two System-row panels read them: "Go GC share of process CPU" (GC over non-idle CPU, so a 32-core host does not dilute it) and "Go live heap vs GC goal" (the gap GOGC=100 costs in RAM and what a GOMEMLIMIT would cap). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(grafana): move the Go GC CPU panel to id 369, off the id trezor#1828 took Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…rezor#1799) getEthereumTokensTransfers pre-sized the result and skipped an element on a GetContractInfo error, leaving a zero-valued TokenTransfer with empty contract, from and to in the API response. A pending contract creation whose init code looks like transfer(address,uint256) hits this path, because the calldata parser sets the contract to the missing "to". Build the slice with append and, when the metadata read fails, keep the transfer with its addresses and value and the coin's default decimals. Closes trezor#1715 Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…010, EIP-7708) (trezor#1802) * fix(polygon): stop indexing the native token contract 0x…1010 as an ERC-20 The MRC20 system contract emits an ERC-20 Transfer for every native POL move routed through it, which Blockbook already records as an internal transfer. Indexing it as a token gave every holder a "Matic Token" pseudo-balance equal to their native balance, and clients that sum tokens with the native balance counted POL twice. EthereumParser now keeps a set of ignored contracts whose transfer logs and transfer() payloads are not token transfers; the address is checked only for transfer-event logs, before they are decoded. Polygon registers 0x…1010. Token transfers are re-derived from receipt logs at both index and read time, so historical transaction details are cleaned without a reindex, and BlockChainParser.EthereumTypeIsIgnoredContract lets the address token list skip holdings indexed before the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(eth): skip EIP-7708 ETH-transfer logs from the system address Since Glamsterdam (live on Sepolia since 2026-10-06) every nonzero ETH transfer emits an ERC-20 shaped Transfer log from 0xffff…fffe, so each ETH move was also indexed as an ERC20 transfer of a pseudo-token, next to the native value and the internal transfer. The system address has no code and cannot emit logs on a chain without EIP-7708, so every EthereumParser ignores it; no per-chain configuration or fork height is needed, and chains adopting EIP-7708 later are covered. On a post-fork Sepolia block this halves the log-parsing time and allocations. Closes trezor#1845 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ator (trezor#1793) * fix(typescriptify): make blockbook-api.ts reproducible from the generator Regenerating blockbook-api.ts no longer reproduces the committed file: the library inlines ts_type unions, so the hand-added TxChainExtraData and AccountChainExtraData aliases that tests/openapi/src/parity.ts imports disappear, and it emits every pointer field as optional, so the nullable WsNewBlock.evmData turns into `evmData?:` and fails the parity check. The 2-space indent on top rewrote every line and hid real drift. The generator now owns the output: 4-space indent, a tsAliases table that Go fields reference via ts_type:"<Name>", and an opt-in ts_nullable:"true" tag that emits `name: T | null` for pointer fields without omitempty. Unreferenced aliases, misused tags and missing field lines fail the run instead of silently degrading. Nullability stays opt-in because a generic pointer rule would flip ~25 other fields and break both parity and the Suite contract. Regenerated blockbook-api.ts picks up the drift the Go structs already had: TronChainExtraData.claimedVoteReward and feeLimit and the Erc4626Token.asset doc string. Closes trezor#1566 Closes trezor#1734 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * ci: fail the unit-tests job when blockbook-api.ts is stale Adds `make typescriptify` to regenerate blockbook-api.ts inside the build image and `make check-typescriptify` to diff the committed file against a fresh run. The check runs in the unit-tests job rather than Lint because the generator links RocksDB through cgo. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(typescriptify): build the chainExtraData unions from a Go payload registry The generator carried the TxChainExtraData/AccountChainExtraData unions as TypeScript source in a Go table, and the catch-all `{ payloadType: string; payload?: any }` member meant `payloadType === 'tron'` never narrowed the payload (TronChainExtraData | any collapses to any). bchain.ChainExtraPayloads now maps each payloadType to its payload structs; the generator renders a closed union per wrapper from it and types the wrapper fields through ManageType, so the ts_type tags and the alias table go away. A Tron parser test pins GetChainExtraPayloadType to the registry so a new payload cannot ship untyped, and the generator refuses an empty or non-struct registry. Regenerated blockbook-api.ts also picks up TronChainExtraData.withdrawnUnfreeze from the rebase onto master (trezor#1682). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(typescriptify): discover chainExtraData wrappers through an interface The generator still named the two wrapper structs and their registry slot in a table. Each wrapper now implements bchain.ChainExtraPayloadWrapper, which picks its payload struct from a registry entry, and the generator finds wrappers while walking the struct graph it already scans for ts_nullable. The generated file is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(typescriptify): check staleness in the unit tests The committed blockbook-api.ts is now compared against the generator's output by TestCommittedFileIsCurrent, which the existing `make test` already compiles and runs; the check-typescriptify targets and the extra CI step, which rebuilt the whole cgo graph in a second container, are gone. ManageType takes the wrapper's reflect.Type directly, renderUnions no longer touches the converter, and the explicit 4-space indent, already the library default, is dropped. The generated file is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(agents): document how API structs reach blockbook-api.ts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
sidhujag
marked this pull request as ready for review
October 20, 2026 17:39
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This was referenced Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge Trezor Blockbook
masterat3e6a90a287fb4e6d597978baf0aceeec985b5e41into Syscoin, bringing in 301 upstream commits including the spending-outpoint fix, bounded filtered-history scans, transaction lookup improvements, and maintenance updates.Resolve the merge while preserving Syscoin SPT parsing/indexing, Bridge V2 metadata and SPV proofs, fee/burn send options, and Pali transaction summaries. Adapt the new single-output DB reader to Syscoin records, count rejected asset matches against the scan budget, and preserve combined mempool/confirmed pagination. Descriptor scan exhaustion returns an explicit error rather than a successful partial history, including when longer indexed scripts share a complete address descriptor as a prefix. Regenerate the API types and align OpenAPI with existing Syscoin fields, base64 memos, and SPT token standards.
Validation:
PR #31 remains separate: this branch does not enable
-extendedindexor rebuild any deployed database. Hosted unit tests are queued on the self-hosted runner; local results above are complete.