Continuation Effects
Suppose we had the following bit of code in a contract's implementation:
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize)] struct PayToKey(bitcoin::PublicKey); /// Helper fn default_coerce( k: <T as Contract>::StatefulArguments, ) -> Result<PayToKey, CompilationError> { Ok(k) } /// A Guarded CTV Function #[continuation( /// required: guards for the miniscript clauses required guarded_by = "[Self::guard_1,... Self::guard_n]", web_api, /// helper for coercing args for json api, could be arbitrary coerce_args = "default_coerce" )] fn to_address(self, ctx:Context, o:PayToKey) { let amt = ctx.funds(); ctx.template().add_output(amt, &o.0, None)?.into() } }
When the to_address
function gets passed by the compiler, a unique pointer (an effect path) is
generated for it from the context object. This enabled sending it parameters
later in the future.
On creation of the context object a effects: Arc<MapEffectDB>
parameter is
available. This MapEffectDB
links the effect paths to a list of arguments
intended to be passed to this branch which can generate new contract transitions
intended to be signed off on by the guards to that path.
For example, consider a contract for a NFT (a provenance checkable certificate of ownership).
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize)] struct NFT(bitcoin::PublicKey); #[derive(Serialize, Deserialize)] struct Sale(bitcoin::PublicKey, AmountF64); /// Helper fn default_coerce( k: <T as Contract>::StatefulArguments, ) -> Result<Sale, CompilationError> { Ok(k) } impl NFT { #[guard] fn signed(self, ctx:Context) { Clause::Key(self.0) } #[continuation( guarded_by = "[Self::signed]", web_api, /// helper for coercing args for json api, could be arbitrary coerce_args = "default_coerce" )] fn make_sale(self, ctx:Context, o:Sale) { let amt = ctx.funds(); ctx.template() .add_amount(o.1) // Carry whatever funds in the UTXO to the buyer in // a new NFT .add_output(amt, &NFT(o.0), None)? // Pay the sale amount to the previous owner .add_output(amt, &self.0, None)? .into() } } impl Contract for NFT { declare!{updatable<Sale>, Self::make_sale} } }
The updates generated through make_sale
generate the transactions for a series
of sales. For example, imagine I start with a NFT(Bob)
.
I can recompile NFT(Bob)
with the context (not exactly the pointer, but just for example)
NFT(Bob)
with effects
{"0": {"make_sale": [["Alice", 10]]}}
Supposing Alice pays into the transaction, a future transaction could be:
{"0": {"make_sale": [["Alice", 10]]}, "1": {"make_sale": [["Carol", 11]]}}
Thus by starting with a known valid NFT (e.g., Bob being the original artist), the effects can regenerate a series of state transitions for verification of provenance.
Further, as effects at a given point are a set, there could be multiple in flight transitions. E.g.,
{"0": {"make_sale": [["Alice", 10], ["Eve", 10]]}}
represents Alice and Bob both having the ability to purchase the NFT for 10 BTC.
Challenges
- By using decreasing time locks, implement a dutch auction pitting two participants against each other.