Goal and motivations
The goal of this thread is to start engaging builders, wallet software and more generally the ecosystem to think practically on covenant usability.
Currently, the most rational choice is either shipping your own covenant-specific compatible wallet software or to push current wallet to adopt your covenant-specific SDK. I believe there are more universal paths that should be considered.
I will try to assume minimal prior knowledge and provide optional deeper accordions where I think it deserves special attention for the most curious readers.
Wallet implementation challenges
TLDR; As a builder using covenant or SilverScript (or any other abstraction layer above), I currently need to tell my users (or my wallet partners) to implement my covenant comprehension so they can (i) track covenant state across executions and (ii) build compatible transactions / transition executions.
Letâs walk through this simplified example so we understand better whatâs going on under the hood (do not use this example in production, it has flaws):
contract ThresholdVault(
pubkey initAgent, /* agent public key */
pubkey initOwner, /* human public key */
int initThreshold, /* threshold Kas value agent is allowed to spend in autonomy */
) {
pubkey agent = initAgent;
pubkey owner = initOwner;
int threshold = initThreshold;
/* agent can invoke this in autonomy if spent Kas value is below threshold */
entrypoint function auto(sig agentSig) {
require(tx.outputs.length == 2);
require(tx.outputs[0].value <= threshold);
require(checkSig(agentSig, agent));
validateOutputState(1, {
agent: agent,
owner: owner,
threshold: threshold,
});
}
/* human and agent can together decide to spend more than threshold if
they both sign */
entrypoint function manual(sig agentSig, sig ownerSig) {
require(checkSig(ownerSig, owner));
require(checkSig(agentSig, agent));
}
}
It compiles to a redeem_script and hence has a dedicated P2SH address: address = P2SH(BLAKE2B(redeem_script)).
First time you hear about P2SH? read 1. and 2. here now
note: deploy a covenant
When I will send the first UTXO to this address, we effectively say I will âdeployâ the covenant, and this UTXO will be part of the UTXO set like any others, only difference is the Script Public Key (SPK) that is a P2SH instead of a âstandardâ or classic P2PK address.
The redeem_script can be summarized as:
template_prefix + encoded_state + template_suffix
Which means that any state changes will produce a new P2SH address. If we take a look at our Vault, since the state never changes, the address will remain stable across execution.
note: system should rely on CovenantID to fetch related P2SH
If we imagine a more complex script, usually it will have state changes (e.g. a token balance). For these covenants, you most certainly want to bind it to a stable identity, for this youâll make sure the script enforces CovenantID continuation.
Here are two ways of doing it with our example above:
- programmatically
/* there is exactly one covenant continuation */ require(OpAuthOutputCount(this.activeInputIndex) == 1); /* the first (and only because of previous check) output index continuing the covenant lineage */ int next_idx = OpAuthOutputIdx(this.activeInputIndex, 0); validateOutputState(next_idx, { agent: agent, owner: owner, threshold: threshold, }); - with annotations
/* here, the annotation does the same for us automatically */ #[covenant(binding = auth, from = 1, to = 1, mode = transition)] function auto(State prev_state, sig agentSig) : (State) { /* [...] */ return({ agent: prev_state.agent, owner: prev_state.owner, threshold: prev_state.threshold, }); }
A wallet later will use this stable identity to identify P2SH(s) tied to this CovenantID(s), in this example case there is only one Vault instance (/covenant) living at a time, so only one P2SH address at a time for this CovenantID.
Unless a wallet specifically knows this Vault code:
- and that wallet user somehow hints he has deployed his own Vault covenant (with provided parameters), the wallet is unable to read the current state, or know itâs even deployed.
- wallet doesnât know how to construct valid transition execution (invoke
autoormanualfunction)
So what now?
Conceptually, what existing or not yet existing network actor should have the responsibility (note: multiple actors can be involved) to:
- track covenant states, and expose them
- verify source code (vs. whatâs deployed)
- understand how to build a âcovenant-validâ transaction
I also want you to think by yourself and express more questions / challenges, share your views. I already have a biased opinion on these topics that Iâll share later.