Shekyl Stats

Refreshing...

Network

Connected
Seed Nodes Active
--

Chain

Current Block
0
Network Height
—
Top Block Hash
--
Block Time Target
2 min

Rewards

Last Block Reward
0.000000 SKL
Difficulty
0
Estimated Hash Rate
0 H/s

Supply

Circulating Supply
--
Remaining Supply
--
Total Burned
0 SKL

Economics

Release Multiplier
0
Burn Rate
0%

Staking

Stake Ratio
0
Staker Pool
0.000000 SKL
Staker Emission Share
0%
Total Staked
N/A
Staking Height
N/A
Tier 0 Lock Blocks
N/A
Tier 1 Lock Blocks
N/A
Tier 2 Lock Blocks
N/A

Protocol

Transaction Format
TransactionV3
Membership Proof
FCMP++
Spend Auth
Ed25519 + ML-DSA-65
Confidentiality
Stealth + BP+

Node

TX Pool Size
0
Database Size
0 B
Node Version
--
Sync Status
Syncing

Development digest — 1 October 2026

Started by Shekyl Foundation • 0 replies • 18 views

0

Shekyl FoundationAutomated

10/1/2026, 8:51:06 PM

State of dev as of a5798fc199ef, 1 October 2026. This window settled how the archival record is divided, and the daemon's networking moved onto the new Rust transport. The node also lost a good deal of inherited code. On the wallet side, two smaller pieces of work landed. Both are about the wallet saying exactly what happened.

Archival shards are measured in bytes, not transactions — RULED

A pruned Shekyl node keeps the chain's skeleton and throws away the bulky parts of each transaction: the proofs and the post-quantum signatures. Archival nodes keep those parts and are paid to do so. The archive is divided into shards, and a shard is the unit an archival operator commits to holding and can be challenged on. So every node, pruned or not, has to agree exactly on where one shard ends and the next begins.

Until now a shard was a fixed number of transactions. That count was sized to match a storage unit the project had already retired, so nothing about the archive's own obligation justified it. A fixed count is also not a fixed cost. The data an archive must keep for a transaction varies roughly five-fold with its shape: how many inputs it has, whether those inputs are multisig, how deep the membership tree was when it was built. Our staking simulation found what that predicts. A stretch of chain dominated by heavy transactions makes heavy shards, and those end up under-held.

We considered three alternatives and did not adopt them:

  • Counting inputs instead of transactions narrows the spread, but it reverses which periods count as heavy, because a proof's fixed cost is spread over more inputs. It also still misses multisig and tree depth.
  • Computing each transaction's size from the skeleton fails because the skeleton cannot tell a single-signature input from a multisig one. That information lives only in the data a pruned node discards. Recording it permanently in the skeleton would make every input's multisig shape public forever, and privacy is not something we trade for bookkeeping.
  • Having the sender declare the length in the transaction would mean every signer knows the final length of every signature before signing. Today's schemes allow that. A future post-quantum scheme with variable-length signatures would not.

The ruling: a shard is a fixed number of archival bytes, and each transaction's archival length is folded into its transaction ID. Nobody declares the length and nothing new goes on the wire. A full node measures the length from the bytes it already holds while computing the ID. A pruned node stores the length beside the hashes it already keeps. An earlier design rejected byte-based boundaries for a good reason: a peer serving pruned data could state any length, and nothing in the chain would catch it. Binding the length into the ID answers that objection. A wrong length produces a wrong ID and fails the block's Merkle check.

The Rust chain store now cuts shards by archival length. Its tests cover pruned and unpruned nodes placing every boundary identically. Folding the length into the transaction ID is not built yet. It is waiting on one open question: how pruned transports should carry the length. The size of a shard is still provisional.

The daemon's networking moves to the Rust transport — LANDED

Every peer connection a Shekyl daemon made used to go through a TCP server inherited from the Monero codebase. This window it was replaced by a Rust transport layer, and the old server was deleted. The deletion covered the server itself, its throttling, its SOCKS dialer, and the tests that existed only to drive it. The removal ran to more than 8,000 lines.

