Just want to drop an update on progress:
Fundamentally, CashConnect uses LibAuth templates. There is an additional top-level actions
object I plan on adding which will define the “transaction shapes” (NOTE: “transaction shapes” need only define the REQUIRED inputs/outputs to satisfy the Smart Contract constraints - CashConnect does additional UTXO selection automatically and will append appropriate change outputs).
{
"entities" {
// ... Entity Names/Descriptions and their variables
},
"scripts": {
// ... CashASM Scripts
},
"actions": {
// These will define the transactions/transaction shapes available.
}
}
We do want something like this for security: Many smart contracts have a payoutLockingBytecode
or similar argument. If we allow arbitrary transactions to be defined on-the-fly (outside of the template), the script responsible for executing this payoutLockingBytecode
argument can be invoked by a malicious service to pay money to itself (i.e. the malicious service calls the RPC and provides a transaction containing a SINGLE OUTPUT that pays to itself). As we want to be able to hash templates and have wallets mark them as trusted, this presents a security risk.
Actions will allow us to constrain calling a script like this by ensuring that scripts can only be called within the context of one of these provided actions
(transaction shapes). Thus, the script executing payoutLockingBytecode
could only be called in conjunction with the appropriate input
- meaning, even if one of these templates was used by a malicious service, they would still be constrained to the intended flows defined in the template.
Using the BCH Guru contract (still forthcoming, sorry) as an example, the format I will likely give these will look as follows:
{
"entities": { ... },
"scripts": { ... },
"actions": {
"bch.create_offer": {
"name": "Create Offer",
// The key from variables that will be used to sign this transaction (this might be removed and just default to "wallet" in future. Unsure yet.
"signer": "player_1_key",
"transactions": [
{
"outputs": [
{
"script": "pvp_bch_offer.lock",
// We support CashASM in transaction fields so that amounts can be deterministic in the template itself.
"valueSatoshis": "<player_2_amount> 4000 OP_ADD",
"resolve": {
"player_1_pubkey_hash": "$(<player_1_key.public_key> OP_HASH160)",
// The below embeds the locking bytecode for the game contract as a parameter to the locking script for offer.
// There's a bit of dirtyness here that I can't see a way out of:
// For these contracts, we need the wallet to provide the resolved values for player_1_datasig.
// So, we have to give it a name, but it would be nicer if we could somehow "nest" it under player_1_datasig_hash.
"game_contract_bch_locking_bytecode": {
"script": "pvp_bch_game.lock",
"resolve": {
"player_1_prediction_raw": "$(<player_1_prediction> <player_1_prediction_nonce> OP_CAT)",
"player_1_datasig": "$(<player_1_key.schnorr_data_signature.resolved.player_1_prediction_raw>)",
"player_1_datasig_hash": "$(<resolved.player_1_datasig> OP_SHA256)",
"player_1_pubkey": "$(<player_1_key.public_key>)",
}
}
}
}
]
}
]
},
}
}
The above will actually be transpiled down a little bit: A clone of script
in each transaction will be added to the top-level scripts
object with the variables under resolve
injected. So, for example, pvp_bch_offer.lock
might become pvp_bch_offer.lock.wallet
and the following will be added to the template:
{
"scripts": {
// ...
"pvp_bch_offer.lock.wallet": {
"script": "<player_2_amount> <player_1_locking_bytecode> <resolved.player_1_pubkey_hash> <resolved.game_contract_bch_locking_bytecode> <expiry_timestamp> $(<pvp_bch_offer.bytecode>)",
"lockingType": "p2sh32"
},
"pvp_bch_game.lock.wallet": {
"script": "<player_1_locking_bytecode> <resolved.player_1_datasig_hash> <resolved.player_1_pubkey> <oracle_public_key> <oracle_fee_locking_bytecode> <guru_fee_locking_bytecode> <maturity_timestamp> $(<pvp_bch_game.bytecode>)",
"lockingType": "p2sh32"
},
},
"actions": {
"bch.create_offer": {
"name": "Create Offer",
"signer": "player_1_key",
"transactions": [
{
"outputs": [
{
"script": "pvp_bch_offer.lock.wallet",
"valueSatoshis": "<player_2_amount> 4000 OP_ADD"
}
]
}
]
},
}
}
Any of the variables we over-ride in our resolve
object (e.g. <player_1_datasig>
) will also be added to scripts and prefixed with resolved.
:
{
"scripts": {
"resolved.player_1_datasig": {
"script": "$(<player_1_key.schnorr_data_signature.player_1_prediction_raw>)"
},
}
}
These resolved
variables will also be returned in the payload when CashConnect executes an action (builds the transactions) and are often necessary so that the redeem script can be reconstructed by a Backend/Settlement Service.
If anyone has any input on this (or perhaps a cleaner way of doing it), please let me know. Otherwise, will work on getting this implemented and hopefully have a release with a Stable RPC end of this month/early next month.