Protocol
Rotation
A one-time key signs once. The lock rotates in the same instruction that spends it.
The key is one-time. That is the price of the construction, and it is the reason the lock rotates in the same instruction that spends it.
Why a key must never sign twice
Suppose one key signs two digests, and . For every chain the attacker now holds the lower of the two released values:
They can sign any digest whose chain lengths all sit at or above that floor:
With one signature the checksum rules this set down to itself. With two, the floor on the checksum chains is the lower of two checksums, and digests that satisfy the condition exist. The attacker needs no preimage. The program cannot detect that a key was used twice, so rotation is not a feature. It is a requirement.
What the program enforces
finalize_withdrawalsetsVault.commitmentto and incrementsVault.noncein the same instruction that pays out. There is no path that pays without rotating.open_withdrawalrejects withCommitmentReused. Rotating to the same commitment would mean signing twice with one key.init_vaultandopen_withdrawalboth reject a zero commitment withZeroCommitment. A zero commitment is a vault that can be funded and never emptied.Vault.rotationscounts completed rotations, so a wallet can show how many times a lock has turned.
What the client must do
The client derives chain secrets deterministically from the seed, so the key for state is computable before the key for state is revealed. See Recovery words for the derivation.