Worked Example

We stamped a 40-byte file called hello.txt on 2026-10-23 at 09:33:05 UTC and followed it until it was anchored in Ethereum Classic block 25,590,762 about a minute later. Every output and byte below comes from that one run.

The digest landed in tick 29,879,133. The anchor is transaction index 2 of that block, 0xe6cd0d0b… on Blockscout. A digest that arrives alone in its second gets no round tree at all. Our first try at 09:26 UTC came out that way, and for this run we sent two more texts in the same second from separate processes, plus a third 16 seconds later, to give each tree something to pair with.

The File

The file and its SHA-256
$ cat hello.txt
hello from the etchd README, 2026-10-22

$ sha256sum hello.txt
c7a867b8a2c44a0ef5e9641d3f6f0b262836e88c716a93a2daa936b7f12b67da  hello.txt

That is 39 characters and a newline. The proof commits to the digest alone.

Stamping

etchd stamp
$ etchd stamp hello.txt
Submitting 1 digest as root fe1747d2fc44a0da1a33eaedc8d5c169a50aa5d5cc2c4adc31b386c6c0636266 to 1 calendar
stamped  hello.txt  hello.txt.ots
calendar https://a.etc.calendar.etchd.app answered in 609 ms
root fe1747d2fc44a0da1a33eaedc8d5c169a50aa5d5cc2c4adc31b386c6c0636266
Run etchd upgrade on the .ots files once the tick is anchored.

The CLI hashed the file, appended 16 random bytes of its own and hashed again, and that second hash, fe1747d2…, is the root it printed and the whole 32-byte body of its POST /digest to https://a.etc.calendar.etchd.app. The plain digest stayed on our machine.

The calendar answered with 138 bytes. They hold its own 16-byte nonce, the path through the round tree for second 1,792,747,985 and a pending attestation that names the calendar, and the CLI put its nonce in front of them and wrote all 222 bytes to hello.txt.ots.

The pending proof, 222 bytes
$ xxd hello.txt.ots
00000000: 004f 7065 6e54 696d 6573 7461 6d70 7300  .OpenTimestamps.
00000010: 0050 726f 6f66 00bf 89e2 e884 e892 9401  .Proof..........
00000020: 08c7 a867 b8a2 c44a 0ef5 e964 1d3f 6f0b  ...g...J...d.?o.
00000030: 2628 36e8 8c71 6a93 a2da a936 b7f1 2b67  &(6..qj....6..+g
00000040: daf0 105d c801 afb1 f57e 561b 4396 c573  ...].....~V.C..s
00000050: 7f13 1708 f010 3e04 b6de d1f6 d1fd ac7b  ......>........{
00000060: 63ec e85e fb39 08f0 2068 c174 fbe2 c287  c..^.9.. h.t....
00000070: 92cd 48c8 446c f105 7951 6eb4 a1c4 e7b7  ..H.Dl..yQn.....
00000080: b7e7 9580 4cdf 6537 5108 f020 23e8 c7a1  ....L.e7Q.. #...
00000090: b89b a893 41ea b82b 8006 5a03 41b9 b7e5  ....A..+..Z.A...
000000a0: f286 0f2b e00a 2cba 1850 2653 08f1 046a  ...+..,..P&S...j
000000b0: db29 d100 83df e30d 2ef9 0c8e 2120 6874  .)..........! ht
000000c0: 7470 733a 2f2f 612e 6574 632e 6361 6c65  tps://a.etc.cale
000000d0: 6e64 6172 2e65 7463 6864 2e61 7070       ndar.etchd.app

Read it top to bottom. After the header come the ops, each with its argument, laid out as SPEC.md describes under Proof File and under Submission and Round.

OffsetBytesMeaning
0x0000 4f70…7300 0050 726f6f66 00 bf89e2e884e89294OpenTimestamps magic, 31 bytes
0x1f01Major version 1
0x2008 c7a8…67daFile hash op sha256 and the digest
0x41f0 10 5dc8…1317 08Append the CLI nonce, sha256
0x54f0 10 3e04…fb39 08Append the calendar nonce, sha256
0x67f0 20 68c1…3751 08Round tree hop, first companion
0x8af0 20 23e8…2653 08Round tree hop, second companion
0xadf1 04 6adb29d1Prepend the second, 09:33:05 UTC
0xb300 83dfe30d2ef90c8e 21 20 6874…7070PendingAttestation with its 32-byte URI

