1717 Commits

Author SHA1 Message Date
Artem Rootman
302036c2c7 chore: bump emerald-grpc submodule (adds Mova chain refs) (#883)
Some checks failed
Tests / unit-test (push) Has been cancelled
Bumps emerald-grpc 5191731 -> 111ff26, i.e. p2p-org/emerald-grpc#165, which
adds CHAIN_MOVA__MAINNET = 1170 and CHAIN_MOVA__TESTNET = 10205 to the ChainRef
enum.

Describe.kt:38 and SubscribeChainStatus.kt:31 filter chains with
'Common.ChainRef.forNumber(it.id) != null'. mova's grpcId is 1170, which had no
enum entry, so the chain was dropped from the gRPC surface even though the
mova-nodecore upstream is healthy in all three public-multiregion regions.
Completes #882, which added the chain to the embedded chains.yaml.

Submodule range contains exactly that one commit (+2 lines in
proto/common.proto).
2026-07-27 18:01:40 +03:00
Artem Rootman
66d9e8078a chore: bump public submodule to 2120281 (adds Mova chain) (#882)
Bumps foundation/src/main/resources/public 0a070aac -> 2120281, pulling in
drpcorg/public#237, #238, #240, #241:

- Add Mova chain (MOVA_MAINNET 0xf1cc / grpcId 1170, MOVA_TESTNET 0x2853 /
  grpcId 10205) — the deployed dshackle image did not know the chain, so the
  mova-nodecore upstream in dshackle-public-multiregion was silently skipped
- aptos: fork-choice: height
- jovay: disable-log-index-validation
- tron: support-safe-block-tag: false (new optional schema key, ignored by
  ChainsConfigReader which reads keys explicitly)

Diff is additive only (+28 lines): no chain-id/grpcId changes to existing chains.
2026-07-27 13:16:54 +00:00
a10zn8
481c8f1721 Add bearer token authorization for upstreams (#878) 2026-07-03 17:54:11 +03:00
KirillPamPam
f2c1bd0628 Update deps (#877) 2026-06-30 16:59:58 +04:00
a10zn8
2b79458bf2 Add node_getChainTips fallback for Aztec v5 (#873) 2026-06-25 14:07:55 +03:00
Vadim Filin
634291bc98 Fix hl (#869)
* fix(hyperliquid): derive native-tx routing labels from the ?hl= URL flag

The include_hl_native_tx/exclude_hl_native_tx detector classified a node by scanning the last 300 blocks for a system (native) topup tx from a per-chain address. That cannot work on testnet:

- the configured testnet address 0x6ed35e7d6de4b45f4efb8a91eff31afa49362569 was a regular bot (non-zero gasPrice, not filtered by hl-compliant mode, present on both node types);
- real testnet system txs (from 0x2222...) are far too sparse and bursty (median gap ~1200 blocks, max ~9000 = ~2.5h at ~1s/block) for any practical window;
- eth_getLogs is identical between compliant and non-compliant modes, so there is no cheap wide-range signal either.

Our hl-node upstreams already encode the mode in the URL (?hl=false serves native txs, ?hl=true is compliant). Read that flag directly:
- GenericUpstream captures the configured RPC/WS URL and exposes getRpcConnectionUrl();
- detectHlNativeTx emits include/exclude_hl_native_tx straight from ?hl= when present (cheap, exact, drift-free), for both mainnet and testnet;
- it falls back to the recent-blocks scan only when there is no ?hl= flag, and only on mainnet (testnet without the flag is too sparse to classify);
- the bogus HL_NATIVE_TX_FROM_TESTNET constant is removed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Fix hl

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 13:41:41 +02:00
Vadim Filin
2ddd9c8889 Update emerald-grpc and foundation submodule references (#866)
* add Humanity chain (grpcId 1167)

Bump emerald-grpc and foundation public submodules to include
CHAIN_HUMANITY__MAINNET. Chain.kt is generated at build time.

* add Humanity testnet (grpcId 10202)

bump emerald-grpc and foundation public submodules

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-02 14:47:19 +02:00
msizov
6dd2ee566f add kite mainnet, robinhood mainnet (#865) 2026-05-29 16:19:47 +05:00
KirillPamPam
998e1ac933 newPedningTransactions validator (#864) 2026-05-26 17:13:31 +04:00
a10zn8
f93f350a29 Add method label to upstream.rpc.conn metric (#863) 2026-05-26 12:11:13 +03:00
KirillPamPam
47dcf9a832 Botanix (#859) 2026-05-22 15:23:02 +04:00
KirillPamPam
4b5524d75c Fix java flag (#858) 2026-05-19 14:19:53 +04:00
KirillPamPam
c4502ce513 Add chains (#856) 2026-05-19 12:57:20 +04:00
a10zn8
e8977066d4 chore: remove unused dshackle-cli Node.js tool (#855) 2026-05-18 18:22:46 +03:00
dependabot[bot]
52beea3141 Bump the gradle-minor-and-patch group across 1 directory with 65 updates (#841) 2026-05-18 17:52:41 +03:00
EugeneDrpc
ca08c8f474 adi update (#854) 2026-05-18 16:41:33 +02:00
dependabot[bot]
afa254d3c0 Bump org.yaml:snakeyaml from 1.24 to 2.6 in /foundation (#840)
Bumps [org.yaml:snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.24 to 2.6.
- [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-2.6..snakeyaml-1.24)

---
updated-dependencies:
- dependency-name: org.yaml:snakeyaml
  dependency-version: '2.6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:58:26 +03:00
dependabot[bot]
612231ef94 Bump org.yaml:snakeyaml from 1.24 to 2.6 (#847)
Bumps [org.yaml:snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.24 to 2.6.
- [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-2.6..snakeyaml-1.24)

---
updated-dependencies:
- dependency-name: org.yaml:snakeyaml
  dependency-version: '2.6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:57:49 +03:00
dependabot[bot]
b33e439873 Bump net.logstash.logback:logstash-logback-encoder from 7.2 to 9.0 (#843)
Bumps [net.logstash.logback:logstash-logback-encoder](https://github.com/logfellow/logstash-logback-encoder) from 7.2 to 9.0.
- [Release notes](https://github.com/logfellow/logstash-logback-encoder/releases)
- [Commits](https://github.com/logfellow/logstash-logback-encoder/compare/logstash-logback-encoder-7.2...logstash-logback-encoder-9.0)

---
updated-dependencies:
- dependency-name: net.logstash.logback:logstash-logback-encoder
  dependency-version: '9.0'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:57:40 +03:00
dependabot[bot]
f9244cf9ab Bump nl.jqno.equalsverifier:equalsverifier from 3.10.1 to 4.5 (#848)
Bumps [nl.jqno.equalsverifier:equalsverifier](https://github.com/jqno/equalsverifier) from 3.10.1 to 4.5.
- [Release notes](https://github.com/jqno/equalsverifier/releases)
- [Changelog](https://github.com/jqno/equalsverifier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jqno/equalsverifier/compare/equalsverifier-3.10.1...equalsverifier-4.5)

---
updated-dependencies:
- dependency-name: nl.jqno.equalsverifier:equalsverifier
  dependency-version: '4.5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:55:59 +03:00
dependabot[bot]
a215f77ea8 Bump io.lettuce:lettuce-core from 5.2.2.RELEASE to 7.5.2.RELEASE (#849)
Bumps [io.lettuce:lettuce-core](https://github.com/redis/lettuce) from 5.2.2.RELEASE to 7.5.2.RELEASE.
- [Release notes](https://github.com/redis/lettuce/releases)
- [Changelog](https://github.com/redis/lettuce/blob/7.5.2.RELEASE/RELEASE-NOTES.md)
- [Commits](https://github.com/redis/lettuce/compare/5.2.2.RELEASE...7.5.2.RELEASE)

---
updated-dependencies:
- dependency-name: io.lettuce:lettuce-core
  dependency-version: 7.5.2.RELEASE
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:55:51 +03:00
dependabot[bot]
14c0e13ba6 Bump org.mockito:mockito-inline from 4.0.0 to 5.2.0 (#850)
Bumps [org.mockito:mockito-inline](https://github.com/mockito/mockito) from 4.0.0 to 5.2.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.0.0...v5.2.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-inline
  dependency-version: 5.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:55:33 +03:00
a10zn8
e865173c4d Fix silent stalls in native_subscribe WS subscriptions (Solana, eth pending tx) (#827) 2026-05-18 14:53:22 +03:00
dependabot[bot]
91d2745382 Bump the gradle-minor-and-patch group in /foundation with 3 updates (#839)
Bumps the gradle-minor-and-patch group in /foundation with 3 updates: [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit-framework), [org.jetbrains.kotlin.jvm](https://github.com/JetBrains/kotlin) and [gradle-wrapper](https://github.com/gradle/gradle).


Updates `org.junit.jupiter:junit-jupiter` from 6.0.1 to 6.0.3
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.0.1...r6.0.3)

Updates `org.jetbrains.kotlin.jvm` from 2.3.0 to 2.3.21
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.3.0...v2.3.21)

Updates `gradle-wrapper` from 9.2.0 to 9.5.1
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.2.0...v9.5.1)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: gradle-minor-and-patch
- dependency-name: org.jetbrains.kotlin.jvm
  dependency-version: 2.3.21
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: gradle-minor-and-patch
- dependency-name: gradle-wrapper
  dependency-version: 9.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: gradle-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:50:27 +03:00
dependabot[bot]
4da5bc5281 Bump gradle/gradle-build-action from 2 to 3 (#838)
Bumps [gradle/gradle-build-action](https://github.com/gradle/gradle-build-action) from 2 to 3.
- [Release notes](https://github.com/gradle/gradle-build-action/releases)
- [Commits](https://github.com/gradle/gradle-build-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: gradle/gradle-build-action
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:50:18 +03:00
dependabot[bot]
0266af2eef Bump svenstaro/upload-release-action from 1.pre.release to 2 (#837)
Bumps [svenstaro/upload-release-action](https://github.com/svenstaro/upload-release-action) from 1.pre.release to 2.
- [Release notes](https://github.com/svenstaro/upload-release-action/releases)
- [Changelog](https://github.com/svenstaro/upload-release-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/svenstaro/upload-release-action/compare/v1-release...v2)

---
updated-dependencies:
- dependency-name: svenstaro/upload-release-action
  dependency-version: '2'
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:50:08 +03:00
dependabot[bot]
aae179dad6 Bump actions/checkout from 4 to 6 (#836)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:49:59 +03:00
dependabot[bot]
fada5a22b9 Bump actions/setup-java from 4 to 5 (#835)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:49:46 +03:00
dependabot[bot]
e56bfae287 Bump org.yaml:snakeyaml from 1.24 to 2.6 in /buildSrc (#834)
Bumps [org.yaml:snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.24 to 2.6.
- [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-2.6..snakeyaml-1.24)

---
updated-dependencies:
- dependency-name: org.yaml:snakeyaml
  dependency-version: '2.6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:49:36 +03:00
dependabot[bot]
08763f3815 Bump com.squareup:kotlinpoet from 1.14.2 to 2.3.0 in /buildSrc (#833)
Bumps [com.squareup:kotlinpoet](https://github.com/square/kotlinpoet) from 1.14.2 to 2.3.0.
- [Release notes](https://github.com/square/kotlinpoet/releases)
- [Changelog](https://github.com/square/kotlinpoet/blob/main/docs/changelog.md)
- [Commits](https://github.com/square/kotlinpoet/compare/1.14.2...2.3.0)

---
updated-dependencies:
- dependency-name: com.squareup:kotlinpoet
  dependency-version: 2.3.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:49:20 +03:00
dependabot[bot]
f4a821eb6e Bump eclipse-temurin from 21 to 25 (#832)
Bumps eclipse-temurin from 21 to 25.

---
updated-dependencies:
- dependency-name: eclipse-temurin
  dependency-version: '25'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:49:11 +03:00
dependabot[bot]
7ee5375a53 Bump com.palantir.git-version from 0.12.3 to 5.0.0 (#842)
Bumps com.palantir.git-version from 0.12.3 to 5.0.0.

---
updated-dependencies:
- dependency-name: com.palantir.git-version
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 14:48:27 +03:00
a10zn8
0e978979ab chore: add Dependabot configuration (#831) 2026-05-18 12:28:24 +03:00
EugeneDrpc
6bf8680359 implement lowerbound fix (#830)
* implement fix
2026-05-15 16:05:24 +02:00
EugeneDrpc
b99b74fbe1 Sig nonce fix (#829)
* Update submodules

* sign fix

* copilot comment fix
2026-05-13 12:06:55 +02:00
EugeneDrpc
3c4fe8985e Update submodules (#828) 2026-05-13 11:09:59 +02:00
a10zn8
43b9d50484 Add B² Network mainnet support (#826) 2026-05-08 11:56:37 +03:00
a10zn8
5f454f5247 Feat/autodetect trace rawtx and storage values (#825) 2026-05-05 16:25:17 +03:00
Vadim Filin
aec44185ff Complete Algorand support: real lower-bound detection, settings detector, chain validator (#824)
* Complete Algorand support: real lower-bound detection, settings detector, chain validator

- AvmLowerBoundStateDetector: replace stub with RecursiveLowerBound binary
  search over GET /v2/blocks/{round}?header-only=true. algod has no native
  prune-boundary endpoint; the cheapest reliable signal is a 200/404 probe,
  which converges in O(log latest_round) calls at startup and refreshes
  cheaply via the cached-bound fast path.
- AvmLowerBoundService: forward the upstream so the recursive detector can
  read its head and ingress reader.
- AvmUpstreamSettingsDetector: read /v2/versions for client_version
  (build.major.minor.build_number) and tag client_type=algod.
- AvmChainSpecific: wire the settings detector and add an optional chain-id
  validator that compares the configured chain-id against /v2/genesis
  network/id (Algorand has no EVM-style numeric chain id; skip cleanly when
  unset so existing chains.yaml entries continue to work).

* avm: map drpc synthetic chain-id to algod genesis network

chains.yaml uses synthetic chain-ids for Algorand (0x65901 mainnet,
0x65902 testnet, 0x65903 betanet) while algod's /v2/genesis reports
network as mainnet/testnet/betanet. The validator now translates first
and only falls back to the literal-match candidates so deployments that
point chain-id at the network name directly continue to validate.

* avm: tighten genesis validator and correct chain-id provenance comment

The chain-ids 0x65901/0x65902/0x65903 are official Algorand chain-ids,
not synthetic drpc assignments - update the comment accordingly.

Drop the bare-id and network-id fallback candidates: schema id `v1.0` is
shared across mainnet/testnet/betanet, so accepting it as a standalone
match would let a chain-id like `v1.0` validate against the wrong
network. Validation now requires the chain-id to map to a known network
and `genesis.network` to match it exactly, otherwise SettingsError /
FatalSettingError.

* avm: drop verbose comments; switch lower-bound probe to /v2/blocks/*/hash

The hash variant returns ~70 bytes per probe instead of the multi-kB
block header, so non-archival cold starts use noticeably less bandwidth
without any accuracy loss.

* avm: fix /genesis path - it's root-level, not under /v2/

algod's genesis endpoint lives at /genesis, not /v2/genesis (the latter
404s, which was making the chain-id validator reject every Algorand
upstream).

* avm: fix /versions path - it's root-level, not under /v2/

algod's versions endpoint lives at /versions, not /v2/versions.

* avm: bring health validator and chain-id error policy in line with nodecore

- validate(): also report Unavailable on `last-round=0` (node has no head yet)
  and on `stopped-at-unsupported-round` (halted on a consensus upgrade) so
  the router stops sending traffic to a stuck node. Previously only
  `catchup-time > 0` mapped to SYNCING; the other two conditions were
  silently treated as OK.
- AvmStatus: add `stopped-at-unsupported-round`.
- validateGenesis(): unknown chain-id is a static config mistake that won't
  resolve on its own, so return UPSTREAM_FATAL_SETTINGS_ERROR instead of
  UPSTREAM_SETTINGS_ERROR (which loops the validator forever).

* avm: emit cached or LowerBoundType.UNKNOWN when probe fails

If the recursive search hits a hard error or the upstream has no
retained blocks at all, emit either the previously cached STATE bound
(so the router keeps using the last known good value) or an explicit
LowerBoundData(0, UNKNOWN) so consumers see a definite "we don't know"
signal instead of silence.

* Remove hardcode
2026-05-05 13:39:25 +02:00
Vadim Filin
34e12da583 Add Aztec upstream settings detection and improve node validation (#822)
* feat(aztec): add missing v4 RPC methods for full Aztec network support

Adds methods identified by the Aztec team audit (v4 branch) that were
returning -32601 method not available on dRPC:

- node_getBlockByArchive
- node_getBlockByHash
- node_getBlockHeaderByArchive
- node_getCheckpointNumber
- node_getMaxPriorityFees
- node_getTxsByHash

Also adds the rest of the v4 AztecNodeApiSchema surface that was not
yet in the allowlist so the upstream chain has full coverage:

- node_getBlockHashMembershipWitness
- node_getCheckpointedBlocks
- node_getCheckpointedBlockNumber
- node_getCheckpoints
- node_getCurrentMinFees
- node_getL1ToL2MessageCheckpoint
- node_getPrivateLogsByTags
- node_getPublicLogsByTagsFromContract

* feat(aztec): add upstream settings detector

Modeled on Starknet/Near settings detectors. Calls node_getNodeVersion
to set client_type=aztec and client_version=&lt;version&gt; labels on each
Aztec upstream, so monitoring/routing can distinguish nodes by version.

Closes the parity gap with StarknetUpstreamSettingsDetector that the
Aztec implementation was missing.

* feat(aztec): wire settings detector and add L2 tips health validation

Closes the parity gap with StarknetChainSpecific:

- Override upstreamSettingsDetector() to enable client_type/version
  labels via the new AztecUpstreamSettingsDetector.
- Add a second health validator that probes node_getL2Tips and rejects
  upstreams that return empty tips, inconsistent (proven > proposed) or
  show excessive prover lag. Until now only node_isReady was checked,
  which an Aztec node returns true for even when its tips are stale or
  empty - exactly the failure mode that surfaces to clients as
  "block height goes backwards" and "method returns null intermittently"
  during routing across multiple Aztec upstreams.

Removes the unused parseInstant() helper.

* feat(aztec): finish chain-specific stubs

Three pieces in AztecChainSpecific were stubbed out and now have a real
implementation:

- upstreamSettingsValidators returned emptyList(). It now validates the
  upstream's reported node_getChainId against the configured chain id
  (decimal or 0x-hex compared numerically) and emits
  UPSTREAM_FATAL_SETTINGS_ERROR on mismatch, mirroring the pattern
  Polkadot uses with system_chain. This ensures Aztec mainnet upstreams
  cannot be silently mixed with testnet/devnet ones.

- getFromHeader threw NotImplementedError. It is reachable only from
  GenericWsHead (websocket new-head delivery), and Aztec is HTTP-poll
  only - so it was never called. Route it through parseBlock so the
  same parser handles header/tip-shaped payloads if a future WS-capable
  backend is wired in. Behaviour today is unchanged.

- listenNewHeadsRequest / unsubscribeNewHeadsRequest still cannot be
  satisfied (Aztec has no websocket newHeads subscription) but now
  throw UnsupportedOperationException with a descriptive message
  instead of NotImplementedError, matching the message used in nodecore.

* fix(aztec): address Copilot review on validateTips

- Use a single Long-typed `threshold` variable for both the comparison
  and the warning log. Previously the check used `lagging.toLong() * 10`
  but the log printed `lagging * 10` (Int multiplication), which could
  overflow and disagree with the check.
- Wrap `Global.objectMapper.readTree` in try/catch and explicitly check
  for empty/whitespace input, returning SYNCING with a warning instead
  of letting parse errors fall through to UNAVAILABLE. The previous
  `raw.isMissingNode` branch was dead code (root is never a MissingNode).
- Treat a missing/unparseable `proven.number` as SYNCING with its own
  warning. Previously it defaulted to 0, conflating a legitimate zero
  with missing data and risking a false "excessive prover lag" verdict
  on early-genesis upstreams.

* test(aztec): add AztecChainSpecificTest covering validateTips and parseBlock

Per Copilot review on PR #822: locks in the validateTips classification
behaviour for the cases that matter for routing health -

- healthy tips (OK)
- lagging=0 disabling the gap check (OK)
- empty / whitespace / unparseable / JSON-null payloads (SYNCING)
- missing or zero proposed (SYNCING)
- missing proven (SYNCING)
- proven ahead of proposed (SYNCING)
- excessive proposed-proven gap relative to laggingLagSize (SYNCING)

Plus a parseBlock test that asserts proposed.{number,hash} is what the
head tracker sees.

* test(aztec): add AztecUpstreamSettingsDetectorTest

Per Copilot review on PR #822: locks in version-string parsing for the
representative `node_getNodeVersion` payloads -

- quoted JSON string with leading "v"
- quoted JSON string without leading "v"
- raw unquoted string (some clients)
- empty string -> UNKNOWN_CLIENT_VERSION
- upstream call error -> UNKNOWN_CLIENT_VERSION
- object payload with `nodeVersion` field (label detection path)

Also asserts the (client_type, client_version) label pairs are produced
in the right order via detectLabels().

* feat(aztec): replace stub lower-bound detector with real probing

Previously AztecLowerBoundStateDetector returned a hardcoded
LowerBoundData(1, STATE) regardless of what the upstream actually had.
That meant routing assumed every Aztec upstream was a full archive,
which is exactly what the customer's "method returns null intermittently
for an old block" complaint looked like - a non-archive upstream got
asked for state it never kept and answered `null`, but the router did
not know to prefer an archive peer.

Switch to the same RecursiveLowerBound machinery Polkadot and Beacon
use: binary-search [0, head] by probing node_getBlock(N), treat a JSON
`null` result (or "block not found"/"pruned"-style error) as the
upstream not having that block, and converge on the lowest available
block. The result is then used by the existing routing layer to send
historical-state requests only to upstreams that can actually serve
them.

* feat(aztec): pass upstream to lower-bound state detector

Required for the now-real AztecLowerBoundStateDetector, which probes
node_getBlock(N) on the upstream itself. Mirrors PolkadotLowerBoundService.

* fix(aztec): harden validateChainId parse path; simplify whitespace check

- validateChainId now defensively handles empty/whitespace bodies and
  unparseable JSON the same way validateTips does: a concise warn log
  and UPSTREAM_SETTINGS_ERROR (Copilot review L187). Without this, a
  bad chain-id payload bubbled up through GenericSingleCallValidator
  as a generic error with no Aztec-specific log line.
- chainIdMatches becomes `fun` instead of `private fun` so it can be
  unit-tested directly.
- validateTips whitespace check rewritten as `String(data).isBlank()`
  for readability; behaviour unchanged.

* fix(aztec): tolerate whitespace/casing in lower-bound null check

Per Copilot review on PR #822 L61: the detector compared the result
byte-for-byte to "null", so a payload like "null\n" or "NULL" would
have been treated as a real block and made the binary search converge
on a non-existent lower bound. Switch to a trimmed case-insensitive
String comparison wrapped in `isNullResult()` so the helper can be
covered separately by tests.

* fix(aztec): make parseClientVersion handle object payloads

Per Copilot review on PR #822 L35: parseClientVersion only stripped
quotes/`v` from raw bytes, so an object payload like
{"nodeVersion": "v0.84.0"} would have been returned literally as the
"version" string by detectClientVersion(), while detectLabels() (which
goes through clientVersion(JsonNode)) would have correctly returned
"0.84.0". Try JSON parsing first and delegate to clientVersion(JsonNode)
so both paths agree; the literal string-strip stays as a fallback for
raw non-JSON responses.

* test(aztec): add validateChainId/chainIdMatches coverage

Per Copilot review on PR #822 L115: the new node_getChainId settings
validator had no unit tests. Adds:

- chainIdMatches: decimal vs hex equivalence (1 vs 0x1, 0 vs 0x0,
  Sepolia 11155111 vs 0xaa36a7), explicit mismatches, case
  normalisation, leading-zero stripping
- validateChainId: VALID on numeric/string match, FATAL on numeric
  mismatch, SETTINGS_ERROR for empty/whitespace/unparseable/JSON-null
  payloads and unexpected object payloads.

* fix(aztec): parse v4 nested L2Tips schema (proven.block.number)

Live testnet log on aztec-testnet 4.2.0-rc.1 showed every health probe
warning "returned tips without a proven number" and the upstream stuck
in SYNCING. The reason: node_getL2Tips changed shape between Aztec
versions:

  v3:  {proposed: {number, hash}, proven: {number, hash}, checkpointed: {number, hash}}
  v4:  proven/finalized/checkpointed each became
       {block: {number, hash}, checkpoint: {number, hash}}
       proposed stayed flat.

parseBlock and validateTips now look at v4 nested paths first
(proposed.{number,hash}, proven.block.number) and fall back to the v3
flat paths so older upstreams keep working too. Removes the noisy
full-payload dump from the warn log.

* test(aztec): cover v4 nested L2Tips schema in chain-specific tests

Adds two cases for the actual aztec-testnet 4.2.0-rc.1 payload shape:
- parseBlock against the v4 nested {proposed, proven.block, finalized,
  checkpointed.block} response, asserting proposed.{number,hash} are
  picked up.
- validateTips returns OK on the same payload (gap 66934-66908=26 ≤
  threshold 50 with lagging=5).

Existing v3-flat fixtures remain to lock the fallback path.

* perf(aztec): probe lower bound via node_getBlockHeader instead of getBlock

The recursive lower-bound detector does ~log2(currentHeight) probes per
refresh cycle. node_getBlock returns the full block with transactions
(often KBs), but for the "is this block present?" check we only need
the header. node_getBlockHeader returns the same `null`-on-missing
signal in a much smaller payload.

* fix(aztec): seed STATE=1 fallback so UNKNOWN bound never appears

On the very first detection tick the upstream head is not yet known,
so RecursiveLowerBound.initialRange() returns Mono.empty() and the
recursive search produces an empty Flux. The base LowerBoundDetector
then substitutes LowerBoundData.default() = (0, UNKNOWN), which gets
stored alongside the real STATE bound discovered five minutes later.
The result was the noisy "lower bounds=[STATE=1, UNKNOWN=0]" line in
the multistream state log.

Aztec full nodes are archive by default, so substitute STATE=1 as the
detector's own empty-fallback. Subsequent ticks still re-run the binary
search and refine the value if the upstream prunes state.

* feat(aztec): retry transient HTTP 5xx in health validators

Aztec public RPC endpoints (testnet/mainnet) occasionally answer
502/503/504 with HTML during deploys or sequencer failovers. The
default GenericSingleCallValidator would turn the very first such hit
into UNAVAILABLE, ejecting the upstream from the multistream until the
next probe succeeded - causing visible flapping in the state log even
though the upstream itself was healthy.

AztecRetryingValidator wraps the read with reactor's Retry.backoff
filtered on ChainException messages "HTTP Code: 502/503/504"; up to two
retries with 500ms backoff are attempted before falling through to the
existing onError (Unavailable) branch. Non-transient errors (timeouts,
JSON-RPC errors, 4xx) bypass the retry and propagate immediately.

* feat(aztec): use AztecRetryingValidator for health probes

The two health-probe validators (node_isReady, node_getL2Tips) now go
through AztecRetryingValidator, which retries transient HTTP 5xx
errors a couple of times before falling through to UNAVAILABLE. This
absorbs the occasional 502/503/504 the public Aztec endpoint emits
during deploys/failovers and stops the upstream from flapping in/out
of the multistream (visible in the live testnet log as a single
status=[UNAVAILABLE/1] tick every few minutes).

Settings validation (node_getChainId) is left on the default
GenericSingleCallValidator since it runs once at startup; if it hits
a 5xx the upstream stays unvalidated until the next health tick takes
over.

* fix(aztec): simplify retry predicate to avoid Kotlin SAM edge cases

Removes the callable reference to a companion-object predicate
(`::isTransientHttpError`) and the `onRetryExhaustedThrow` BiFunction
lambda - both have historically tripped over Kotlin/Reactor SAM
resolution. Inlines the predicate into a plain lambda bound to a
private instance method, and lets Retry's default exhaustion behaviour
propagate the underlying cause as-is.

Renames the constructor parameter `backoff` to `retryBackoff` to
disambiguate from the static `Retry.backoff` factory method at the
call site (the original code worked but the duplication was avoidable).

Also widens the slf4j log when message is null.

* fix(aztec): log full throwable in retrying validator; drop const Long literal

- Per Copilot review L83: log.error now passes the throwable as a
  positional slf4j argument so the full stack trace survives, matching
  GenericSingleCallValidator's diagnosability.
- Replaces `const val DEFAULT_MAX_RETRIES: Long = 2` with a plain
  @JvmField `val ... = 2L`. The literal-2 form has bitten Kotlin
  compilation in the past depending on toolchain version; explicit Long
  literal removes the ambiguity.

* fix(aztec): getFromHeader fails fast for polling-only chain

Per Copilot review L65: parseBlock parses the node_getL2Tips response
shape, not a websocket newHeads event. Delegating getFromHeader to
parseBlock would silently produce height=0 BlockContainers if a WS
connector were ever (mis)wired for Aztec. Reverting getFromHeader to
throw UnsupportedOperationException with the same message used for the
listen/unsubscribe newHeads stubs - same approach Starknet/Near/AVM
take for polling-only chains.

* fix(aztec): return UNKNOWN on JSON-null / unparseable version payloads

Per Copilot review L52: when the JSON payload parsed successfully but
clientVersion(node) couldn't extract a usable version, parseClientVersion
fell through to the literal trim/quote-strip branch and could end up
returning the whole JSON object as the "version" string, polluting
client_version labels and version-rule logic.

Trust clientVersion(JsonNode) when JSON parses, treat literal "null"
(any case) and blank strings as UNKNOWN_CLIENT_VERSION in both the
JSON and string-fallback paths. Also tightened the JsonNode branch to
emit UNKNOWN for "null" string values.

* revert(aztec): drop AztecRetryingValidator

Per user direction. Restoring GenericSingleCallValidator for the Aztec
health probes in the next commit. The retry-on-502 path will be
revisited later if needed.

* revert(aztec): use GenericSingleCallValidator for health probes

Restores the original validator for node_isReady and node_getL2Tips.
The retry-on-5xx behaviour added in earlier commits (AztecRetryingValidator)
will be revisited later if needed - keeping the chain-specific in line
with the rest of the polling chains for now.

Schema, settings-detector, chain-id and L2-tips-content validations
introduced earlier in this PR all remain in place.

* refactor(aztec): replace RecursiveLowerBound binary search with single getWorldStateSyncStatus call

node_getWorldStateSyncStatus reports oldestHistoricBlockNumber directly
(see yarn-project/stdlib/src/interfaces/world_state.ts in
AztecProtocol/aztec-packages). One RPC per refresh instead of
~log2(currentHeight) probes, and the value comes from the world-state
synchronizer itself rather than being inferred from JSON-null responses
to node_getBlockHeader.

Falls back to STATE=1 (Aztec archive default) when the call fails - the
public Aztec endpoint occasionally returns transient errors on this
method per the Aztec team's audit; the next refresh tick will pick up
the real value.

* fix(aztec): on error keep cached STATE bound instead of clobbering it

Previous fallback emitted STATE=1 on error. Because the base
LowerBoundDetector filter accepts any LowerBoundData with lowerBound==1
unconditionally, a transient world-state-sync-status failure on a
pruning Aztec upstream would have rewritten its real prune boundary
(e.g. STATE=10000) down to STATE=1 - the router would then send
historical-state requests there and get nulls.

On error now:
- if a STATE bound is already cached, re-emit the cached value (filter
  passes, updateBound is a no-op, cache stays);
- if no STATE has been read yet, emit nothing - the router sees no
  STATE bound for this upstream until the next successful refresh.

Same semantics applied to the "field missing/non-numeric" branch of
parseOldestHistoric, which also previously hard-defaulted to 1.

* fix(aztec): on transient error re-emit cached LowerBoundData (same timestamp)

The previous on-error branch emitted a freshly-constructed
`LowerBoundData(cached.lowerBound, STATE)`, which carries a new
`Instant.now()` timestamp. That feeds LowerBounds.updateBound a "new
sample, same value" point and biases the linear-regression coefficient
`k` toward zero - which then makes predictLowerBound() under-predict
how fast the sliding window is moving forward.

Re-emit the cached LowerBoundData object as-is. updateBound's
`newBound.timestamp != lastBound.timestamp` guard short-circuits, so
the regression is left untouched. Cache stays at the last good value
until the next successful refresh.

Also stops the malformed-payload branch of parseOldestHistoric from
hard-defaulting to STATE=1 (same clobbering risk via the special
`lowerBound == 1L` filter); it now prefers the cache, only synthesises
STATE=1 on a first-tick malformed response with no cache to fall back
on.

* E2E test success

* revert: drop unrelated Makefile/gitignore changes

Per review comment "Too hard" on Makefile L6: the `clean && ./gradlew run`
addition forced a full rebuild on every `make run-main`, which is overkill
for normal dev use. The original `./gradlew run -x test` is restored.

The matching `.gitignore` line (`*_test.sh`, added in the same series of
commits) is also reverted in the next commit - both changes are unrelated
to the Aztec PR scope.

* revert: drop unrelated *_test.sh ignore rule

Out-of-scope of the Aztec PR; could shadow legitimate `*_test.sh`
files anywhere in the tree. The smoke-test script lived only in
local sandboxes.

* fix(aztec): never synthesize STATE=1 on missing oldestHistoricBlockNumber

Per review comment on AztecLowerBoundStateDetector L106 ("One means all
history available"): the malformed-payload-and-no-cache branch was
returning `LowerBoundData(1, STATE)`, which advertises the upstream as
a full archive node. That is a lie if the upstream simply didn't tell
us where its prune boundary is, and the base LowerBoundDetector filter
accepts `lowerBound == 1L` unconditionally so the lie would persist in
the cache and bias router decisions.

retainCachedOrSkip() now centralises the failure path:
 - if we have a cached LowerBoundData, re-emit it unchanged (timestamp
   guard makes updateBound a no-op, regression preserved);
 - otherwise emit nothing - the router sees no STATE bound for this
   upstream until the next successful refresh.

The same helper handles both the RPC-error path (was already correct)
and the malformed-payload path (was the buggy branch).

* refactor(aztec): drop validateTips validator per review

Per review comment on AztecChainSpecific L142 ("Do we really need this?"):
the second health validator (node_getL2Tips + custom shape/lag checks)
duplicated work that other parts of the system already handle:

- empty / unparseable / null tips: the head-tracker (which polls
  node_getL2Tips for `latestBlockRequest`) already produces a height=0
  BlockContainer in those cases, and HeadLagObserver flags the upstream
  as lagging.
- proposed-vs-proven and prover-lag heuristics: too strict for Aztec
  testnet, where prover lag legitimately reaches dozens of blocks while
  the network is still healthy from a routing perspective.

node_isReady alone now drives upstream availability; head-skew detection
stays where it belongs (HeadLagObserver). validateTips and the unused
PROVEN_NUMBER paths are removed. The schema comment on the remaining
PROPOSED_* arrays is kept since parseBlock still relies on the v3/v4
shape fallback.

* Fix review comments

* Fix review comments
2026-05-04 21:23:09 +02:00
Anton
c78dc7f6c7 Add 'state is not available' as non-retryable for proof lower bound (#821)
opBNB (BSC-derived) returns this error from eth_getProof on pruned
state. Without it in NO_PROOF_ERRORS, RecursiveLowerBound retries
indefinitely and emits the "report it to dshackle devs" warning.
The same string is already non-retryable in the state detector.
2026-05-01 16:12:12 +03:00
Vadim Filin
c653cd9925 Allow unescaped control chars at the JSON-RPC response parser (#819)
The previous fix added ALLOW_UNQUOTED_CONTROL_CHARS only to the lenient
parser used inside the upstream-settings detector. In production that
parser is never reached: the response body is parsed first by
ResponseParser.parseInternal with a default JsonFactory, which rejects
the raw LF embedded in Moca's web3_clientVersion result and turns the
response into an error before it ever reaches the detector.

Enable ALLOW_UNQUOTED_CONTROL_CHARS on the JsonFactory used by
ResponseParser so the RPC envelope parses, the result bytes flow
through, and the existing lenient detector path handles them as
before.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-30 18:14:38 +02:00
Vadim Filin
aeebca7248 Handle unescaped control chars in web3_clientVersion responses (#818)
* Fix node type detection for Tendermint nodes with unescaped control chars

Some Tendermint EVM nodes (e.g. Moca) return a web3_clientVersion result
like "Version dev ()\nCompiled at  using Go go1.23.11 (amd64)" where the
\n is a real LF (CTRL-CHAR, code 10), not a JSON escape. Jackson's
default parser rejects this with "Illegal unquoted character", causing
EthereumUpstreamSettingsDetector to fail node type detection.

Parse the response leniently with ALLOW_UNQUOTED_CONTROL_CHARS at this
single call site, and trim the version label to its first line so the
resulting client_type/client_version labels stay clean.

* Normalize whitespace uniformly instead of dropping post-newline content

Previous fix took only the first line of the version string, which would
drop the version token if a node placed it after the LF (only Moca
happens to put it on the first line). Replace that with a uniform
whitespace-collapsing helper applied in both code paths that extract a
client version (mapping for node-type detection and parseClientVersion
for client-version detection), so every token of the version string is
preserved as a single-line, single-spaced label regardless of where it
sits.

* Revert version-string normalization, keep only ALLOW_UNQUOTED_CONTROL_CHARS

The lenient JSON parser is sufficient on its own to fix the original
parse error. Reverting the mapping/parseClientVersion changes leaves the
existing slash/semver/dot logic untouched and lets the raw version
string flow through as before.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-30 11:46:55 +02:00
Vadim Filin
06ce6927e2 Add abcore (#817) 2026-04-28 19:16:37 +02:00
Vadim Filin
08713cef56 Add orderly support (#816) 2026-04-24 13:09:58 +02:00
Vadim Filin
24d54d34d7 Update chains (#814) 2026-04-23 17:45:41 +02:00
Artem Rootman
ed7cfdc90b Add client_type label to upstream lag and availability metrics (#815) 2026-04-23 14:26:59 +00:00
Vadim Filin
5414e06e46 Add support for Algorand Virtual Machine (AVM) blockchain (#813)
* add AVM blockchain type support for Algorand

Introduce a new blockchain type `avm` (Algorand Virtual Machine) so
Algorand-based chains (Mainnet, Testnet, Betanet) declared in chains.yaml
can be parsed and driven through dshackle's generic polling pipeline.

Key additions:
- BlockchainType.AVM (JSON_RPC) and `avm` mapping in chain codegen
- AvmChainSpecific: polls `algod_getBlock` for latest block and uses
  `algod_status` / `algod_genesis` for health and settings validation
- DefaultAvmMethods: allowed algod_* RPC surface with send-transaction
  broadcast quorum and hardcoded `algod_chainId` / `algod_genesisId`
- AvmLowerBoundService / AvmLowerBoundStateDetector for lower-bound tracking
- Registration in ChainSpecificRegistry and CallTargetsHolder
- Unit tests for block parsing, sync validation and method policies

https://claude.ai/code/session_01TZ64DZis9YaJSU3oMwtRLe

* inline DummyChainReader in AVM test

Match the Starknet test pattern and drop the dedicated DummyChainReader
file; instead pass an anonymous ChainReader directly where needed.

https://claude.ai/code/session_01TZ64DZis9YaJSU3oMwtRLe

* fix AVM network validation logic

Validation used AND across all conditions, so when netVersion was 0
(typical for non-EVM chains) the check short-circuited and allowed a
mismatching chain.chainId through. Simplify to the Cosmos pattern:
reject when chain.chainId is set and doesn't match genesis.network.

https://claude.ai/code/session_01TZ64DZis9YaJSU3oMwtRLe

* switch AVM to REST transport for native algod nodes

Native Algorand algod nodes expose a REST API (/v2/status, /v2/blocks,
/v2/transactions, ...) rather than JSON-RPC, so drive AVM upstreams
through dshackle's REST reader instead:

- BlockchainType.AVM is now ApiType.REST
- AvmChainSpecific polls GET#/v2/status for latest block info and uses
  GET#/v2/genesis for chain-id validation, both via RestParams
- DefaultAvmMethods exposes the algod /v2 endpoints with VERB#/path
  identifiers (POST#/v2/transactions uses BroadcastQuorum for sends)
- Tests updated to match the REST-based method names

https://claude.ai/code/session_01TZ64DZis9YaJSU3oMwtRLe

* align AVM endpoints with algod OpenAPI spec

Validated the AVM method surface against the official algod spec
(algorand/go-algorand algod.oas3.yml) and corrected mismatches:

Wrong paths removed:
- GET /v2/genesis, /v2/versions, /v2/health, /v2/ready, /v2/metrics
  (these exist at root level, not under /v2/)
- GET /v2/blocks/{round}/header (use header-only query on /v2/blocks/{round})
- GET /v2/blocks/{round}/transactions (no such path)
- GET /v2/lightheader/{round} (real path is /v2/blocks/{round}/lightheader/proof)
- POST /v2/transactions/dryrun (real path is /v2/teal/dryrun, already listed)

Added real endpoints:
- Root: GET /genesis, /health, /ready, /metrics, /versions, /swagger.json
- /v2/blocks/*/txids, /v2/blocks/*/logs, /v2/blocks/*/lightheader/proof
- /v2/accounts/*/transactions/pending
- /v2/deltas/*, /v2/deltas/*/txn/group, /v2/deltas/txn/group/*

Also fixed AvmChainSpecific settings validator: use GET#/genesis (root)
instead of the non-existent GET#/v2/genesis.

Added regression test asserting the spurious paths are NOT callable.

https://claude.ai/code/session_01TZ64DZis9YaJSU3oMwtRLe

* fetch real AVM block data via chained api.read

parseBlock now uses the ChainReader passed through AbstractPollChainSpecific
to fetch GET#/v2/blocks/{lastRound}?header-only=true after /v2/status,
so BlockContainer.timestamp comes from block.ts and hash/parentHash
decode from block.seed/block.prev (base64 -> 32 raw bytes) instead of
being synthesized from the round number.

Also differentiate quorum per method in DefaultAvmMethods: single-
resource lookups (/v2/blocks/{round}, /v2/accounts/{addr}, etc.) now
use NotNullQuorum so a single replica returning empty/404 doesn't
shadow a valid response from another upstream. List and status
endpoints keep AlwaysQuorum; send endpoints keep BroadcastQuorum.

https://claude.ai/code/session_01TZ64DZis9YaJSU3oMwtRLe

* Update submodules

* Update chains

* Fix settings validation

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 13:10:56 +02:00
Vadim Filin
aa40578664 Add safe error parsing for REST HTTP responses (#812)
* fix(rest): handle non-JSON error bodies on non-200 HTTP responses

When a REST upstream (e.g. TON) returns a non-200 status with a plain-text
body such as "Service Unavailable" but advertises application/json as the
content-type, the error parser would throw JsonParseException
("Unrecognized token 'Service'"), which bubbled up as a generic -32005
ChainException and obscured the real cause.

Catch JsonParseException/IOException around readError and fall back to a
"HTTP Code: X" ChainCallError (CODE_UPSTREAM_INVALID_RESPONSE), matching
the behavior already implemented in JsonRpcHttpReader. Also return the
fallback when readError returns null.

* fix(rest): close parser, log stack traces, fall back on empty errors

Address PR review feedback:
- Close the Jackson JsonParser via `use { ... }` to release buffers.
- Switch to parameterized SLF4J logging and pass the exception so stack
  traces are preserved on parsing/IO failures.
- Treat a parsed error with code=0 and blank message (e.g. from a bare
  `{}` body) as no useful error info and fall back to the generic
  HTTP-status error so the upstream status isn't silently dropped.

* fix(rest): short-circuit HTTP 503 without JSON parsing

Narrow the fix to exactly the case reported: HTTP 503 Service Unavailable
responses (typically from Cloudflare) carry a plain-text or HTML body but
can be served with Content-Type: application/json, so attempting to parse
the body as JSON raises JsonParseException and surfaces as a confusing
-32005 "Unrecognized token 'Service'" error.

For status 503, skip parsing entirely and return a ChainCallError
directly. All other non-200 responses keep the existing behavior.

* docs(rest): align 503 branch comment with error code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-21 18:24:33 +02:00
a10zn8
25dbc7c388 response signing with auth key (#811)
* docs: spec for unifying response signing with auth key

Design для замены отдельной SignatureConfig на подпись ответов
RSA-ключом, взятым из auth.server.provider-private-key.

Made-with: Cursor

* docs: implementation plan for auth-based response signing

План реализации по спеке 2026-04-20-response-signing-with-auth-key-design.md.
Разбит на 9 задач: новые RsaSigner/DisabledSigner по TDD, переработка фабрики
под AuthorizationConfig, удаление SignatureConfig/signed-response, снятие
cache-гейта в EthereumLocalReader, обновление документации.

Made-with: Cursor

* feat(signature): add RsaSigner using SHA256withRSA

Made-with: Cursor

* feat(signature): add DisabledSigner that throws on sign()

Made-with: Cursor

* refactor(signature): ResponseSigner.sign returns non-null Signature

Made-with: Cursor

* refactor(signature): build signer from AuthorizationConfig

Made-with: Cursor

* refactor(config): remove signed-response YAML section and SignatureConfig

Made-with: Cursor

* refactor(signature): remove EcdsaSigner and NoSigner

Remove the obsolete ECDSA signer and the transitional NoSigner stub.
Replace NoSigner usages in tests with DisabledSigner, and pass
DisabledSigner to BroadcastReader in tests (signer is now non-null).

Made-with: Cursor

* refactor(local-reader): serve cached results for requests with nonce

Made-with: Cursor

* docs: replace signed-response with auth section incl. response signing

Made-with: Cursor

* docs: correct auth section YAML and expand Response Signing note

- docs/reference-configuration.adoc: fix the YAML shape of the auth
  section (server.keys.{provider-private-key,external-public-key} and
  publicKeyOwner) to match AuthorizationConfigReader, and expand the
  Response Signing subsection with the actual signed-message format.
- .gitignore: ignore /demo/response-signing/ (local sandbox with keys
  and generated gRPC stubs — regenerate with generate-keys.sh + protoc)
  and /docs/superpowers/ (specs/plans kept local-only for now). Any
  previously tracked files under these paths are untracked here.

Made-with: Cursor

* feat(upstream): auto-inject secure-signed label when signing is enabled

Expose ResponseSigner.enabled and thread the bean through UpstreamCreator
so Generic/Ethereum/Bitcoin creators can enrich each upstream's labels
with secure-signed=true whenever auth-backed response signing is active.
A user-provided value for the label is preserved.

Made-with: Cursor
2026-04-21 10:33:37 +03:00