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 KiB
Text
69 lines
3 KiB
Text
pragma cashscript ^0.12.0;
|
|
|
|
// DeviantEscFeeBlockHeightV2 — EXPERIMENTAL FEASIBILITY TEST (not production).
|
|
//
|
|
// Workaround for trigger 3 after DeviantEscFeeBlockHeight.cash failed to
|
|
// compile (tx.time is not a readable integer — only a require() operand).
|
|
//
|
|
// Approach: the claimer PASSES the block height as an argument
|
|
// (`claimedHeight`), and the contract binds it to consensus with
|
|
// require(tx.time >= claimedHeight);
|
|
// which lowers to OP_CHECKLOCKTIMEVERIFY: the tx's nLocktime must be
|
|
// >= claimedHeight, and CLTV guarantees nLocktime <= the real current
|
|
// height. So claimedHeight <= currentHeight. The fee is then computed
|
|
// from claimedHeight arithmetically (which IS allowed — it's a normal int).
|
|
//
|
|
// !!! SECURITY HOLE (the headline finding for trigger 3) !!!
|
|
// require(tx.time >= claimedHeight) only puts a LOWER bound on the height.
|
|
// Script has NO opcode to read the *actual* current height as a value, so
|
|
// it cannot force claimedHeight up to "now". A rational claimer therefore
|
|
// always passes the SMALLEST height that still validates (>= genesisHeight),
|
|
// paying the base fee forever. Block-height fee escalation is thus NOT
|
|
// adversarially enforceable on BCH — it degrades to "honest claimers pay
|
|
// more, cheaters pay base." Documented, not production-viable.
|
|
|
|
contract DeviantEscFeeBlockHeightV2(
|
|
bytes tokenCategory,
|
|
int backingRateSats,
|
|
int baseFeeBps,
|
|
int feeStepBps,
|
|
int maxFeeBps,
|
|
int genesisHeight,
|
|
int blocksPerStep,
|
|
bytes founderLockingBytecode
|
|
) {
|
|
function claim(int tokenAmount, bytes recipientLockingBytecode, int claimedHeight) {
|
|
require(tokenAmount > 0);
|
|
|
|
// Bind the claimed height to consensus (lower bound only — see hole above).
|
|
require(tx.time >= claimedHeight);
|
|
require(claimedHeight >= genesisHeight);
|
|
|
|
int currentReservoir = tx.inputs[this.activeInputIndex].tokenAmount;
|
|
int grossSats = tokenAmount * backingRateSats;
|
|
|
|
int elapsed = claimedHeight - genesisHeight;
|
|
int feeBps = baseFeeBps + (elapsed / blocksPerStep) * feeStepBps;
|
|
if (feeBps > maxFeeBps) {
|
|
feeBps = maxFeeBps;
|
|
}
|
|
int feeSats = grossSats * feeBps / 10000;
|
|
|
|
require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);
|
|
require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);
|
|
require(tx.outputs[0].tokenAmount == currentReservoir - tokenAmount);
|
|
require(tx.outputs[0].value >= 546);
|
|
|
|
require(tx.outputs[1].lockingBytecode == recipientLockingBytecode);
|
|
require(tx.outputs[1].tokenCategory == tokenCategory);
|
|
require(tx.outputs[1].tokenAmount == tokenAmount);
|
|
require(tx.outputs[1].value == grossSats);
|
|
|
|
if (feeSats > 0) {
|
|
require(feeSats >= 546);
|
|
require(tx.outputs[2].lockingBytecode == founderLockingBytecode);
|
|
require(tx.outputs[2].value == feeSats);
|
|
require(tx.outputs[2].tokenCategory == 0x);
|
|
}
|
|
}
|
|
}
|