Ephemeral Sidechains

Ephemeral Sidechains: pruning headers, not just bodies, by anchoring the checkpoint to Bitcoin

(Follow-up to a previous discussion with @SergioDemianLerner about RSKIP215.)

TL;DR. RSKIP215 (Ephemeral Blockchain) removes old block bodies from consensus, but a syncing node still downloads every RSK header from genesis — the spec explicitly starts by “requesting all block headers from genesis”. For Rootstock that is the expensive part: ~500 B per header, ~600 B of merge-mining proof, plus uncles on roughly half the blocks. Even a Superchain-style 20× reduction leaves a lot of data. But a merge-mined sidechain has an asset that standalone chains lack: Bitcoin itself, whose headers are 80 bytes and whose canonical blocks provide external timestamps and difficulty-weighted samples of hashrate. Ephemeral Sidechains use Bitcoin as the header backbone, so initial sync downloads zero RSK headers before a well-defined checkpointing period.

Assumptions and security model

We assume the following:

  1. Majority of Rootstock miners in the past 14 days are honest.
  2. Reorganizing more than 14 days of Rootstock blocks is impossible.

These are fair assumptions especially giving that currently the PowPeg makes it impossible to reorg that deep without a hard fork, and in any future trust minimized or trustless bridge situation, reverting 14 days worth of Rootstock blocks is most probably going to cause a catastrophic failure of the peg, as the challenging period of a withdrawal attempt is most likely less than two weeks.

Sync algorithm

Finding Checkpointing Period

  1. Download all Bitcoin headers, as any SPV wallet does (~70 MB total today, and checkpointable).

  2. Partition them into difficulty-adjustment periods of 2016 blocks. Call the latest (possibly incomplete) period P(L), and the ones before it P(-1), P(-2), … Within a period every block shares one difficulty D(P), read directly from the already-downloaded headers — so difficulty weighting below costs zero extra data.

  3. Walking backwards from the Bitcoin tip, download each block’s coinbase transaction + Merkle inclusion proof (~600 B per block) and count how many coinbases in each period carry an RSKBLOCK: tag. Define each period’s observed merge-mining work as

    W(P) = tags(P) × D(P)
    
  4. Checkpoint Period: the first P(i), walking back, such that

    W(P(i+1)) + … + W(P(L))  >  W(P(i))
    

    i.e. the merge-mining work observed after the period exceeds the work observed inside it. This mirrors the removal condition of RSKIP215 and serves the same purpose: if participation suddenly drops, the checkpoint retreats until the tail accumulates enough observed work to make re-mining from inside the Checkpoint Period at least as expensive as the honest tail.

Bitcoin headers:  ...──| P(-4) |──| P(-3) |──| P(-2) |──| P(-1) |──| P(L) partial |──▶ tip
tags per period:         1450       1500        380        410           260
difficulty D(P):          100        110        120        130           135     (relative units)
W = tags × D:          145,000    165,000     45,600     53,300        35,100

walk back from tip:
  i = -1 : tail = 35,100                     35,100 > 53,300 ?  no
  i = -2 : tail = 53,300 + 35,100 = 88,400   88,400 > 45,600 ?  YES  
  →  Checkpoint Period = P(-2)

Finding Latest Checkpoint Block

Our purpose is to ensure that the checkpoint block has NOT being mined in a long range attack to overpower other forks in a the fork choice rule. To achieve that, our main constraint is that the checkpoint has to be mined later than the Checkpoint Period start.

To prove that, we define the Checkpoint Block as the earliest block that has in its merge mined bitcoin header a Previous Block hash points to an actual Bitcoin block in the canonical chain, within the Checkpoint Period.

BITCOIN (canonical headers, from SPV sync)

 [B*]...──[b2015]────[ b2016 ]──[ t1 ]──[ t2 ]──[ t3 ]── ... ──▶ btc tip
 └───── Checkpoint Period ───┘  └───────────── tail ─────────────┘
                            ▲
                            └──────┐ Previous Block
                               ┌───┴──────────────────────────┐
                               │ Bitcoin Merged Mining Header │
                               └──────────────────────────────┘
ROOTSTOCK                          │
                               ┌───┴────┐
 (pruned headers) ── ✂ ────────│ RSK_C  │──[c1]──[c2]── ... ──▶ rsk tip
                               └────────┘
                            Checkpoint Block 

Fork Choice and SAFE block

Given two forks with valid Checkpoint Blocks, the one with the heaviest observed subtree (GHOST) is the canonical rootstock chain.

Once a block in the canonical chain is buried under enough confirmations we can consider it SAFE from reversion.

Armadillo

