pragma cashscript ^0.13.0; // Subscription — a prepaid balance the host can draw from once per period, // and the buyer can take back at any time. // (Argus/src/lib/plans.js SUBSCRIPTION_ROADMAP; ROADMAP-MAINNET.md §9 step 4.) // // STATUS: chipnet draft. NOT REVIEWED. // // The buyer funds this script. Three things then hold, and the buyer can check // each from the script alone: // // · the balance stays theirs — cancel() needs only their signature, no // notice, no cooperation, and returns whatever is left; // · the host cannot take more than one period's price per period — pull() // pays exactly the host's lock at least `priceSats`, sends the rest back to // this same script, and can only run on an output that is `periodBlocks` // old, which the re-created output is not; // · nobody else gains anything — pull() needs no signature, so the host does // not have to be online with a key, but its only possible beneficiary is // the host's lock. // // A pull spends this input ALONE. With two inputs, one "price to the host" // output and one "rest back to the script" output could satisfy both, and the // host would collect two balances for one payment. One input means the fee // comes out of the balance too, bounded by `maxFeeSats`. // // The first period is not special-cased: the balance is drawn one period // after it is funded. A host that wants payment up front takes the first // period in the funding transaction itself. contract Subscription(bytes20 buyerPkh, bytes hostLock, int priceSats, int periodBlocks, int maxFeeSats) { // One period's payment to the host. function pull() { require(this.age >= periodBlocks); require(tx.inputs.length == 1); int balance = tx.inputs[0].value; require(tx.outputs[0].lockingBytecode == hostLock); require(tx.outputs[0].value >= priceSats); require(tx.outputs[0].tokenCategory == 0x); // Everything else stays in the subscription, less a bounded fee. When // what would remain is too small to be an output, it is the fee. int rest = balance - priceSats - maxFeeSats; if (rest >= 546) { require(tx.outputs[1].lockingBytecode == tx.inputs[0].lockingBytecode); require(tx.outputs[1].value >= rest); require(tx.outputs[1].tokenCategory == 0x); } } // The buyer ends it and takes the balance. No conditions on where it goes. function cancel(pubkey pk, sig s) { require(hash160(pk) == buyerPkh); require(checkSig(s, pk)); } }