etchd info prints the same ops. We added the message each one produces.

etchd info, with the messages added
$ etchd info hello.txt.ots
File sha256 hash c7a867b8a2c44a0ef5e9641d3f6f0b262836e88c716a93a2daa936b7f12b67da
222 bytes, pending
PendingAttestation(https://a.etc.calendar.etchd.app)

append 5dc801afb1f57e561b4396c5737f1317                                   CLI nonce
sha256                -> fe1747d2fc44a0da1a33eaedc8d5c169a50aa5d5cc2c4adc31b386c6c0636266
append 3e04b6ded1f6d1fdac7b63ece85efb39                                   calendar nonce
sha256                -> 04e3ec4ffbca0bce79f52b4f23a721be2bdf5f1d8b6f92f438804c578c8c2508
append 68c174fbe2c28792cd48c8446cf10579516eb4a1c4e7b7b7e795804cdf653751   round tree
sha256                -> 0579c9be07c27f0db7b8a294ab077b8a764e2270109b18e43658b0d4512a015a
append 23e8c7a1b89ba89341eab82b80065a0341b9b7e5f2860f2be00a2cba18502653   round tree
sha256                -> ea1e47c37df77b8f9d179d4257e7fd2be11f1e5d377724af132833b5d7a2360c
prepend 6adb29d1      -> 6adb29d1ea1e47c37df77b8f9d179d4257e7fd2be11f1e5d377724af132833b5d7a2360c
verify PendingAttestation(https://a.etc.calendar.etchd.app)

The last message, 36 bytes, is the round commitment. The calendar files our round under it, and it is the key we use later to ask for the rest of the proof.

Three digests arrived in that second. With three leaves, ours and the first companion pair up, and the second companion moves up a level unchanged before it pairs with the result, so both of our round hops are appends.

Waiting for the Anchor

Tick 29,879,133 runs from 09:33:00 to 09:33:59 UTC. It held two rounds. When the minute closed the calendar sealed the tick root 667b853c… and sent it to the anchor contract at ETC block 25,590,761, and it was mined in the next block, 25,590,762, at index 2.

Upgrade three minutes later
$ etchd upgrade hello.txt.ots
pending  hello.txt.ots
hello.txt.ots  https://a.etc.calendar.etchd.app says Pending confirmation in tick 29879133, block 25590762, 7/400 confirmations
$ echo $?
2

Exit code 2 means still pending. On 2026-10-23 the calendar answered GET /timestamp/<commitment> with a 404 until the anchor was 400 blocks deep, about 90 minutes, while its JSON view at /v1/proofs/6adb29d1… carried the same ops from the moment the transaction was mined.

Upgrading

etchd upgrade asks the calendar named in the pending attestation for the rest of the path, with the full 36-byte commitment as hex in the URL. The answer is a serialized timestamp from the commitment to the ETC attestation. The CLI merges it in place.

We did not wait for the 404 to clear. We took the 195 bytes of the answer from /v1/proofs at 108 confirmations and merged them with the same Timestamp.merge the CLI calls, and the proof grew from 222 bytes to 418.

The upgraded proof, 418 bytes
$ xxd hello.txt.ots
...                                                the first 176 bytes as before
000000b0: db29 d1ff 0083 dfe3 0d2e f90c 8e21 2068  .)...........! h
000000c0: 7474 7073 3a2f 2f61 2e65 7463 2e63 616c  ttps://a.etc.cal
000000d0: 656e 6461 722e 6574 6368 642e 6170 7008  endar.etchd.app.
000000e0: f020 54d7 efe5 7761 1943 0264 f0aa c221  . T...wa.C.d...!
000000f0: bbd1 f901 61da 8d9f 1099 9bfd 5aa9 a935  ....a.......Z..5
00000100: d2f5 08f1 2062 1d6d d210 e658 c99e 6497  .... b.m...X..d.
00000110: 7858 1350 df66 ca91 eac4 4c85 3e1c 33eb  xX.P.f....L.>.3.
00000120: c399 5b76 33f0 60a8 55b2 8ccd 168c 8d65  ..[v3.`.U......e
00000130: bca3 84e8 0e7a 51de 051b c37f 83a5 3b5e  .....zQ.......;^
00000140: 7902 8a30 7bf2 6381 6f9a 5a5d a69a dcfc  y..0{.c.o.Z]....
00000150: 5344 48e6 f2d1 edab 62b0 4e4e 41d3 17bc  SDH.....b.NNA...
00000160: 3bb0 ca58 ffcd 8708 7638 39ee 3a2d e97d  ;..X....v89.:-.}
00000170: 6b2b 6c28 4e7c 0fc1 a936 9475 72f0 85ad  k+l(N|...6.ur...
00000180: 1bd4 1cb7 9221 d008 f108 0000 0000 01c7  .....!..........
00000190: eb5d 0059 1578 395f 27f2 a106 3dea f799  .].Y.x9_'...=...
000001a0: 0c02                                     ..

The first change is at 0xb3. The pending attestation now starts with ff, the marker for an item that is not the last at its node, and the new branch follows the URI at 0xdf.

Here is the new part, op by op, from the round commitment.

From the round commitment to the ETC attestation
# our leaf in the calendar tree
sha256   -> fe286997a450dc96c7a5bc6055dfb1f98205f56e5d085fa3d89fd85e62c9e8d6
# calendar tree hop, the leaf of the 09:33:21 round, gives the calendar root
append 54d7efe5776119430264f0aac221bbd1f90161da8d9f10999bfd5aa9a935d2f5
sha256   -> c44ddc29c1989b4b4c5407af30b11fcf228fbd7841c426f51744014c7fc4163b
# previous tick root in front
prepend 621d6dd210e658c99e649778581350df66ca91eac44c853e1c33ebc3995b7633
# beacon root, witness root, tick header hash, gives the tick root
append a855b28ccd168c8d65bca384e80e7a51de051bc37f83a53b5e79028a307bf263
       816f9a5a5da69adcfc534448e6f2d1edab62b04e4e41d317bc3bb0ca58ffcd87
       08763839ee3a2de97d6b2b6c284e7c0fc1a936947572f085ad1bd41cb79221d0
sha256   -> 667b853c36933e90630a441a50cf14134b0fda6c0eb9b5604c934ad508f33240
# last tick of the batch as u64be, gives the 40 bytes
prepend 0000000001c7eb5d
         -> 0000000001c7eb5d667b853c36933e90630a441a50cf14134b0fda6c0eb9b5604c934ad508f33240
verify EtcTxAttestation(chain 61, block 25590762, tx 2)

The calendar tree had two leaves, the SHA-256 of each round commitment in the tick, by second. Ours came first. Its hop appends the leaf of the 09:33:21 round, 54d7efe5…. The calendar root then goes into the tick root as one of five 32-byte parts, and the proof prepends the previous tick root and appends the other three in one 96-byte op (beacon root, witness root, tick header hash). All five values are on the tick page, and Ticks says what goes into each.

The batch tree has no hops here. The transaction carried tick 29,879,133 alone. When the calendar falls behind and one transaction carries several ticks, this tree adds append or prepend steps with sha256 like the other two.

Diagram. A digest climbs the round tree, the calendar tree and the batch tree, and the last root goes into an Ethereum Classic transaction. Your proof climbs the round tree, the calendar tree and the batch tree, and the last root goes into the ETC transaction. Each step on the path costs 35 bytes, and the odd leaf at the end of a level moves up unchanged.

The closing prepend puts the last tick of the batch in front as 8 bytes, 0x1c7eb5d = 29,879,133, and the 40 bytes that result are the input of the anchor transaction. The attestation says where to find it.

The attestation bytes
00                   attestation marker
591578395f27f2a1     EtcTxAttestation tag
06                   payload length 6
3d                   chain id 61
eaf7990c             block height 25590762, varuint
02                   transaction index 2

Any ETC RPC returns that transaction, and its input is the 40 bytes above. Here is the answer from etc.etcdesktop.com, trimmed. Blockscout shows the same transaction at /tx/0xe6cd0d0b….

eth_getTransactionByBlockNumberAndIndex(25590762, 2)
$ curl -s https://etc.etcdesktop.com -H 'content-type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionByBlockNumberAndIndex","params":["0x1867bea","0x2"]}'
"blockHash": "0x6b88a150d4bc3731e40a028ccf900a6f419443101b8a3075c57c1d8a2953446c",
"blockNumber": "0x1867bea",
"from": "0xf734f27f32e14e6cfce32c6b8f2c1f57e573064e",
"to": "0xa6f241d25efa4e8582b808fa44cda1a68cd69fed",
"hash": "0xe6cd0d0ba60b89d039055c6c6fbb8544cc3c983903a6f280c655f04cb7441c26",
"input": "0x0000000001c7eb5d667b853c36933e90630a441a50cf14134b0fda6c0eb9b5604c934ad508f33240",
"transactionIndex": "0x2",
"chainId": "0x3d"

Verifying

etchd verify, at 10:02 UTC
$ etchd verify hello.txt.ots
Checking hello.txt.ots against 3 ETC RPCs
pending at https://a.etc.calendar.etchd.app
ETC block 25590762 tx 2 verified, 108 confirmations, block time 2026-10-23T09:33:49Z
  anchored by signer epoch 1 of https://a.etc.calendar.etchd.app (0xf734f27f32e14e6cfce32c6b8f2c1f57e573064e)
Existed before 2026-10-23T09:33:49Z
verified  hello.txt.ots
$ echo $?
2

The verifier read hello.txt next to the proof and then did the following.

  1. Hashed hello.txt and compared it with the proof's digest.
  2. Replayed the ops down to the 40 bytes.
  3. Asked three ETC RPCs for block 25,590,762, index 2. Two had to answer, both with chain id 61 and the same block hash.
  4. Compared the transaction input with the 40 bytes and counted confirmations.
  5. Matched the sender to signer epoch 1.

The match held, at 108 confirmations. The exit code was 2 because the CLI we ran that day wanted 400 before returning 0. That threshold is a flag, and how many confirmations to ask for is your choice.

Block 25,590,762 carries a block time of 09:33:49 UTC, ten seconds before its tick ended, because ETC miners set block times themselves and they drift by seconds (see Precision). The pending attestation stays in the file. The next upgrade asks there for the Bitcoin branch.

The Bitcoin Branch

This proof had no Bitcoin branch yet. The bridge rolls up the ticks of each UTC hour and stamps the roll-up through OpenTimestamps. The branch comes back once a Bitcoin block includes the roll-up, usually a few hours later, and running etchd upgrade again merges it.

An earlier stamp of the same file shows the result. It was made for the README on 2026-10-22 at 08:22 UTC, and a second upgrade on 2026-10-23 took it from 313 bytes to 2,782, with two Bitcoin attestations on a branch that hangs off the tick root next to the ETC prepend.

The README stamp after a second upgrade
$ etchd upgrade hello.txt.ots
complete  hello.txt.ots  upgraded

$ etchd info hello.txt.ots | head -6
File sha256 hash c7a867b8a2c44a0ef5e9641d3f6f0b262836e88c716a93a2daa936b7f12b67da
2782 bytes, complete
PendingAttestation(https://a.etc.calendar.etchd.app)
BitcoinBlockHeaderAttestation(972602)
BitcoinBlockHeaderAttestation(972599)
EtcTxAttestation(chain 61, block 25584107, tx 0)

$ ots info hello.txt.ots | tail -3
        verify BitcoinBlockHeaderAttestation(972599)
        # Bitcoin block merkle root 898043d20e3fc58752d9fd99769e9c43c6e3a8cf48c49d78cc36fe6151a7afc6
 -> prepend 0000000001c7e576

etchd verify prints bitcoin attestation unchecked for those two. It reads ETC only. Stock ots verify checks them against a Bitcoin node, and without one you can compare the merkle root by hand, since block 972,599 on mempool.space shows merkle root 898043d2…afc6 and a block time of 2026-10-22 09:13:58 UTC.

What This Shows

With hello.txt and the 418-byte proof, anyone can check that a file with digest c7a867b8…67da existed before ETC block 25,590,762, which carries the time 2026-10-23 09:33:49 UTC, and see how many confirmations the block has on the day they look. Their own ETC node will do, or two public RPCs. The calendar can be offline. The proof leaves open who wrote the file and when it was first made. Any file with the same 40 bytes has the same digest.