Release v0.19.0 - xchain-decoder - #20
Merged
Merged
Conversation
Re-vendors the shared coin bundles with the bridge escrow addresses and the XBRIDGE gas entry.
Refreshes the lockfile so every package the security audit flags resolves to its patched release inside the existing version ranges. No source change.
Mixed-carrier envelope recognition is now height-gated by an activation map, clearing a reorg halt refuses when a newer halt has landed, dispenser cancel grace anchors on the expiry mark's header time, and the purge-safety probe also covers batch sub-commands. A first-seen source pubkey is carried through transaction insertion, the shutdown timer is injectable, and the fuzz invariants read the dispenser field offsets from the decoder itself instead of restating them.
Two comments pointed at the indexer's dispenser divergence metrics module by its old file name. They now name the snake_case file the indexer renamed it to.
A restructure that renames test files has to prove it changed nothing about what runs, and a passing count proves nothing, so the pin is the set of full test titles per file for the test:unit tier: 84 files, 1622 titles. The identity pin is the other half, sha256 of the five vendored coin files and the two conformance fixtures whose canonicals live in other repos, so drift in a copy this repo may not edit fails here instead of in a consumer's CI. The reachability and reference-map tools come across from the indexer, with the repo name they hardcoded now taken from the checkout directory and the dynamic-require table emptied: every require under this repo's src/ is a literal. Suite wall time recorded at 21.4 s, which keeps the tier per-step.
One source file is reachable from no require in the tree, src/bufferutils.js, and a platform-wide text sweep across every checkout proves it live: the Dockerfile copies it over bitcoinjs-lib at image build time to replace a 64-bit reader that throws above 2^53-1, which a single Dogecoin output exceeds. Deleting it would have wedged block decode in every container with nothing failing here, so the sweep output is committed instead of a deletion.
The style rules had a pre-push gate but no editor-level check, so a writer only learned about a naming or require-placement violation at push time. The preset is vendored by copy rather than imported, because a public clone of this repo has no platform tree beside it to import from. Two local additions over the master copy: the two vendored trees are ignored, since they are refreshed from another repo and an edit here would be drift; and src/clear-reorg-halt.js joins the entry-point list beside the api and the migrator, because its output is its product. It reports 535 findings under src/ today; a later cleanup drains those rather than suppressing them.
Six environment names were read at seven sites across the connector and the database layer, so nothing could say what this service is configured by without reading every file, and two sites reading one name could disagree on its fallback without anyone noticing. The home exports accessors rather than a snapshot, which is the whole of the design: several of these knobs are documented and tested as retunable without rebuilding the object that reads them, and a require-time copy looks identical while silently freezing that. Five reads stay where they are, four because they coerce at the read site and the coerced type is a decision rather than a move, one because it is a bare process.env reference with no name to move.
… home Twelve source files carried three naming schemes between them, and seven protocol-rule modules sat loose at the top of src/ beside the infrastructure, which is the one thing the top level is for. The rule modules move into the existing src/protocol/ home; the rest are renames. Forced by the moves, in this commit because they break otherwise: require and path.join references across src and test, a regex in the chain-identity gate test that matched the old require specifier through escaped characters, two path.join segment lists that name a source file by parts, and four comments naming a moved path. Two body requires hoisted in src; the codemod also hoisted 56 in test files, and those were put back: the rule is scoped to src, and a require inside an it() block is frequently the assertion itself. One declared test-title change, and no other: a title naming chainIdentity.js now names chain_identity.js. Same 84 files, same 1622 titles otherwise. Not moved, each for a reason: bufferutils.js and its in-process twin, which the image build copies over a dependency by literal path; the decoder metrics, because the observability directory is vendored and grading stops at its prefix; the connector and the network table, which three sibling repos hold at the same depth and one cross-repo test resolves from a single path template; and the reorg-halt clear, which xchain-node runs by its literal path inside this repo's container, so it keeps its hyphenated name at src/ rather than following the snake_case sweep.
A leading underscore is neither privacy nor a plain name, and every one of these is called from outside the class it lives in, including from the startup assertion registry, so the prefix said the opposite of what was true. Two populations the rename tool leaves alone on purpose, both rewritten here because they are reflective calls rather than prose: the startup-assertion registry maps each migration file to its assertion method BY NAME as a string and invokes it through that string, and the suite stubs five of these methods by name through sinon, which refuses to stub a property that no longer exists. Fifty-one such occurrences across seven files. Forty-two test titles change, all of them the same underscore dropped from a method name inside the title. No suite is added or removed: same 84 files.
…suite The tree carried four schemes at once: camelCase, kebab-case, a dotted kind infix, and a kind suffix that is not .test.js at all. Every suite is now <unit>.test.js in snake_case, a kind infix is dropped where the directory already says it (security, boundary, regression, unit) and becomes a word where it does not (extra, queries), and the fuzz harnesses keep .fuzz.js because the fuzz glob keys on it. The 52 files that are not suites move into a support/ directory inside their own kind: each kind's setup and helpers, the fuzz mutators, the benchmark harness with its mocks and scenarios. The batch-limit generator was never a test at all and moves to bin/ beside the other operator tools. Rewritten in this commit because a rename without them collects zero files and reads green forever: five kind globs, every --require path across 20 npm scripts, the tier manifest the enforcement suite reads, the two docker wrappers, the mutation configs, the workflow, and the benchmark scenario names in both the harness list and the stored baseline. Also three sibling pins that now name the tracker's renamed files: without them the AuxPoW parity and safe-depth suites do not fail, they SKIP, which is nine assertions going quiet. Same 84 unit files through the rename map, same titles, no suite added or lost.
The 150 raw console calls outside the three process entry points now go through the shared logger. A call with one argument passes it straight through; a call with two or more is folded through node's formatter under a name that cannot collide with this repo's own util module, which is what the vendored console patch already does at runtime, so a folded line renders exactly what it rendered before. The two files that exported a class and then attached named helpers to it line by line now attach them in one Object.assign. module.exports IS the class in both, so this is the same assignment written once, and no call site in this repo or any consumer changes. Two requires move out of function bodies: the coin registry, which the startup pin reads, and dotenv, whose CALL stays inside main because the environment must be read at run time. One source-text assertion and one halt-diagnostics assertion follow: the halt line now reaches the logger the suite already installs rather than console.error, so the test reads the sink. Driven: with the message text changed, that assertion goes red, and back to green when it is restored.
Two sweeps cut comment lines out of this repo, 21 lines across 2 files in June and 630 across 101 files in August, and nothing had been restored. The documentation layer is a product requirement here: the owner wrote these comments so a reader who does not program can follow a file top to bottom, and a suite cannot notice their absence because a comment cannot fail a test. 260 comment runs come back, each anchored to the line of code it was written about rather than to a line number, so a run lands above its own statement wherever the restructure moved it. Not a revert: the same sweep also rewrote surviving lines while scrubbing internal references, and reverting a file would duplicate the rewritten text and put back a reference the push gate refuses. Comment-only, and proven so rather than asserted: with comments and blank lines stripped, all 34 files are byte-identical to their committed versions.
…ool out Top-level src/ is for cross-cutting infrastructure, and three files were not that. The three modules that talk to or parse a coin node (the RPC connector, the network-parameter table and the block decoder) become src/chain/, which is the feature they share. The reorg-halt clear stays at src/clear-reorg-halt.js despite being an operator command rather than service code: xchain-node execs it inside the decoder container by that literal path, and the Dockerfile copies only ./src, so joining the other one-off tools in bin/ would leave the container image without it and break every `xchain-node clear-reorg-halt` call. Two sibling pins move with them, and both were failing OPEN rather than loud: the encoder parity suites resolve that repo's validator, which moved into a directory of its own there, so six assertions had gone quiet as skips. Top-level source files fall from 14 to 11. What stays is named with a reason: the api, the main class, the database and the config home are the declared layout; util.js is the style doc's own top-level example; the migrator is an entry point the shared lint preset pins by that path; the reorg-halt clear is named by path from outside this repo; the metrics module cannot join the vendored observability tree, which is excluded from grading by prefix and would hide it; and the bufferutils pair is copied over a dependency by a literal path in the image build, which this change does not touch.
…ss exemplary The removal sweep did two things, and a line count only sees one. It deleted comment runs, which a restore pass puts back and a coverage floor proves. It also REWROTE runs it kept, scrubbing internal references, and some of those rewrites took the explanation along with the reference: the file ends up with a comment in the same place, shorter, saying less, and nothing notices. bin/comment-run-pairs.js finds those. It pairs each before-and-after comment run by token overlap, at a floor of 0.3, rather than by line, because rewrapping a paragraph changes every line in it and line pairing reports a pure rewrap as a total loss. 1370 runs paired across this repo's sweep, 134 lost words, and reading every one of those shows the large majority are the scrub working: the reference went and the sentence around it stayed, often tighter than before. Two in the main class were real losses and are merged back without the reference: the worked example showing an 8192-byte payload stored as 8197 bytes after alias expansion, and the field-by-field account of what the canonicalizer returns. The main class is also the file a human reads first for this service, so the validation checks in it now each carry a plain-language line: the whole envelope-grammar walk, the stall gates, the size cap, the unknown-action rule, the one-carrier rule and the source-attribution rule. Comment-only, proven by stripping comments and blank lines and comparing bytes.
The rename of the in-process bufferutils patch left this line naming a file that no longer exists. The COPY under it is unchanged and still names src/bufferutils.js, which did not move: the image build pins that path literally, which is why it stayed at the top of src/.
xchain-decoder named xchain-encoder/src/validator.js and xchain-utxo-tracker/src/BlockchainConnector.js and undo-blocks.js in comments and computed sibling-path literals; those files moved during a cross-repo directory reorganization. Forced by xchain-encoder and xchain-utxo-tracker.
Comments, docs and message strings still named indexer files by paths that its feature-directory layout and snake_case renames retired. Each mention now names the file that holds that code, and no executable line changes.
The canonical coin registry repointed a consensus note at the decoder's renamed crypto networks module. This re-vendors that byte change so the copy stays identical to canonical.
bin/suite-title-map.js now reads a structured {paths, titles} rename map as well as a flat one, and bin/pins/suite-title-renames.json declares the 80 unit test files and 45 titles the layout pass renamed, so the compare against bin/pins/at1-suite-titles.json holds with zero undeclared differences.
… file
CODE-STYLE requires require() at the top of a file, not inside a function
body, so a computed or side-effecting import is the only kind allowed to
stay inline. The Database require carried no such reason: db.js only
defines the class, and hoisting it changes nothing about when a connection
opens. require('dotenv').config() stays in main(), since it is not a plain
`const X = require(path)` statement the layout codemod can safely hoist.
…checks COMMENT-STYLE.md requires a plain-language line above every validation check, and dbVerified/tablesVerified had none even though the surrounding boot sequence is otherwise well commented. State in ordinary words what each check protects against, matching the neighbouring stanzas' style. Comments only; no code line changed.
…ugh config.js The five remaining environment reads outside src/config.js now take their raw value from it. Coercion, defaults and the injectable env parameter of resolveTimeoutMs stay at each read site, so every value is unchanged.
dotenv now loads before db.js, as it did before the db.js require was hoisted, and only when the file runs as the process, so a test requiring it for its exports still sees no .env.
Only the chain block decoder applies the BigInt-safe reader, so the patch and the file the Dockerfile copies over bitcoinjs-lib now live beside it, as they do in xchain-utxo-tracker. The Dockerfile COPY source moves with it.
…_halt.js The operator script takes the snake_case file name. The npm script keeps its clear-reorg-halt name and xchain-node execs the new path in the same change.
An installed xchain-node execs `node src/clear-reorg-halt.js` inside the decoder container. The file at that path now runs src/clear_reorg_halt.js as the main module when executed, so the tool loads the service .env and exits exactly as before, and re-exports the same module when required. It is deleted once the fleet runs an xchain-node that execs the new path.
… config read note plainly The config home added one require line to XChainDecoder.js; one of three consecutive blank lines goes, so the file does not grow. The note in config.js now says what a raw read returns without a narrative marker.
…tle blocks by behaviour (cherry picked from commit 84f02f024ca4517ae20ea119a048805c56cd0110)
…n suites into same-title blocks by behaviour (cherry picked from commit eb2fa4cdc5addd9031e31c81772abd8923cc3420)
A dry run only reads state and never calls clearReorgHalt, so gating it on the same 8-character --reason check as a real clear made the read-only path fail for no reason. The reason validation now runs only when a real clear will happen, and the dry-run message reports the checks either way, printing a hint to pass --reason instead of the reason text when none was given.
…nd /live route into a part beside the entry src/api.js keeps its require path and every export; the moved units live in src/api/probe_routes.js byte for byte, with the /live handler's probe state and response split into two helpers under the function limit. The source text scans that count the three health payload sites now read the entry and the part together, and the /live scan reads the part.
src/db.js keeps its require path, the constructor and the exported class; every other method moves byte for byte into a part under src/db/ installed onto Database.prototype, and the checksum rebaselines and migration preconditions attach as side-effect parts. The oversized table verification, block delete, transaction and dispenser inserts, mempool reconcile, migration runner, expiration type check and destructive statement check split into helpers under the function limit. The security source scans read the entry and the parts it requires.
… the entry src/api.js keeps its require path, its exports and every route in the same registration order. The start-failure and process crash records move to src/api/crash_reporting.js, the health method's DB and halt probes to src/api/health_probe.js, and the metrics and log shim wiring to src/api/observability_wiring.js, byte for byte. The entry keeps the health and /status payloads, getmempool and the middleware in named functions under the length limit, reading the running flag and start error through getters. Two source scans follow the moved text into its part, and three comments that pointed at moved text now name its new home.
…spenser fee and mempool methods into parts beside the class The decoder constants and payload helpers move to their own modules, and six method groups install onto the class prototype from src/XChainDecoder/. The entry path, its exported names and the prototype method set are unchanged. getSourceFromOutput, detectEnvelopeWitness and updateMempool are split into in-file helpers as they move. Source-reading tests follow the constant and mempool code to the parts that hold it.
…tializers Identity and connector wiring, parse progress, fetch mode, reorg counters and the halt park state each get an initializer the constructor calls in the same order, so every field is assigned exactly as before.
…side the class verifyReorg moves to src/XChainDecoder/reorg_verification.js and the haltReorg closure to src/XChainDecoder/reorg_halt.js, with every statement and comment moved verbatim. The walk, the above-tip and fork deletes, the tip refresh and the prior-depth read become helpers called with .call(this, ...). Each helper that awaits suspends on the same leaf await the method did before it can return, and the refusal and record steps stay synchronous. The two source-reading tests follow the code: the reorg counter guard reads the part, and the chain-tier gate reads the entry with its parts.
parseTransaction moves to src/XChainDecoder/transaction_parsing.js, and its per-output carrier reads and payload decompile to src/XChainDecoder/carrier_extraction.js, with every statement and comment moved verbatim. The steps are generators driven by one loop in parseTransaction, so each node or database wait is still awaited exactly once, in parseTransaction itself, and a transaction with no carrier still resolves without suspending. A step spells its waits `yield` where the method spelled them `await`.
…e class start moves to src/XChainDecoder/startup.js, and the steps of its block loop to sync_loop.js, tip_refresh.js, block_ingest.js, transaction_ingest.js and dispenser_registration.js beside it, with every statement and comment moved verbatim. The loop-carried cursors, counters and latches live on one object the steps share. A step that ended the iteration returns 'continue', and one that rolled the block back returns 'rollback', so the loop runs resetAfterRollback after every write the step made, as before. Each step that awaits suspends on the same node or database wait the loop did before it can return, and every other step stays synchronous. The consensus pin check stays the first statement of start, so its refusal is still thrown before anything is awaited. The source-reading tests follow the code into the parts. The chain-identity require check accepts the parts' ../ path.
…penser/index.js The indexer M3 move split src/actions/dispenser.js into src/actions/dispenser/, with this.formats now in index.js, so the sibling guard read "sibling not found" under XCHAIN_REQUIRE_SIBLINGS=1 on CI (run 35021438054). The pinned offsets are unchanged; only the cited path moved.
…parts, not the loader alone The indexer registry push made src/protocol_changes.js a loader over src/protocol_changes/*.js, so the FIX_OUTPUT_FANOUT and BATCH_* conformance guards found no row in the single file and run 35053183286 read four failures. Both guards now read the loader plus every part, accept the row in its addChange(...) call form and its registry tuple form, and resolve the mainnet arm's numeric const from the same corpus; under XCHAIN_REQUIRE_SIBLINGS=1 a loader with no parts directory is an error, never a skip.
Version 0.19.0, the 0.19.0 changelog section and the README badges and script-table counts measured on Linux.
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.
Bumps the package and lockfile to 0.19.0 and folds the Unreleased block into a dated 0.19.0 entry. The train carries the XBRIDGE cross-chain bridge, armed on testnet by the ladder and on mainnet at the sentinel, with the token bridge and policy code dark behind their sentinels, the rollcall and tracker fixes, the code-structure passes with the consensus identity pinned, and the activation registry.
Platform notes ride xchain-node's Release.