Brings the whole project into git (previously only the on-ramp feature and a few parallel files were tracked). Includes: wallet core (src/), CashScript contracts/, the Capacitor shell (shells/), scripts/, tests, and the docs corpus (wiki/, snapshots/, Parameters/, Archive/, KnowledgeBase text). Excluded via .gitignore: node_modules/, dist/, .env (secrets), testnet WIF key files (.chipnet-*.json, devWallets.ts), KnowledgeBase/RAW media (~9.6 GB), transcripts/ (session logs), and machine-local config (.claude/*.local, *.lock). Secret-scanned before commit — no credentials in the tree. Note: src/swapMath.ts, src/swapBuilder.ts, contracts/OpenMintToken.cash are legacy (OP_RETURN era) but still imported; committed as-is, flagged for a separate removal once de-referenced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
69 lines
3.7 KiB
Text
69 lines
3.7 KiB
Text
pragma cashscript ^0.12.0;
|
|
|
|
// SortitionBeacon — commit-reveal randomness beacon for Athenae sortition (the
|
|
// Kleroterion's entropy source). This is the BCH-native core of "hybrid E".
|
|
//
|
|
// WHY THIS SHAPE (honest BCH limits):
|
|
// - BCH has NO OP_BLOCKHASH — a covenant cannot read a block hash, so a pure
|
|
// block-anchor mix is not covenant-verifiable. The bias-resistance therefore
|
|
// comes from commit-reveal (one honest committer => unbiasable output).
|
|
// - CLTV (tx.time) is a LOWER bound only (same limit PollCovenant documents),
|
|
// so the commit-phase CLOSE is a finalize-race; the finalize TIMING is the
|
|
// consensus-enforceable anchor: finalize cannot run before commitDeadline.
|
|
//
|
|
// MECHANISM:
|
|
// Commit phase: each commit(H(secret_i)) appends into a hash-chain root carried
|
|
// in the mutable NFT commitment: root_k = sha256(root_{k-1} ++ H(secret_k)),
|
|
// root_0 = tag. Commitments are binding; nobody can see others' secrets.
|
|
// Finalize: after commitDeadline (CLTV), the caller supplies ALL revealed
|
|
// secrets; the covenant recomputes the chain and requires it to equal the stored
|
|
// root (so every secret was genuinely committed), then writes the beacon output
|
|
// seed = sha256(s1 ++ s2 ++ s3). A withholder can only ABORT (griefing) — never
|
|
// bias — and abort is handled upstream by streaming juror re-draw.
|
|
//
|
|
// Fixed cohort N=3 here (finalize arity). Production generalises N via a merkle
|
|
// commit root + per-secret proofs; the security argument is identical.
|
|
//
|
|
// commitment layout: phase(1 byte) ++ value(32 bytes)
|
|
// phase 0x01 = COMMIT (value = running root)
|
|
// phase 0x02 = FINAL (value = seed) -- consumed read-only via co-spend by the draw
|
|
|
|
contract SortitionBeacon(
|
|
bytes tag, // 32-byte domain separator / round id (root_0)
|
|
int commitDeadline // block height; finalize requires tx.time >= this
|
|
) {
|
|
// Permissionless: anyone may add entropy. More commits = more honest mass.
|
|
function commit(bytes commitHash) {
|
|
bytes state = tx.inputs[this.activeInputIndex].nftCommitment;
|
|
bytes phase, bytes root = state.split(1);
|
|
require(phase == 0x01); // must be COMMIT phase
|
|
bytes newRoot = sha256(root + commitHash); // append to hash-chain
|
|
|
|
require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);
|
|
require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);
|
|
require(tx.outputs[0].nftCommitment == 0x01 + newRoot);
|
|
require(tx.outputs[0].value >= 546);
|
|
}
|
|
|
|
// Reveal-all + finalize. CLTV gate makes "before this height" impossible.
|
|
function finalize(bytes s1, bytes s2, bytes s3) {
|
|
require(tx.time >= commitDeadline); // consensus timing anchor
|
|
|
|
bytes state = tx.inputs[this.activeInputIndex].nftCommitment;
|
|
bytes phase, bytes root = state.split(1);
|
|
require(phase == 0x01);
|
|
|
|
// Recompute the exact commit hash-chain from the revealed secrets.
|
|
bytes r1 = sha256(tag + sha256(s1));
|
|
bytes r2 = sha256(r1 + sha256(s2));
|
|
bytes r3 = sha256(r2 + sha256(s3));
|
|
require(r3 == root); // every secret was committed
|
|
|
|
bytes seed = sha256(s1 + s2 + s3); // the beacon output
|
|
|
|
require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);
|
|
require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);
|
|
require(tx.outputs[0].nftCommitment == 0x02 + seed); // phase -> FINAL, value = seed
|
|
require(tx.outputs[0].value >= 546);
|
|
}
|
|
}
|