feat: Stellar family support (stellar, stellar-testnet) - #295
feat: Stellar family support (stellar, stellar-testnet)#295artemrootman wants to merge 5 commits into
Conversation
Re-lands #287 onto main: the original PR was stacked on the ton branch. Same diff, applied onto main with the ripple/ton-only context stripped; also carries the shared DecreasingBoundDetector capability the horizon lower bound depends on (otherwise landing with #284). - stellar-rpc (json-rpc) and Horizon (rest) as split or combined upstreams - per-API health/chain validators, labels, lower bounds (horizon bound may legally decrease on reingest backfill) - method specs stellar-json-rpc/stellar-horizon, docs, tests - bumps pkg/chains/public for the stellar chain entries
…ort-main # Conflicts: # README.md # docs/nodecore/05-upstream-config.md # docs/nodecore/11-method-specs.md
…ort-main # Conflicts: # README.md # docs/nodecore/05-upstream-config.md # docs/nodecore/11-method-specs.md # internal/upstreams/upstream_factory.go # pkg/chains/chains.go
|
|
||
| func (s *StellarChainSpecificObject) LabelsProcessor() labels.LabelsProcessor { | ||
| var detector labels.ClientLabelsDetector | ||
| if s.flavor == stellarHorizon { |
There was a problem hiding this comment.
Need separate specific objects like ton
There was a problem hiding this comment.
Done - split into StellarRpcChainSpecificObject and StellarHorizonChainSpecificObject with a shared embedded base; the factory picks the object from the primary connector type, same as ton.
| } | ||
|
|
||
| func (s *StellarChainSpecificObject) CapDetectors(input caps.DetectorInput) []caps.CapDetector { | ||
| return caps.DefaultCapDetectors(s.upstreamId, input.WsConnector) |
There was a problem hiding this comment.
Stellar doesn't have WS?
There was a problem hiding this comment.
Right, no WS: stellar-rpc is plain HTTP JSON-RPC (no subscriptions at all), and Horizon streams over SSE, not websocket. CapDetectors returns nil now.
| } | ||
|
|
||
| func (s *StellarChainSpecificObject) GetFinalizedBlock(ctx context.Context) (protocol.Block, error) { | ||
| return s.GetLatestBlock(ctx) |
There was a problem hiding this comment.
Latest == finalized?
There was a problem hiding this comment.
Yes - SCP closes ledgers with immediate finality: a closed ledger is final, there are no reorgs, and both APIs only ever serve closed ledgers, so latest == finalized. Also wired the generic BaseBlockProcessor to poll it, same as ton.
| } | ||
|
|
||
| func (s *StellarHorizonLowerBoundDetector) DetectLowerBound(ctx context.Context) ([]protocol.LowerBoundData, error) { | ||
| elder, err := s.fetchElderLedger(ctx) |
There was a problem hiding this comment.
Is there a specific method to detect lower bounds? If so, use only it. Otherwise use a simple binary search like in evm
There was a problem hiding this comment.
There is a specific method - Horizon's root document publishes history_elder_ledger directly. The detector now uses only it: one call with retries, no fallback/local bound.
| } | ||
|
|
||
| func (s *StellarLowerBoundDetector) DetectLowerBound(ctx context.Context) ([]protocol.LowerBoundData, error) { | ||
| health, err := s.fetchHealth(ctx) |
There was a problem hiding this comment.
Pls remove fallbacks, local bound, etc. Just use a simple method with retries
There was a problem hiding this comment.
Done - single getHealth call with retries (stellar-rpc publishes its retention boundary as oldestLedger), fallback and cached bound removed; on error the processor keeps the last published bound.
| internalTimeout time.Duration | ||
| } | ||
|
|
||
| func NewStellarHealthValidator( |
There was a problem hiding this comment.
Done - renamed to StellarSyncingValidator.
| } | ||
| if root.NetworkPassphrase == "" { | ||
| log.Error().Err(errStellarHorizonEmptyPassphrase).Msgf("failed to validate the chain of horizon upstream '%s'", s.upstreamId) | ||
| return validations.SettingsError |
There was a problem hiding this comment.
Done - FatalSettingError.
| "github.com/rs/zerolog/log" | ||
| ) | ||
|
|
||
| type StellarHorizonHealthValidator struct { |
There was a problem hiding this comment.
Done - renamed to StellarHorizonSyncingValidator.
| // body is not the health document. | ||
| var health StellarHorizonHealth | ||
| if err := sonic.Unmarshal(response.ResponseResult(), &health); err != nil { | ||
| if response.HasError() { |
There was a problem hiding this comment.
Didn't get it. It should be before unmarshalling
There was a problem hiding this comment.
Reordered - the error check is first now, with the 503-carries-body case as an explicit branch inside it: Horizon answers /health with HTTP 503 while its captive core is syncing, but the body still carries the health booleans, so we parse it to distinguish Syncing from down.
| "type": "plain" | ||
| }, | ||
| "methods": [ | ||
| { "name": "GET#/", "params": [], "settings": { "cacheable": false } }, |
There was a problem hiding this comment.
Done - both stellar specs reformatted, one field per line.
…le lower bounds - StellarChainSpecificObject split into StellarRpcChainSpecificObject and StellarHorizonChainSpecificObject with a shared embedded base; the factory picks the object from the primary connector type (rest => horizon) - CapDetectors returns nil: stellar-rpc has no ws, horizon streams over SSE - both objects wire BaseBlockProcessor polling the finalized head (== latest, SCP closes ledgers with immediate finality; safe detection disabled) - both lower bound detectors simplified: one call (getHealth oldestLedger / root history_elder_ledger) with retries, no fallback/cached-bound logic; horizon keeps AllowsBoundDecrease (db reingest range backfill moves the elder ledger down) - health validators renamed to StellarSyncingValidator and StellarHorizonSyncingValidator - empty network passphrase is FatalSettingError on both APIs - horizon /health fetch checks the response error before unmarshalling, with the 503-carries-body case as an explicit branch - unused protocolVersion field dropped from the getNetwork parse - both specs reformatted as plain json, one field per line - tests for the split objects, bounds and validators
|
Done in #336 |
Re-lands #287 onto main: the original PR was stacked on the ton branch (#286), so its merge target was wrong. Same diff, applied onto main with the ripple/ton-only context stripped.
Note: includes the shared
DecreasingBoundDetectorcapability inlower_bounds(the horizon lower bound depends on it); the same change ships in #284, whichever merges second resolves trivially.Replaces #287.