Assume for simplicity contract id == txid of kickoff tx.
"register" == where we store committed state, can be CCV, caboose, whatever
kickoff tx has two outputs. These outputs, or the descendant of the first output and the second output MUST be spent together on the second spending path.
output 1 has a spk HISTORY_SPK which:
- self-replicates spk in new output (for simplicity)
- checks input's register for a contract id commitment
if commitment exists:
forward contract id commitment in register to new output
else:
check input for "kickoff" commitment (not sure what this does?)
if kickoff commitment exists:
assert txid-based contract id commitment by introspecting kickoff tx
and embed contract id in register in new output
(this commitment may have random metadata for whatever)
else:
fail - checks inputs' parent tx that it also came "from" HISTORY_SPK
XOR
- generates the contract id (~same steps as above)
- assert that contract id matches other input's prevout
- assert exiting business logic (like, where coins are going)
Clearly, HISTORY_SPK is correct because we audited the code, so this allows an inductive proof of ancestry at each spend, where the contract id is the kickoff txid.
Can we transfer the proof to other contracts?
Second output knows its own prevout, needs to "whitelist" HISTORY_SPK (because we know it's correct because we audited it). This can be done by inspecting the kickoff tx or embedding it in the taptree of the CLRT directly, or put in the output register.
output 2 has a spk EXIT_SPK which:
- checks other input is HISTORY_SPK
- does inductive checks to validate ancestry proof to contract id
- asserts contract id matches own prevout
- asserts any own business logic as well (e.g., CSV)