In a previous article, we introduced a powerful construct OP_PUSH_TX. It allows access of current transaction inside Bitcoin smart contracts, which enables many sophisticated features such as maintaining state.
sCrypt exposes the basic OP_PUSH_TX in a standard library contract Tx, which suffices for many use cases. However, as contracts become more complex, more customization is needed. For example, one may need to specify the ephemeral key k in a contract using R-Puzzle.
Tx.checkPreimageAdvanced() is another standard library function that is functionally equivalent to Tx.checkPreimage(), but exposes more parameters for users to tune. In addition to sighashPreimage in Tx, it adds more parameters to control ECDSA signing in OP_PUSH_TX.
- privKey & pubKey: ECDSA key pair
- invK: the modular inverse of k, the ephemeral key
- r: x-coordinate of R, which is kG
- rBigEndian: the signed magnitude representation of r, in big endian. Strictly speaking, this can be derived from r but it requires non-trivial computation in script. So we expose it here as an optimization to be computed off-chain
- sigHashType: SIGHASH flag used to indicate which part of the transaction is signed by the ECDSA signature
ANYONECANPAY Counter Contract
In another article, we implemented a counter contract that tracks the number of times its function has been called. One drawback of it is that the contract itself has to pay transaction fees and once its fund is depleted, the contract cannot be called again.
We improve the counter contract by taking advantage of parameter sighashType in TxAdvanced. By setting it to SIGHASH_ANYONECANPAY, we let caller of the contract pay the transaction fee by appending funding inputs to the first input unlocking counter contract. We also allow caller to send the remaining fund to a change address by appending another output. Its complete code is shown below with comments inline.
Line 20 enables SIGHASH_ANYONECANPAY flag. Line 24 checks sighashPreimage is for the current transaction as in Tx. Line 38 sets contract new balance. Line 42 and 43 add change output.
Here is code to deploy the contract and repeatedly call its function increment(). An instance of the contract with counter increment from 0 to 4 can be found: 0 -> 1 -> 2 -> 3 -> 4. Note each transaction has two inputs and two outputs, not one input and output in the original counter contract. The amount of the first input and the first output are the same, meaning counter contract’s balance is maintained. The difference of the second input and the second output covers transaction fee.
When deploying the counter contract, we set the new balance the same as the old balance. The contract can live forever as long as someone pays transaction fee to trigger it.
An interesting alternative is to require the new balance to be higher than the old balance, therefore charge caller service fees to pay the contract to perform certain kinds of computation. We will leave it to the readers to figure out what kinds of applications this enables.
Thanks go to Bill from BitShizzle for implementing the AdvancedCounter contract.