The ETC Anchor
Your digest climbs three Merkle trees, and the last root goes into the input of one Ethereum Classic transaction. That transaction is the anchor every proof ends in.
The Merkle Tree
Etchd builds its trees the way python-opentimestamps does. Leaves pair left to right as sha256(left || right), and when a level has an odd count the last element moves up unchanged. A single leaf is its own root. Each step on the path appends or prepends the sibling and hashes with SHA-256. It costs 35 bytes in the proof.
Your proof walks all three in a row.
digest
append(server nonce) sha256
round tree hops every digest in the same second
prepend(u32be(second)) pending attestation sits here
sha256
calendar tree hops every second of the minute
prepend(prev_tick_root)
append(beacon_root || witness_root || tick_header) sha256
tick_root
batch tree hops every tick in one ETC transaction
prepend(u64be(last_n)) EtcTxAttestationA proof with no tree hops is 297 bytes. One with a million digests in the same second, a full minute of rounds and a 1,000-tick backlog is 1,557 bytes. To see the trees of a real tick, open its page from the live view, which draws them next to the raw tick.
One Transaction a Minute
One transaction carries a Merkle root over every closed tick that is not on chain yet, which in normal running means one tick and one transaction a minute. The calldata is 40 bytes.
calldata = u64be(last_n) || batch_rootEach one is a legacy EIP-155 transaction at a fixed 1 gwei with a gas limit of 30,000. It uses about 23,100 gas.
The Contract
The transaction goes to the anchor contract EtchdAnchor at 0xa6f241d25efa4e8582b808fa44cda1a68cd69fed on ETC mainnet, chain id 61, verified on Blockscout. It stores nothing. It copies the calldata and emits it as a LOG1 with the sender as the only topic. This is the whole source, from EtchdAnchor.sol.
// SPDX-License-Identifier: MITpragma solidity 0.8.33; /// @title etchd anchor/// @author Classix/// @notice Once every UTC minute etchd (https://etchd.app) sends this contract 40 bytes: the newest tick number/// covered (8 bytes, big-endian) and a Merkle root (32 bytes) over the ticks in that batch. Each tick root/// commits to every hash stamped, every chain head and every witness capture of its minute./// @dev The contract stores nothing and has no owner, so anyone can call it. It logs the calldata unchanged, with the/// sender's address as the only topic, so anchors can be found with eth_getLogs. Proofs never rely on the log./// They point at the transaction and check its calldata. etchd publishes its signer addresses at/// https://etchd.app/v1/signers, which is how its anchors are told apart from anyone else's.contract EtchdAnchor { fallback() external payable { assembly ("memory-safe") { // Copy the whole calldata (40 bytes from etchd) to memory at offset 0. calldatacopy(0, 0, calldatasize()) // Emit it as a log with one topic, the address that sent the transaction. log1(0, calldatasize(), caller()) } }}A Public Noticeboard
Nothing guards EtchdAnchor. It has no owner and no access check, and it copies whatever calldata it gets into a log whose only topic is the sender's address.
event = LOG1(topic0 = msg.sender, data = calldata)Anyone can post to it, whether another calendar with roots of its own or someone with 40 bytes to put on ETC for some other reason. We left it open on purpose. ETC Transaction Format in the design notes gives the reasons.
Records from different senders end up side by side in the same log, but a proof points at one exact transaction, the verifier checks who sent it, and our site shows only our own records.
One Exact Transaction
An ETC attestation names a block height and a position in that block, and the verifier compares the input of that one transaction with the 40 bytes your proof computes. The verifier does no search of the contract's log, and a post from someone else cannot stand in for one of ours, or the other way round.