Commit 665deed
feat(zebrad): add zcashd-compat mode with a supervised zcashd wallet sidecar (#10952)
* feat(network): always gossip blocks to configured zcashd-compat sidecar peers
Adds block_gossip_peer_ips plumbing so inbound peers from configured IPs:
- always receive AdvertiseBlock inventory broadcasts,
- get one reserved inbound connection slot,
- bypass the recent-IP reconnection throttle,
- are exempt from the find-response stall detector.
Adds init_with_block_gossip_peer_ips to keep the existing init API stable.
Ported from Zakura (zakura-core/zakura) commits ad1a89b341 and 4036e47114.
* feat(zebrad): add zcashd-compat mode with supervised zcashd sidecar
Adds a [zcashd_compat] config section and --zcashd-compat start flag.
When enabled, Zebra pins block gossip to the sidecar's inbound peer IPs
and optionally spawns and supervises a hard-locked zcashd wallet sidecar:
- config: enabled/manage_zcashd/zcashd_source/zcashd_path/datadir/extra args
- preflight: Linux hardware and disk checks with --unsafe-low-specs override
- datadir: bootstrap zcashd datadir and a minimal zcash.conf on first start
- managed: SHA256-pinned sidecar download from the embedded release manifest
- supervisor: spawn zcashd pinned to Zebra's P2P listener, restart with
capped backoff, and SIGTERM with a grace period on shutdown
Ported from Zakura (zakura-core/zakura), adapted to Zebra's start command
and config conventions.
* feat(zcashd-compat): add installer, make targets, sync-check, and Docker compat image
- scripts/install-zebra.sh: interactive installer with split-binary,
supervised, Docker, and build-from-source zcashd-compat modes; downloads
SHA256-pinned zebrad release and valargroup/zcashd sidecar binaries with
no build dependencies required
- make/zcashd-compat.mk + root Makefile: start/status/sync targets
- deploy/zcashd-compat/sync-check.sh: peer-pinning and height-drift checks
- docker: runtime-zcashd-compat image stage and ZCASHD_COMPAT_ENABLED
entrypoint opt-in
Ported from Zakura (zakura-core/zakura), adapted to Zebra release artifact
naming, zfnd Docker images, and Zebra's config keys.
* docs(zcashd-compat): add zcashd-compat book chapter and changelog entry
* test(zcashd-compat): add regtest integration test suite for the zcashd sidecar
Ports Zakura's zcashd-compat integration harness and 26 tests covering
startup, chain agreement, wallet RPCs, transparent tx flow, supervisor
resilience, and reorg handling (depths 1-80, restart cycles, churn soak).
Tests are #[ignore]d and gated on TEST_ZCASHD_COMPAT=1 with a zcashd
binary from TEST_ZCASHD_PATH or the embedded manifest. Run with:
cargo nextest run --profile zcashd-compat-integration --run-ignored=only
* fix(zcashd-compat): reject zebrad binaries without zcashd-compat support in the installer
The installer downloaded the pinned zebrad release for zcashd-compat
profiles, but no Zebra release includes zcashd-compat mode yet, so the
printed start commands failed at startup with 'unknown field zcashd_compat'.
Check 'zebrad start --help' for the --zcashd-compat flag after resolving the
binary, and fail with guidance to build from a zcashd-compat branch and
re-run with --zebrad-path --no-download.
* fix(network): queue block gossip for busy sidecars and canonicalize ban checks
- A configured zcashd-compat sidecar that is busy with another request when a
block is advertised no longer misses the advert: the latest advert is queued
per sidecar and delivered as soon as the peer is ready again, driven by a
detached task so the connection does not cancel it.
- Inbound ban checks now also match the canonical IP, so an IPv4-mapped IPv6
connection cannot dodge an IPv4 ban and then claim the zcashd-compat
reserved slot and its privileges.
- Documents the new init variant in the zebra-network changelog.
* fix(zcashd-compat): terminate the sidecar on signal shutdown, re-verify cached binaries, and skip unknown-datadir disk checks
- On SIGINT/SIGTERM the runtime drops the start future without running the
supervisor's shutdown path, orphaning the supervised zcashd. Track the
child pid and terminate it synchronously after runtime shutdown
(SIGTERM, grace period, SIGKILL last resort, reaping via waitpid).
- The managed zcashd cache now records and re-verifies the extracted
binary's own SHA256 on every reuse, so a modified or corrupted cached
executable is re-downloaded instead of executed.
- Preflight no longer requires disk capacity for a zcashd datadir it
invented: in externally managed mode without an explicit zcashd_datadir,
the sidecar's disk usage is unknown to Zebra and is not checked.
- Renames supervisor metrics to the dot-separated zcashd.compat.supervisor.*
convention, documents block_gossip_peer_ips as a trusted-peer privilege
boundary, and fixes the manifest consumer docs.
* test(zcashd-compat): fix harness leaks and timeouts, add graceful-shutdown coverage and a scheduled CI workflow
- Construct the test setup before the readiness waits so its Drop guard kills
the supervised zcashd when setup fails, instead of leaking it.
- Treat empty TEST_ZCASHD_COOKIE_FILE/RPC_USER env values as unset (the Make
targets export them unconditionally).
- Add per-request timeouts to the zcashd RPC test client so polling limits
are actually enforced.
- Rename zebrad_clean_shutdown to zebrad_abrupt_kill (it SIGKILLs zebrad),
and add zebrad_graceful_shutdown_stops_zcashd, which SIGTERMs zebrad and
asserts the supervised zcashd also exits.
- Correct the suite README: opt-in CI status, real test names and assertions,
and the actual entry-point file.
- Add a manual + weekly-scheduled zcashd-compat regtest workflow that runs
the suite against the manifest-pinned sidecar build.
* fix(zcashd-compat): make supervised targets enable supervision, bound sync-check RPC calls, and clarify docs
- The compat-zebrad-start-supervised targets now set MANAGE_ZCASHD=true;
they previously started zebrad in externally managed mode.
- sync-check.sh curl calls get connect/total timeouts so a stalled RPC
cannot defeat the check deadline.
- The installer explains that zcashd-compat Docker images are not published
yet and points at make compat-docker-build.
- The book marks the embedded third-party sidecar download as experimental
pending a provenance decision, and fixes a garbled sentence.
* fix(zcashd-compat): deep-review fixes for slot lockout, shutdown paths, and tooling checks
Correctness:
- Sidecar-listed IPs now reserve one slot per configured IP and fall back to
the public pool when reserved slots are taken, so an unrelated local process
or a fast sidecar reconnect can no longer lock the real sidecar out.
- The abandoned-zcashd cleanup now also runs on error exits (moved into
RuntimeRun::run, since APPLICATION.shutdown never returns), only forgets the
child pid once its exit is confirmed, and checks waitpid before signalling
so a recycled pid is never SIGTERMed.
- The queued sidecar block advert now skips peers banned while it was queued,
matching broadcast_all_queued.
- p2p_connect_addr substitutes the loopback of the listener's own IP family,
so an IPv6-only listener no longer sends the sidecar to 127.0.0.1.
- Preflight runs in spawn_blocking, and checks the cached sidecar's provenance
marker instead of re-hashing the whole binary a second time at startup.
- Child log sanitizing also routes non-ASCII bytes to the slow path, so C1
controls like single-byte CSI cannot reach the terminal.
- Archive/binary digest comparisons are ASCII-case-insensitive; pid casts use
i32::try_from instead of .
- Bare 'make' prints help instead of downloading the sidecar archive; the
compat status pgrep checks no longer match their own shell wrapper; the
make drift check fails on missing heights instead of passing on empty vars;
the installer requires a numeric port in --zebra-p2p-addr.
- Test teardown kills the sidecar before the fallible zebrad asserts, so an
early return cannot leak it.
Cleanups from the same review: sidecar identity checks unified on the O(1)
tracked key set (route_p2c, gossip selection), hex::encode for digests,
struct-update syntax in the test builder, and cfg-gated dead_code allows.
* fix(zcashd-compat): sweep-review fixes for arg precedence, installer commands, and download policy
- Duplicate -datadir=/-conf= values in zcashd_extra_args now resolve to the
LAST occurrence, matching zcashd's own single-valued option semantics, so
bootstrap/preflight prepare the datadir zcashd actually uses.
- Warn at startup when zcashd_compat.p2p_connect_addr is non-loopback but
block_gossip_peer_ips is the loopback default: in cross-container
topologies the sidecar's source IP is not loopback and it would silently
lose every sidecar privilege. The book example now shows a real
non-loopback source IP.
- The installer's docker-supervised command uses the compat image's
ZCASHD_COMPAT_ENABLED entrypoint opt-in again (it printed a command that
failed at startup with 'zcashd_source=path requires zcashd_path'), and
keeps the sidecar wallet RPC on loopback instead of exposing it on every
host interface under --network host.
- The default-profile native and build-from-source commands now pass the
selected network instead of silently starting Mainnet.
- The managed download's redirect policy enforces HTTPS on every hop, as its
security contract documents, not just on the final URL.
- Docs: config and zebra-network changelog describe the real reserved-slot
pool (one slot per listed IP, public-pool fallback with normal limits).
* feat(rpc): add regtest generatetoaddress and a zcashd wallet-conformance harness
Adds a regtest-only generatetoaddress RPC that mines blocks paying the
coinbase to a caller-specified address (reusing the generate path via a
per-call miner-params override on a cloned block-template handler, leaving
the configured mining.miner_address untouched).
Adds zebrad/tests/zcashd-wallet-conformance/, a Python harness that runs
zcashd wallet/RPC tests against a Zebra + zcashd-sidecar pairing: N sidecars
fan into one Zebra, which is the miner and relay, and node[i].generate()
mines coinbase to node i's own wallet via generatetoaddress. Includes a
single-node and a faithful multi-node wallet port, plus run_upstream.py, an
adapter that runs unmodified upstream tests by monkeypatching the framework's
node-lifecycle and topology primitives.
The Zebra half is validated live (generate and generatetoaddress mine to the
correct address on regtest); end-to-end runs against the third-party sidecar
binary are documented as pending a capable environment.
* chore(zcashd-compat): pull the sidecar zcashd from ZcashFoundation/zcashd
Points the embedded manifest, installer, and docs at the new
ZcashFoundation/zcashd zebra-compat-v1.0.0 release (forked from
valargroup/zcashd) instead of valargroup directly. The runtime binary is the
same SHA256-pinned artifact.
Also fixes the wallet-conformance harness sidecar config: bind RPC to loopback
explicitly (the default all-interfaces bind fails in restricted sandboxes) and
drop the allowdeprecated args that this zcashd version rejects.
* fix(network): always answer getheaders with a headers message
A `FindHeaders` request that resolves to no new headers was mapped to
`Response::Nil`, which sends nothing back to the peer. The `getheaders`
protocol requires a `headers` response, so leaving it unanswered keeps the
peer's `getheaders` request pending forever. A zcashd sidecar following
Zebra defers every later inv-triggered `getheaders` behind that stuck
request and never syncs. Always reply with a `headers` message, even when
it is empty.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(state): fix Regtest difficulty and block time for zcashd sidecar sync
Two Regtest mining behaviours prevented a zcashd sidecar from following a
Zebra Regtest chain over P2P:
- Zebra retargeted difficulty on Regtest, while zcashd uses a fixed minimum
difficulty with no retargeting (`fPowNoRetargeting`). The sidecar rejected
Zebra's headers as "bad-diffbits". Pin every Regtest block to the powLimit
in `expected_difficulty_threshold`, which both the block template and
validation use, so mining and validation stay consistent.
- Block time was `now()` clamped to `[median-time-past + 1, median-time-past
+ 90 min]`. A fresh Regtest chain starts from the 2011-era genesis, so real
time is always far ahead and every block pinned to `max_time`, racing chain
time ~90 min per block and outrunning the sidecar's acceptable block-time
window. Advance Regtest block time minimally instead, matching zcashd's
regtest behaviour and keeping mined timestamps tightly clustered.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(zcashd-compat): make the wallet conformance harness pass end-to-end
Get the single-node zcashd wallet RPC conformance run passing against the
real Zebra + zcashd-sidecar pairing:
- Give Zebra's RPC client a generous timeout, since mining many Regtest
blocks in one `generate` call is slow.
- Use `COINBASE_MATURITY + 1` confirmations for spendable-coinbase checks:
`z_getbalanceforaccount` is purely confirmation-gated and does not apply
coinbase maturity itself, so maturity is expressed as a minconf.
- Wait for Zebra (the miner) to accept the wallet tx into its own mempool
before mining the confirming block, and drive zcashd's transaction-relay
trickle by advancing the sidecar's frozen `mocktime`. With the clock
frozen, zcashd's `nNextInvSend` timer never elapses, so it never flushes
queued tx invs to Zebra; real-clock nodes are unaffected. Block sync is
unaffected because block invs are sent unconditionally.
- Poll for balances/confirmations to tolerate wallet-scan lag.
- Read an optional `HARNESS_TRACING_FILTER` env var (deliberately not
`ZEBRA_`-prefixed, which zebrad treats as config-field overrides).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(zcashd-compat): port the multi-node wallet test to the shielded flow
This zcashd build disables the legacy transparent `getnewaddress` and does
not credit transparent coinbase to a unified-address receiver, so rewrite
the multi-node conformance test to use the account / unified-address /
shielded z_* RPCs, mirroring the single-node conformance test. Three
sidecars fan into one Zebra, each mining its own shielded coinbase, and a
shielded z_sendmany transfer propagates node0 -> Zebra -> node2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(zcashd-compat): document the shielded flow, Regtest fixes, and adapter limits
Update the wallet-conformance README and the run_upstream adapter docstring to
match the working harness: the shielded-first wallet flow (accounts / unified
addresses / z_* RPCs), the Regtest behaviours required for a sidecar to follow
Zebra (fixed difficulty, minimal block-time advance, getheaders always
answered, mocktime clock-advance to drive tx relay), the validated end-to-end
PASS status of both conformance tests, and the real config-matching limits of
running unmodified upstream tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(ci): satisfy hadolint, shellcheck, and markdownlint
- docker/Dockerfile: DL3022 is a false positive on the external
`zcashd_compat` named build context (passed via docker build
--build-context, which hadolint cannot model); ignore it inline.
- scripts/install-zebra.sh: remove literal duplicate alternatives in
three case patterns (SC2221/SC2222).
- zcashd-wallet-conformance README: dash list bullets and underscore
emphasis per the repo markdownlint config (MD004/MD049).
* build(docker): bump the pinned curl to the current trixie security release
The runtime image pins curl=8.14.1-2+deb13u3, which was superseded in the
Debian trixie repository by +deb13u4, so apt-get install fails and every
Docker image build errors out.
* build(deps): update yanked spin 0.9.8 to 0.9.9
spin 0.9.8 was yanked from crates.io, failing the cargo-deny advisories
checks. cargo update -p spin moves the lockfile to the 0.9.9 replacement.
* docs(rpc): declare the new required RpcServer method as a breaking change
cargo-semver-checks flags RpcServer::generate_to_address as a major break
(new trait method without a default). Declare it under an Unreleased
'Breaking Changes' heading in the crate changelog, as the pr-gate workflow
requires for PRs with a breaking-change title marker.
* fix(ci): satisfy codespell
An unpinned codespell in the spell-check job gained new dictionary
entries: fix a long-standing 'reclaculated' comment typo and reword
'Implementors' in the new zebra-rpc changelog entry.
* build(docker): bump pinned protobuf-compiler to the trixie point release
Debian superseded protobuf-compiler=3.21.12-11 with 3.21.12-11+deb13u1,
failing apt-get install in the build stage like the earlier curl pin.
* ci(lint): pin a known-good nightly for cargo-udeps
The 2026-07-13 and 2026-07-14 nightlies ICE while building zebra-chain's
tests with -Z binary-dep-depinfo, failing the unused-deps job on every
run. Pin nightly-2026-07-12, verified locally against the same build.
Unpin once a fixed nightly ships.
* test(zcashd-compat): port the Rust wallet and tx-flow tests to the shielded flow
The sidecar zcashd build is shielded-first: it rejects
-allowdeprecated=getnewaddress at startup (crash-looping the supervisor
and failing every test in the suite), disables the legacy transparent
getnewaddress, and does not credit transparent coinbase to an account.
Drop the fatal startup argument, and port address generation and the two
tx-flow tests to the account / unified-address / z_sendmany flow, funding
the wallet by mining coinbase to its Sapling receiver via zebrad's
regtest-only generatetoaddress — matching the Python conformance harness.
All three ported tests pass against a live sidecar.
* chore(zcashd-compat): point the installer at the v6.0.0-zcashd-compat.1 preview release
The installer previously downloaded zebrad from the v6.0.0 release, whose
binaries do not include zcashd-compat support. Pin the preview release tag
and its archive checksum so the binary install modes work end-to-end.
* feat(zcashd-compat): make the runtime-zcashd-compat image self-contained
The stage previously required an externally prepared build context with a
zcashd binary. It now downloads and hash-verifies the ZcashFoundation/zcashd
zebra-compat sidecar release inside the build (zcashd and zcash-cli), so a
plain 'docker build --target runtime-zcashd-compat' produces a complete
supervised-sidecar image. The sidecar release ships x86_64 Linux binaries
only, so the stage is amd64-only. The baked-in release is overridable via
build args, and make compat-docker-build no longer needs a prepare step.
* ci(release): publish the zcashd-compat sidecar image from branch releases
Point the release Docker build at the runtime-zcashd-compat target so a
release from this branch publishes the sidecar-bundled image (revert to
'runtime' before merging to main), and pin the installer's Docker modes to
the published image tag.
* rpc: add default impl for new generate_to_address to avoid major bump
* ci: revert dockerfile_target to 'runtime'
* remove unneeded prop.txt file
* fix build
* update changelog
* address low-hanging high-impact copilot findings
* cargo fmt
* route PushTransaction to zcashd-compat sidecar peers, rename block broadcast helpers to sidecar broadcast
* make chowning datadirs more robust
* point install script to 6.2.0
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Conrado Gouvea <conrado@zfnd.org>1 parent 10f79fa commit 665deed
63 files changed
Lines changed: 13886 additions & 52 deletions
File tree
- .config
- .github/workflows
- book/src
- user
- deploy/zcashd-compat
- docker
- make
- scripts
- zebra-network
- src
- peer_set
- initialize/tests
- set
- tests
- peer
- client
- zebra-rpc
- src
- methods/types
- zebra-state/src/service
- check
- read
- zebrad
- src
- commands
- components
- zcashd_compat
- tests
- common
- configs
- zcashd_compat
- integration
- zcashd-wallet-conformance
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
55 | 69 | | |
56 | 70 | | |
57 | 71 | | |
| |||
103 | 117 | | |
104 | 118 | | |
105 | 119 | | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
8 | 32 | | |
9 | 33 | | |
10 | 34 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1609 | 1609 | | |
1610 | 1610 | | |
1611 | 1611 | | |
| 1612 | + | |
| 1613 | + | |
| 1614 | + | |
| 1615 | + | |
| 1616 | + | |
| 1617 | + | |
| 1618 | + | |
| 1619 | + | |
| 1620 | + | |
| 1621 | + | |
1612 | 1622 | | |
1613 | 1623 | | |
1614 | 1624 | | |
| |||
5466 | 5476 | | |
5467 | 5477 | | |
5468 | 5478 | | |
| 5479 | + | |
| 5480 | + | |
| 5481 | + | |
| 5482 | + | |
| 5483 | + | |
| 5484 | + | |
| 5485 | + | |
| 5486 | + | |
| 5487 | + | |
| 5488 | + | |
| 5489 | + | |
5469 | 5490 | | |
5470 | 5491 | | |
5471 | 5492 | | |
| |||
7027 | 7048 | | |
7028 | 7049 | | |
7029 | 7050 | | |
| 7051 | + | |
| 7052 | + | |
| 7053 | + | |
| 7054 | + | |
| 7055 | + | |
| 7056 | + | |
| 7057 | + | |
| 7058 | + | |
| 7059 | + | |
| 7060 | + | |
7030 | 7061 | | |
7031 | 7062 | | |
7032 | 7063 | | |
| |||
7638 | 7669 | | |
7639 | 7670 | | |
7640 | 7671 | | |
| 7672 | + | |
7641 | 7673 | | |
7642 | 7674 | | |
7643 | 7675 | | |
| |||
7655 | 7687 | | |
7656 | 7688 | | |
7657 | 7689 | | |
| 7690 | + | |
7658 | 7691 | | |
7659 | 7692 | | |
7660 | 7693 | | |
| |||
7667 | 7700 | | |
7668 | 7701 | | |
7669 | 7702 | | |
| 7703 | + | |
7670 | 7704 | | |
7671 | 7705 | | |
7672 | 7706 | | |
7673 | 7707 | | |
| 7708 | + | |
7674 | 7709 | | |
7675 | 7710 | | |
| 7711 | + | |
7676 | 7712 | | |
7677 | 7713 | | |
7678 | 7714 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
72 | | - | |
| 72 | + | |
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
| |||
0 commit comments