Replacing the code every connection runs through is risky, so we built a way to show the swap was safe first. We wrote a differential harness that drives both stacks with the same scripted peer and compares what comes back, byte for byte, along with whether the session was established. The intended differences between the two stacks were written down in advance, each by name. One example is that the new transport refuses to queue unbounded outgoing data. Any other difference counted as a finding. The old stack's transcripts are now kept as fixed reference recordings. They can change only by a recorded decision, and CI checks the new transport against them.

The I2P address type went with the old server. The daemon never shipped an I2P connector. It had an address type, a parser and a proxy path, and operator documentation pointing at that path. Tor is the anonymity network the node supports, and the documentation now says so. Adding I2P later means building a real connector and bringing the address type back along with it. That stays on record as possible future work.

With the cutover the transport carries the same bytes as before, so an upgraded node and an older build still talk to each other. The transport also has an optional encrypted mode for clear-internet connections. It stays off. Turning it on is a separate step that comes after it has been tested on this transport.

The wallet refuses an unsafe rollback and says how to recover — LANDED

When the chain reorganises, a wallet rolls its local copy of the membership tree back to the fork point and replays from there. Below a certain depth, parts of that tree are frozen: they are sealed, and other parts of the system may already have relied on them. A rollback that deep would delete sealed records.

The wallet now refuses any rollback deeper than its finality window. The boundary is exact. Keeping the oldest height inside the window drops nothing sealed, and one block further is the first refusal. The wallet also refuses when it cannot confirm a common ancestor inside the window. A shallower disagreement is still handled by the normal rollback.

A refusal like that is only useful if the person who hits it can act on it, so the error carries the remedy. A wallet RPC client gets a dedicated "re-sync required" code. The message gives the rollback depth, the window, and the exact steps to rebuild the tree. If the refusal happens during a rescan, the message also says that the wallet history has already been cleared. Without that, a user would just run the rescan again, and the rescan alone cannot rebuild the tree.

A payment now answers the request it was made for — LANDED

A Shekyl payment link can carry a request identifier, so a merchant or a person can tell which invoice a payment settles. The receiving wallet already knew how to match that identifier. But no sending wallet put it into the payment, so every payment made from a link arrived without its request.

The sender now writes the identifier into the payment output's encrypted label. Only the recipient can read that label, and every output carries one whether or not it holds an identifier, so the transaction looks the same to an observer either way. A payment between two wallets that pass the identifier through is now matched to its request when it arrives. If an identifier cannot fit in the label, the wallet refuses to build the transaction. It does not silently drop the identifier the payer asked to send. Recording the identifier in the sender's own history needs a storage format change, which is still to come.

A constant that was only right by coincidence — RULED

When an archival operator misses its obligations, the protocol allows a grace period before the penalty applies. That grace was written as a fixed block count. In production it happened to equal one settlement epoch.

It was always meant to be one epoch. Two separate design rounds, months apart, both described it that way. As a raw number, though, it could drift away from the epoch: a later change to the epoch length would have left it behind. That already happened in test networks, which run shorter epochs. There the grace stayed at its production block count, so every penalty the test networks exercised ran under a relationship production never has.

The Foundation ratified the meaning rather than the number. The grace is now defined as one settlement epoch, and every dependent rule is written in epochs. The separate block-count parameter is deleted, along with an export that nothing called. Production behaviour does not change. The change is that test networks now exercise the same relationship as production. If the ratio ever needs to change, the multiplier is the only thing to edit.


This post describes the state of the dev branch at a5798fc199ef on 1 October 2026. Shekyl is pre-genesis and under active design; anything marked IN FLIGHT may change or be abandoned. Where a later post contradicts this one, the later post is correct.

References: PR #897, PR #906, PR #910, PR #919 (SHT-Q2); PR #900, PR #909 (P2P cutover, D13/D14); PR #918, PR #920 (CT-6 C7); PR #901; PR #921 (DRS-E4 slash grace)

Sign in to reply and vote.