Skip to content

MustPay... Generic Evals Proof of Concept

dimxy edited this page Apr 25, 2022 · 25 revisions

MustPayCC and MustPayPKH Generic Evals Description and Sample Usage

What's new

  • Updated for cc script extensions (see below)
  • More updates for cc script shared variables, python eval_assets.py demo script added, MustPayPKH eval added
  • New test chain params

Concept

MustPayCC and MustPayPKH generic evals allow to lock coins or token and spend them only if the spending transaction has a cc or normal vout satisfying the condition in the MustPay... eval parameter ('rule condition') and also has the amount denoted by the eval param script.
Additionally, MustPayCC allows to spend a cc vin by using the whole or a subset of the rule condition in the MustPayCC eval parameter.
The code is here: https://github.com/dimxy/komodo/tree/research-new-cc-param

The whole 'Generic Evals' concept is devised by Alright, a lead komodo blockchain developer.

How MustPayCC eval works:
when a vin with MustPayCC eval code is being spent, its validation code looks into the spending transaction for a cc vout which has a cryptocondition that matches the rule condition stored in the MustPayCC eval parameter.
When matching is performed the MustPayCC validation code is trying to compare the whole condition in the tx outputs or its subconditions with the rule condition in the MustPayCC eval param. (For comparison the MustPayCC rule condition and conditions or subconditions in tx outputs are converted to anonymous conditions according to the cryptocondition algorithm.
MustPayPKH works almost in the same way apart from that it searches for normal addresses in vouts instead of cryptoconditions.

CC scripting extension

A CC scripting extension has been added to the EASSETS03 chain. A CC script is a BTC-like script extended to calculate the value of cc tx output amounts. Usage of CC scripts is intended to provide more advanced logic for generic evals like MustPayToCC or MustPayToPKH.
A CC script is basically a BTC script which additionally allows to access several preloaded variables like 'cc vin amount', 'output amount' and user-defined variables (like token price or whatever) and make calculations with btc script math opcodes. The result of the cc script is always an amount value which is used by the MustPayCC eval to validate the destination output amount.
OP_MUL and OP_DIV opcodes are additionally enabled for cc scripts.
There are two kinds of cc scripts used: 'LoadScript' and 'AmountScript': A LoadScript preloads some user-defined or pre-defined variables into the cc script interpreter memory to make them accessible in the AmountScript. To create both scripts makeccscript rpc is used.

Demo of 'Assets CC' implemented purely with MustPay... evals

There is a sample python app to demonstrate how to use MustPay... evals to implement assets cc (aka tokenDEX). The app path is src/tui/eval_assets.py. It currently implements assetsask, assetsfillask and assetscancelask scenarios.
To run the sample create your tokens to use in ask and replace the path to the node config file with your own path.
Currently supported calls are:
Create ask:

python3 ./eval_assets.py "/Users/dimxy/Library/Application Support/Komodo/EASSETS03/EASSETS03.conf" assetsask --numtokens=1 --tokenid='50e728d6952e949997426158ab92f4a5b67d4f2949ebb1b9c80d91f4a875cd79' --price=0.00005

Fill ask:

python3 ./eval_assets.py "/Users/dimxy/Library/Application Support/Komodo/EASSETS03/EASSETS03.conf" assetsfillask --numtokens=1 --tokenid='50e728d6952e949997426158ab92f4a5b67d4f2949ebb1b9c80d91f4a875cd79' --txid=e39e111975bcd1032817d1d330fd99e0d39796ace9df735197cb5fdb4d21768a

Cancel ask:

python3 ./eval_assets.py "/Users/dimxy/Library/Application Support/Komodo/EASSETS03/EASSETS03.conf" assetscancelask --tokenid='50e728d6952e949997426158ab92f4a5b67d4f2949ebb1b9c80d91f4a875cd79' --txid=26fbfa177eacde50a08d1d6a900035b3083a24d9d6997f04e8cccd20a8100dd0

Use --help option to get info about eval_assets.py parameters (they are much the same as in the original tokenDEX rpc api)
Also see the eval_assets.py source code to learn how to use generic evals for tokens and assets.

There are some hints how to install the slickrpc python module required to connect to komodo rpc: see this.

Helper RPCs to work with MustPay... evals

There are several rpcs designed to help to make MustPay.. generic eval parameters and create transactions with cryptoconditions with those generic evals:

  • makeccscript make cc scripts from a more familiar infix-style expression and preloaded vars
  • makemustpayccparam makemustpaypkhparam - make eval param from scripts and a destination
  • getccevalparam get eval param for evalcode from a tx output
  • parsemustpayccevalparam parsemustpaypkhevalparam - parse mustpay... params and return their scripts
  • createccevaltx - create a tx with cc inputs and outputs
  • tokenv2getutxos - helper to get tokens cc utxos for a pubkey (like getaddressutxos) These rpcs may be used in python scripts to create transactions with generic evals.

How to create cc scripts:

The makemustpayccparam and makemustpaypkhparam rpcs accept a pair of 'cc script' parameters (namely LoadScript and AmountScript). There is a helper makeccscript rpc to create such cc scripts based on a more user friendly expression language:

~/repo/komodo/src/komodo-cli -ac_name=EASSETS02 makeccscript
usage: makeccscript 'json'
parses a json containing a list of variables accessible in the script and a cc eval expression and returns two btc-like load-variables and amount-calculation scripts
json structure: {"vars":[ {"VAR<id>":<value>}], "expr":"<expr>"}
"vars" are preloaded variables to use in the expression
"expr" is the expression itself
Operations currently available in expressions: +,-,/,*,!,<,>,<=,>=,()
User defined variables are in the format: VAR<id>, <id> must be unique for all eval params in the same scriptPubKey
Variables must be either integer amount or hex encoded data (hex data not supported yet)
VINAMOUT - preloaded value with input amount. 
VOUTAMOUT - preloaded value with output amount. 
Numeric constants are enabled as operands

Example:
Script to ensure that the cc output amount is subtracted by some value of 5500 satoshi. Let's load this into VAR1 and load VINAMOUNT var into VAR2 and use both in the output calculation expression: the script: '{"vars":[ {"VAR1": 5500}, {"VAR2": "VINAMOUNT"} ], "expr":"VAR2 - VAR1"}' By this, the amount script will ensure that the output value must be not less than 'VINAMOUNT - 5500'

~/repo/komodo/src/komodo-cli -ac_name=EASSETS03 makeccscript '{"vars":[{"VAR1":5500}, {"VAR2":"VINAMOUNT"} ], "expr":"VAR2 - VAR1"}'
{
  "LoadScript": "0101027c15ee0102eb",
  "LoadScriptDecoded": "1 5500 OP_LOAD_VAR 2 OP_LOAD_INPUT_AMOUNT",
  "AmountScript": "0102ef0101ef94",
  "AmountScriptDecoded": "2 OP_PUSH_PRELOADED_VAR 1 OP_PUSH_PRELOADED_VAR OP_SUB"
}

The created LoadScript and AmountScript now could be used in makemustpayccparam or makemustpaypkhparam

RPC to make MustPayCC eval param

komodo-cli -ac_name=EASSETS03 makemustpayccparam eval-id load-script amount-script 'cc-json' dont-check-sig - requires eval-id, load and amount scripts, condition in json and dont-check-sig (true or false). The eval-id is number to identify this eval condition (in case if there are several such evals in the scriptPubKey). Those params are put into MustPayCC eval parameter.

RPC to make MustPayPKH eval param

komodo-cli -ac_name=EASSETS03 makemustpaypkhparam eval-id load-script amount-script address - requires eval-id, load and amount scripts and address, which are put into MustPayPKH parameter

RPC to create cc transactions with conditions with generic evals

komodo-cli -ac_name=EASSETS03 createccevaltx 'tx-json' - allows to create and sign a transaction with cc vins and vouts. The rpc also will add necessary normal vins (plus txfee) if total inputs is less than outputs. tx-json param:
'{ "vins": [...], "vouts": [...], "vinccs": [...] }'

  • 'vins' - vin array in the format: '"vins":[{"hash": prev-tx-hash, "n": prev-utxo-n }, {...}]'
  • 'vouts' - vout array in the format: '"vouts":[{"nValue": satoshis, "Destination": address, "cc": condition-in-json }, {...}]'
  • 'vinccs' - array of cc used for spending cc utxos, the format is: '"vinccs": [{ "cc": condition-in-json, "sign": true/false }, {..}]'

Why do we need the 'vinccs' array of conditions? It is used to indicate which cc vins we want to sign with the pubkey in the -pubkey komodod param and which vins should not be signed at all (that is, which are locked on a generic eval cc utxo and are spent without signature): note the property "sign" which can be true or false.

Example of MustPayCC use

This simple example is to demonstrate how to lock tokens on MustPayCC eval and spend them only if the tx has a vout with a specific pubkey
To find a token utxo to spend use tokenv2getutxos to get token utxos for a tokenid and pubkey:

~/repo/komodo/src/komodo-cli -ac_name=EASSETS03 tokenv2getutxos e4acbebdc5e287a4dc809d4ee60fbdaff5f0ebbd2cc27fb0cd1cbebf56418a6e 035d3b0f2e98cf0fba19f80880ec7c08d770c6cf04aa5639bc57130d5ac54874db 1
[
  {
    "hash": "e4acbebdc5e287a4dc809d4ee60fbdaff5f0ebbd2cc27fb0cd1cbebf56418a6e",
    "n": 1,
    "satoshis": 1,
    "script": "4c654ea262a035a003800102af2e8001f58129630121035d3b0f2e98cf0fba19f80880ec7c08d770c6cf04aa5639bc57130d5ac54874db0254320000a129a5278020c440145cd59b832e38e39ed5903a8a2f4d94b4059735a160d12b06219019cea58103020000cc280402f5010121035d3b0f2e98cf0fba19f80880ec7c08d770c6cf04aa5639bc57130d5ac54874db0075"
  }
]

We found one token utxo.
Now we need to create MustPayCC eval parameter. First, create a trivial script which simply allows the output amount as it is, use makeccscript rpc:

~/repo/komodo/src/komodo-cli -ac_name=EASSETS03 makeccscript '{"vars":[{"VAR1":"VOUTAMOUNT"}], "expr":"VAR1"}'
{
  "LoadScript": "0101ec",
  "LoadScriptDecoded": "1 OP_LOAD_OUTPUT_AMOUNT_BY_DEST",
  "AmountScript": "0101ef",
  "AmountScriptDecoded": "1 OP_PUSH_PRELOADED_VAR"
}

Next step use makemustpayccparam with the scripts from above:

~/repo/komodo/src/komodo-cli -ac_name=EASSETS03 makemustpayccparam 1 0101ec 0101ef '{"type":"threshold-sha-256","threshold":2,"subfulfillments":[{"type":"eval-sha-256","code":"9Q","param":"7401e4acbebdc5e287a4dc809d4ee60fbdaff5f0ebbd2cc27fb0cd1cbebf56418a6e"},{"type":"secp256k1-sha-256","publicKey":"035d3b0f2e98cf0fba19f80880ec7c08d770c6cf04aa5639bc57130d5ac54874db"}]}' false
010101030101ec030101ef0201020202005da25ba02ea003800102af278001f581227401e4acbebdc5e287a4dc809d4ee60fbdaff5f0ebbd2cc27fb0cd1cbebf56418a6ea129a5278020c440145cd59b832e38e39ed5903a8a2f4d94b4059735a160d12b06219019cea58103020000

We just created a MustPayCC parameter with a condition (anonymised) which requires that in the spending transaction there must be an output with a eval tokens condition and secp256k1 condition with some public key we would like to. The param also has an amount of 1 satoshi which must be the value of the destination utxo which satisfies the MustPayCC param. We will use this parameter in creation of a transaction to lock tokens on the eval condition. Basically this MustPayCC param requires that this the locked token can be sent to a specific pubkey.

Now let's create a cc transaction to send tokens to MustPayCC eval (0x86). Note the eval param we made on the previous step to pass in the MustPayCC condition (with codehex 0x86) eval param:

~/repo/komodo/src/komodo-cli -ac_name=EASSETS03 createccevaltx '{"vins":[{"hash":"e4acbebdc5e287a4dc809d4ee60fbdaff5f0ebbd2cc27fb0cd1cbebf56418a6e","n":1}],"vouts":[{"nValue":1,"cc":{"type":"threshold-sha-256","threshold":2,"subfulfillments":[{"type":"eval-sha-256","codehex":"86","param":"010101030101ec030101ef0201020202005da25ba02ea003800102af278001f581227401e4acbebdc5e287a4dc809d4ee60fbdaff5f0ebbd2cc27fb0cd1cbebf56418a6ea129a5278020c440145cd59b832e38e39ed5903a8a2f4d94b4059735a160d12b06219019cea58103020000"},{"type":"eval-sha-256","codehex":"f5","param":"7401e4acbebdc5e287a4dc809d4ee60fbdaff5f0ebbd2cc27fb0cd1cbebf56418a6e"}]}}],"vinccs":[{"cc":{"type":"threshold-sha-256","threshold":2,"subfulfillments":[{"type":"secp256k1-sha-256","publicKey":"035d3b0f2e98cf0fba19f80880ec7c08d770c6cf04aa5639bc57130d5ac54874db"},{"type":"eval-sha-256","codehex":"f5","param":"7401e4acbebdc5e287a4dc809d4ee60fbdaff5f0ebbd2cc27fb0cd1cbebf56418a6e"}]},"sign":true}]}'

The result is tx in hex. Sending the tx to the chain:

~/repo/komodo/src/komodo-cli -ac_name=EASSETS02 sendrawtransaction <tx-in-hex>
b894444c00b46284bc5f8be3b342792aafce2a2499b7d1327c34a32c8c9fb348

Note the txid of the sent transaction. Also the vout number with the MustPayCC eval is 0. This will be used for the next transaction which will spend this cc output.
NOTE: you could copy the json above into an online json formatter like https://jsonformatter.curiousconcept.com/ to see it formatted.

Now let's create a transaction to spend the above MustPayCC eval utxo, this transaction 'must pay token to a cc output' with a certain pubkey what makes it satisfy the condition in the MustPayCC vin eval param:

~/repo/komodo/src/komodo-cli -ac_name=EASSETS03 createccevaltx '{"vins":[{"hash":"b894444c00b46284bc5f8be3b342792aafce2a2499b7d1327c34a32c8c9fb348","n":0}],"vouts":[{"nValue":1,"cc":{"type":"threshold-sha-256","threshold":2,"subfulfillments":[{"type":"eval-sha-256","codehex":"f5","param":"7401e4acbebdc5e287a4dc809d4ee60fbdaff5f0ebbd2cc27fb0cd1cbebf56418a6e"},{"type":"secp256k1-sha-256","publicKey":"035d3b0f2e98cf0fba19f80880ec7c08d770c6cf04aa5639bc57130d5ac54874db"}]}}],"vinccs":[{"cc":{"type":"threshold-sha-256","threshold":2,"subfulfillments":[{"type":"eval-sha-256","codehex":"f5","param":"7401e4acbebdc5e287a4dc809d4ee60fbdaff5f0ebbd2cc27fb0cd1cbebf56418a6e"},{"type":"eval-sha-256","codehex":"86"}]},"sign":false}]}'

We just made a cc transaction with a token vout having a secp256k1 condition with the same pubkey which is required in the MustPayCC param secp256k1 rule condition. You could first try to create another transaction with a different pubkey to see that such a transaction should fail.

Test Chain Parameters

EASSETS03 params (updated):
./komodod -ac_name=EASSETS03 -ac_supply=1000000000 -ac_cbmaturity=1 -ac_cc=1 -dnsseed=0 -addnode=65.21.181.77

To connect to komodo rpc from python it is required to install these py packages (you may also install them into a py virtual environment):

python3 -m pip install setuptools 
python3 -m pip install wheel 
python3 -m pip install pycurl
python3 -m pip install slick-bitcoinrpc

On ubuntu 18 I also had to install those libs before to allow the above modules to install and build:

sudo apt-get install libgnutls-dev
sudo apt-get install libpython3.8-dev libnss3 libnss3-dev
sudo apt install libcurl4-openssl-dev libssl-dev
sudo apt-get install libgnutls28-dev 
Clone this wiki locally