MW scripts
NOTE: Even if this holds any grounds, I don't believe it's a good fit for Grin for many reasons, but it may be something other projects could research further.
The grand MW formula that proves the spending history was valid and that there's no inflation is
Σutxo = Σkernel + offset*G + height*reward*H
The reason this proves the validity of all past transactions is because all relevant spending conditions are captured in the kernels which are ever growing. To remove an element from the left-hand side of the equation (a UTXO), you'd need to cancel out its blinding factor through a new kernel. I think this same strategy could be used for general spending conditions.
Below is a way to introduce a more powerful scripting variant whose correctness of spending history is verified by the grand MW formula. As mentioned above, the formula needs to capture all the outputs created and spent by valid spending scripts which preserves the security model.
Since the equation uses the ECC algebra, in order to do this, we have to map the scripts to curve points. We achieve this by mapping a script to a NUMS point which also helps us avoid any potential inflation vectors. The main idea is that when we create an output with a spending condition of a merkle tree root hash root_hash
, we compute its corresponding NUMS mapping as P = to_nums(root_hash)
. To keep the formula balanced, we also create a new type of kernel KernelNUMSAdd
that has a field hash
which gets populated with root_hash
and the verifier adds to_nums(hash)
to the grand formula to keep the transaction and the grand equation balanced. We now have the grand equation balanced when we create a script output. Now we want to make sure that the output can't possibly be spent without a valid spending condition being met. To achieve this, when we spend an output, we also require a new type of kernel KernelNUMSSubtract
that has two fields:
proof
which is an inclusion proof showing a spending condition forroot_hash
input
which is an input parameter to the revealed spending condition
The verifier validates the spending condition and subtracts to_nums(root_hash)
. Essentially we have the following formula which prevents any manipulation and proves that all past spending scripts were valid without having to retain anything beyond kernels (which now include scripts as NUMS points as well as their valid spending proofs). We can think of the grand formula as being extended to
Σutxo + Σutxo_nums = Σkernel + Σkernel_nums + offset*G + height*reward*H
It is impossible to spend an output without showing a kernel with its spending condition even in the case of a long reorg. This is because, like output creation/spending, the script creation/spending is equally balanced on both sides of the equation. When we create an output with a blinding factor r1
, we add r1
to both left and right side of the equation. When we spend an output we remove r1
from the left side and subtract it from the right side which cancels the previous r1
and thus keeps the equation balanced. Similarly with scripts, when we add a script output we add a specific NUMS to the left and right side of the equation and remove that NUMS from the left side and subtract it from the right side when that output is spent. This means that if someone wanted to spend a script output without revealing a script condition, the formula would not balance out because we would no longer have the NUMS point in the UTXO set, but we would have it cancelled out on the right side of the equation and thus the formula would not be balanced. A new verifier can check the whole history of spending conditions was valid.
This approach keeps the kernels "self-isolated" meaning that to validate a kernel, we don't need any additional data other than what is available on the kernel.
NITX example
Suppose we create an output whose one of the spending conditions is to reveal a signature with a specific pubkey A
with the message being the kernel curve point. To spend this output, we create a new KernelNUMSSubtract
kernel that reveals the inclusion proof for that spending condition script and we give the signature as the input param for that script. Since the script can include a public key of other parties, this allows noninteractive transactions given that the parties somehow communicate the blinding factor and the value of the output spent.