Even if we only see one Rootstock fork, we can then depend on the Armadillo algorithm to detect any hidden forks, and if we think there is one, we delay our subjective SAFE block tagging until we are satisfied a block is buried under enough blocks that don’t seem to be contested with a hidden fork.

FINAL Block

Once we settle which Checkpoint Block is in the canonical chain, using the fork choice rule with or without Armadillo’s help, we can then consider that Checkpoint Block to be FINAL.

                     ┌─────────┐
   Fork A:  ─── ✂ ───│ RSK_C_A │──[a1]──[a2]──[a3]──[a4]     subtree work W_A
                     └─────────┘         └──[a2']            (uncles count, GHOST)

                     ┌─────────┐
   Fork B:  ─── ✂ ───│ RSK_C_B │──[b1]──[b2]                 subtree work W_B
                     └─────────┘

   Both Checkpoint Blocks reference canonical BTC >= B*  ⇒  both ADMISSIBLE
   Fork choice: heaviest observed subtree,  W_A > W_B    ⇒  Fork A canonical
   Fork A's Checkpoint Block                             ⇒  FINAL
   Node downloads state at RSK_C_A, validates forward

   Fork X:  ──[x1]──[x2]──[x3]── ...   every prevHash < height(B*)
                                       (pre-mined long-range fork)
                                   ⇒  INADMISSIBLE — never reaches GHOST

State Download

The state at the FINAL block is then trusted to be downloaded by full nodes that are willing to validate blocks going forward. Otherwise, light clients can check state proofs at any block they consider SAFE.

Cost comparison (initial sync, before the horizon)

RSKIP215 Ephemeral Sidechains
RSK headers from genesis ~500B × 2 * 60 * 24 *365 (~500MB/yr ) none
Merge-mining proofs ~600B × 2 * 60 * 24 *365 (~600MB/yr) ~600B × 2 * 60 * 24 *14 (~25MB)
Uncle headers ~50% of blocks ~50% of blocks
Bitcoin headers 80 B × ~52.5k/yr (~4 MB/yr, checkpointable)
Coinbase + Merkle proof ~600B × 2016 × (2-5) (~2.5-6MB)
Sum 1.65 GB ~50MB

Implementation

I am keeping track of a reference implementation here.

Feedback very welcome.

While thinking about this, I found myself contemplating not only leveraging Armdaillo/SFAMM (Strong fork aware merge mining) but also Inclusive Fork-Aware Merge-Mining; basically:

  1. Check the coinbase txns in the latest complete difficulty epoch in bitcoin (2016 blocks with the same difficulty).
  2. Each block containing an RSK tag, endorses the block in that tag and all its ancestors.
  3. Each block that does not contain an RSK tag, endorses the block its parent endorses.
    • For simplicity only check coinbase txns within the epoch, so if the first block if it is missing a tag, endorses nothing.
  4. Consider a Rootstock block FINAL (discard earlier blocks and do initial headers download starting from it) if and only if it is the root of a tree (canonical and uncles) endorsed by 1009/2016 blocks.

Edit: to be on the safer side, we can increase the threshold from 50%+1 (1009/2016) to 2/3 + 1 (1345/2016) .. making sure that any dissenting (possibly hidden) fork is not strong enough + mute any statistical variants causing this bitcoin-side metric to be different from the consensus fork choice rule in Rootstock.

This means miners with 33% of the bitcoin hash rate can stall removing historical Rootstock headers, but that is not a security issue, instead it just disables most of the benefit of the ephemeral sidechain for light clients.

Some data scrapping Bitcoin coinbase txns and RSK tags to inform my decisions, considering different lengths of tail blocks, and count the number of participating blocks (with RSK tags) and how many of them agree on the same fork (using Armadillo brilliant CPV):

  1. Latest complete epoch (2016 blocks) + incomplete current epoch (2272 blocks total):
================ CPV agreement (RSKIP110) ================
blocks in tail           : 2272
rsk tagged (T)           : 1360
untagged (neutral)       : 912
participation P          : 59.86%  (OK >= 50%)
cpv-consistent tags      : 1359 / 1360
agreement A              : 99.93%  (OK >= 90%)
divergent tags           : 1
  btc 961061: disagrees at rsk#9123008 (deepest 9123008)
-----------------------------------------------------------
verdict                  : AGREEMENT CONFIRMED
  1. 26208 blocks:
================ CPV agreement (RSKIP110) ================
blocks in tail           : 26208
rsk tagged (T)           : 20777
untagged (neutral)       : 5431
participation P          : 79.28%  (OK >= 50%)
cpv-consistent tags      : 20761 / 20777
agreement A              : 99.92%  (OK >= 90%)
divergent tags           : 16
  btc 936236: disagrees at rsk#8520960 (deepest 8520960)
  btc 936976: disagrees at rsk#8535872 (deepest 8535872)
  btc 944836: disagrees at rsk#8727360 (deepest 8727360)
  btc 944837: disagrees at rsk#8727360 (deepest 8727360)
  btc 944890: disagrees at rsk#8728448 (deepest 8728448)
  btc 945580: disagrees at rsk#8745216 (deepest 8745216)
  btc 945717: disagrees at rsk#8748160 (deepest 8748160)
  btc 947439: disagrees at rsk#8791744 (deepest 8791744)
  btc 947524: disagrees at rsk#8793472 (deepest 8793472)
  btc 950253: disagrees at rsk#8857920 (deepest 8857920)
  btc 954403: disagrees at rsk#8962688 (deepest 8962688)
  btc 954558: disagrees at rsk#8966016 (deepest 8966016)
  btc 955340: disagrees at rsk#8983616 (deepest 8983616)
  btc 955951: disagrees at rsk#8998336 (deepest 8998336)
  btc 959316: disagrees at rsk#9084928 (deepest 9084928)
  btc 961061: disagrees at rsk#9123008 (deepest 9123008)
-----------------------------------------------------------
verdict                  : AGREEMENT CONFIRMED

Evidently the agreement increase when the window is larger, also there is more chance to satisfy a much more demanding threshold (67%) which makes it quite hard to fake even if 33% of bitcoin miners are trying to screw with Rootstock:

================ CPV agreement (RSKIP110) [iteration 5] ================
blocks in tail        : 10354
rsk tagged            : 7058
untagged (neutral)    : 3296
participation P       : 68.17%
cpv-consistent tags   : 7052 / 7058
agreement A           : 99.91%
t = P x A             : 0.681   (T = 0.67) PASS
divergent tags        : 6
  btc 954403: disagrees at rsk#8962688 (deepest 8962688)
  btc 954558: disagrees at rsk#8966016 (deepest 8966016)
  btc 955340: disagrees at rsk#8983616 (deepest 8983616)
  btc 955951: disagrees at rsk#8998336 (deepest 8998336)
  btc 959316: disagrees at rsk#9084928 (deepest 9084928)
  btc 961061: disagrees at rsk#9123008 (deepest 9123008)

Source code of this script: rsk/crates/rsk-node/examples/epoch_tags.rs at 3ee334aad02c2bfabbb8189e56195f29d49df4b7 · Nuhvi/rsk · GitHub

I figured out a good formula for Ephemeral Sidechains in the context of BitVM bridge or any trustless bridge, by parameterizing the threshold based on the amounts withdrawn. The same can apply to light clients based on the funds that the user has on the sidechain.

Basically the logic is to require a minimum number of difficulty epochs relative to the amount withdrawn according to the formula: number_of_epochs = ceil(sqrt(amount_in_sats) / (2^20))

These numbers are chosen so that the minimum is 1 difficulty epoch (14 days) and the most is 26 epochs (~1 year).

Hi Nuh,

Thank you for sharing this. Really interesting. I have been following your posts here in the forum but I did not get the chance to reply to any of them yet, sorry.

I read another one of your posts where you were proposing using peg-outs as checkpoints since they cannot be rolled back in practice. I really liked that idea. How does this checkpointing system compare to that?

We are currently working on a better fork detection system to replace Armadillo. Here is the RSKIP and we will publish the paper soon.

Also, I noticed that you shared a repo with a Rust light client. I used AI to build that too, and then converted it into a full node. Here is the repo in case you are interested, and here is a blog post about the work.

As SDL pointed out, Rootstock currently doesn’t have a singleton UTXO where all pegins are merged in and all pegouts go out from, and even if we had that, it is not clear that this will remain forever, not unless we have a trustless bridge at least. So in order to find the current address of the federation, you need to read the Rootstock chain, which defeats the point.

Ephemeral Sidechains doesn’t require that, instead it just reads the RSKBLOCK (I wish the RSKBLOCK tag or at least RSK headers contained a dual parent to point to the “heavy” blocks so we can skip the linear backwards iteration, similar to the Superchain proposal).

Ephemeral Sidechains without any soft forks, just behaves like Ephemeral Blockchain that SDL already suggested, but it prunes headers too, not just blocks, and it does that because it leverages Bitcoin headers, which are cheaper to keep and not prune. Thus the IBD becomes as fast as the proposed Superchain (which I can’ find any specs for) but even cheaper still, because the Superchain uses Rootstock headers instead of bitcoin headers, which is an order of magnitude larger in size.

I also am always trying to force Rootstock’s reliance on Bitcoin, to eventually reach something like Sync chain , Rootstock having entirely separate fork choice rule from Bitcoin, doesn’t set well with me, not on the long term.

I am aware of this, but I am not really sure what is the point of it, compared to just enshrining Armadillo in RSKj and make RSKj always aware of Bitcoin, and not just using a relay. I am open to the suggestion that it is better at fork detection.

but note that fork detection in Ephemeral Sidechains, is not used for realtime detection and halting accepting transactions, it is more of an enhancement, even in its absence, you could measure how many Bitcoin blocks included endorsement of a given Rootstock chain over the past two weeks or so.

Yes I am aware of this and found it very useful, I will probably leverage it more when I need to do advance stuff. Or just fork it and introduce the Ephemeral Sidechain concept to it, since it can always work without consensus.

Thank you for bootstrapping that.

I would have to think more about it, but depends on whether the point is faster syncs or pruning data, doesn’t it? Maybe we could find a way to keep track of the federation address on Bitcoin too?

Of course things would be simpler if Rootstock operated as a sync chain. I also like the idea, and maybe we should consider it. Until then, the new fork detection system embeds detection into the node without relying on external monitoring systems or running a Bitcoin node.

1 Like

Fair enough, if your goal is pruning both blocks and headers, but not necessarily faster IBD, then you can do that. I never thought about that because I always think from the point view of an ephemeral light client that goes online occasionally and needs to figure out stuff as fast as possible, almost like a DNS query. But if you have a long running node and you don’t care about IBD, you just want to discard history including headers, then my original suggestion of using the pegouts would work, I think.

Well, you don’t have to run a bitcoin node, you can run an SPV inside RSKj, you only need headers and coinbase … but I don’t have a strong opinion here so I will not push too hard against that RSKIP, I don’t think it is a bad idea, as long as it doesn’t make syncchain in the future harder.

1 Like

By the way we can combine the core of Ephemeral Sidechains suggestion (using RSKBLOCK as voting on Bitcoin on a checkpoint) with the concept of pegouts as checkpoints, but that will require that RSK headers to contain a metadata pointing to the last pegout, then you fetch the headers of the heavy blocks (blocks mentioned in bitcoin coinbase txns) and count their votes on the latest pegout transaction.

The only problems with that are:

  1. very very recent pegouts, won’t have enough votes on them.
  2. if you don’t have pegouts for a year (which would be likely if rootstock is successful enough that people are satisfied to stay inside it), then you don’t get any performance wins.

So all-in-all, I say just using the concept of Ephemeral Blockchains suggested by SDL, but extending it to prune even the headers, with the help of bitcoin headers, is the best idea I can think of at the moment, short of ZK magic that I really don’t like.

What exact use case do you have in mind?

My main use case is literally DNS. I am working on a name system that is different from .rsk in important ways, but not relevant to this discussion. So let’s just assume I am trying to resolve sergio.lerner.rsk from cold start, using a fork of Chromium or even harder; a fork of curl. In this situation, unless I am going to trust an RPC, which I don’t want to do, I want to find the contract’s “SAFE” state as fast as humanly possible. On the average case, there will already be a “recent” query, so we already synced up to some point in the past, but worst case scenario, in fact the scenario that every new user will need to go through for their first impression, will require a sync from zero.

In this situation, I don’t think anything can work better than my proposal here in Ephemeral Sidechains, except a ZK light client that compresses all history, which is what BitVM will have to have anyways, but I don’t like its complexity.

Syncing to the tip of Bitcoin is very fast, as you can see it happening in Electrum wallet whenever you open it after days of no use. So if the only thing you need on top is to download the past ~2016 coinbase transactions + the past two weeks of Rootstock headers … well I don’t know how can you make light client any lighter and faster IBD without ZK clients that are too expensive to be worth it for me.

1 Like

Note: Superchains would make Ephemeral Sidechains faster still, because it would allow me to only download 2016 RSK headers to find the “FINAL” checkpoint and then calculate the fork’s difficulty from there. Until then I need to download 2016 * 20 RSK headers, and RSK headers are expensive.

But I still didn’t read any spec for superchains, so I can’t tell for sure what I think about. But looking forward to that.

The only optimization I would want after superchains or something like it that skips headers and reduces the overhead of faster blocsk, is that Rootstock headers become smaller … I keep thinking that the RSKBLOCK tag should actually become a full mini-header with the information necessary to find the “FINAL” block without downloading any RSK headers.

But if extending the RSKBLOCK tag is not possible, maybe there is a hardfork that could create a “mini header” for Rootstock, that only includes the PoW and fork choice relevant information, so difficulty and timestamps … but skip all the rest.

Another note; 50-100MB proof of IBD is small enough that it can be requested in one roundtrip from an untrusted peer or server. Of course it also can be bundled in applications as a trustless checkpoint. Or downloaded from the server that is serving a web app, so that the web app can act as an SSV (simple state verification) client.