After presenting the construction of the MRTTREE smart contract in part 1 we can now proceed to show how to use it to build split tickets.
Funding and Purchasing the Ticket
A Loop Out MRTTREE construction is used to perform the funding for the ticket.
In the first coordination step, users and provider build the utxo that will be used as input to the ticket being purchased. Given this is a Loop Out, the provider requires both the off-chain inbound bandwidth (to receive the off-chain contributions) and on-chain coins under its control (that it will grant to the group of users).
Concurrently to the funding of this utxo, the ticket transaction is built. It has only a single input spending directly from the MRTTREE utxo.
Note that the spend path of the newly built MRTTREE utxo and the ticket should have no timelock while redeeming via the tree of transactions should have an initial timelock to discourage all parties from going on-chain.
The redeem path should also have an extra conditional branch to allow the provider to re-aggregate the on-chain funds in case some of the users fail to cooperate but the users that cooperated in the first step do not want to retrieve the funds back on-chain. This path should have a timelock between the ticket and the completely uncooperative cases.
The group key used to make the ticket valid should also include a key from the provider. While the users are paying the provider off-chain, they also submit their individual signature to the ticket. After all users send their payments, the provider adds its signature and broadcasts the ticket.
Users are still assured the claim for their share of the funds is true via the tree of transactions.
The voting rights for the ticket can be decided in any number of ways.
For a small number of participants and assuming a trusted VSP where all users have an account, the current method used for the existing split ticket functionality can be used where users pre-commit to the voter selection, all possible tickets (with each user’s voting address) are signed by everyone and during the final step users discover the correct voter. Any failure by the matcher to publish the correct ticket should be prominently notified by users to the larger Decred community.
Another alternative is to have the matcher service (ran by the VSP) generate the voting address itself, then share it only with the user that has won the voter selection lottery. The user can attest that it has received the private key corresponding to that by signing a message with a group key involving the just released key and the key used to sign the ticket transaction.
Note that this requires users to sign only a single ticket transaction. However this also requires the ability to use non-persistent (and non-repeating) voting keys with VSPs.
It’s an open question whether we can devise a protocol that:
- Allows users to sign a single ticket transaction, committing to that voting address;
- Allows the selection of the voting user in a weighed manner;
- While still not allowing non-voting users to terminate the protocol early, before the ticket is fully signed;
- And not requiring a trusted third party (VSP) to hold the key before the selected voter is actually revealed.
Commitment Address & Handling Reward
The reward (or ticket) commitment address is the address which is added as output to vote and revoke transactions and receives the original funds plus the stake base reward in case of votes.
When using the proposed solution for split tickets, the commitment address should be that of the root uxto of a Loop In MRTTREE, which allows the original financers to redeem their funds on-chain if necessary or preferably off-chain with cooperation of the provider.
However there are two main challenges to dealing with the return amount: how to allow a pre-built tree of transactions to redeem the funds given the vote hash is unknown at financing time and to how to allow it to redeem the funds plus the reward given that the reward amount is also unknown.
Rebinding the Root UTXO
The transaction signature verified to ensure an input (and therefore the transaction it’s included in) is valid is done over the hash of the utxo being spent and is called the previous outpoint.
Revocations spending from a ticket can be created ahead of time and a corresponding tree of transactions can be pre-signed during the funding of the ticket. Consensus rules ensure the revoke is only valid once the ticket is either missed or expires.
On the other hand users can’t pre-sign the tree of transactions that spend from a vote given that at funding time the hash of the vote transaction is unknowable. Among other things, it depends the hash of the block immediately preceding it (the block being approved/disapproved) and therefore we can’t bind any new transactions to a vote ahead of time.
In this case, users would be obligated to coordinate after the vote to create the tree of transactions and redeem the funds and any uncooperative user could completely lock the funds of all other participants.
The solution to this is to use
SIGHASH_NOINPUT, which is a signature hash type proposed for a new type of Lightning Network (Eltoo) and can be used as a way to “rebind” the tree of transactions to a vote or revoke transaction.
A quick recap of what
SIGHASH_NOINPUT does: when using this hash type, the previous outpoint is zeroed before being concatenated into the transaction hash calculated for signature creation and verification. This allows a correctly signed transaction to spend funds from any previous output, as long as the pkscript of that output is correctly redeemable by the signature script1
Using this sighash mode the pre-created tree of transactions can spend from the vote utxo without requiring it to have a specific transaction hash or output value and users can again be sure they’ll be able to reclaim their share of the funding even if no other cooperation round happens.
Rebinding the Output Value
SIGHASH_NOINPUT allows users to publish the MRTTREE transactions to redeem their original funds. There’s still the question of the stakebase reward in the case of votes though.
Besides committing to the previous outputs, the signature hash also includes the current outputs of the transaction. This means that a pre-signed transaction sending (for example) 1 DCR to a given address cannot be changed to now send 2 DCR to the same address even if all other things about the transaction were valid and it still carried a reasonable fee.
Similarly to how malleability was added back to inputs, we can add it to output values (though obviously not to the other output fields) by introducing yet another signature hash flag, tentatively called
Intuitively, this mode zeroes the value from outputs when calculating the hash for signature creation and verification and therefore allows the transaction to be broadcast with any output value, allowing any user to publish the root, branches and leaf MRTTREE transaction with the reward added to original funding amount.
Similarly to the
NOINPUT mode however, this adds too much malleability: a malicious PoW miner could simply replace the value of the outputs with the minimum amount possible and swap the rest of the funds as transaction tree.
And even if the miner didn’t do this, users could themselves change the amounts in intermediate and leaf outputs such that their preferred output got the absolute majority of the funds.
This forces the addition of another opcode, tentatively named
OP_CHECKOUTPUTPERCENTAGE. The semantics of this opcode would roughly work as follows:
- It pops two values from the stack;
- Assumes the first one is an output index;
- Assumes the second one is a decimal2 representing an appropriately coded percentage;
OP_TRUEto the stack if the output at the given index has a value greater than or equal to the given percentage of the input value of the script being evaluated;
OP_FALSEin all other cases and failure modes3 .
With this opcode we can add a conditional check in every transaction in the MRTTREE to ensure that their outputs are carrying appropriate minimum values which correspond to a percentage of the total funds.
There would need to be some allowance for miner transaction fees and any miner could always rewrite the transaction to extract as much fee as possible given the input amount and output percentages enforced, however assuming the on-chain fees remain consistently low (close to the minimum mempool relay fee) then users can calculate the minimum possible reward in case of votes during the initial funding such that the enforced percentages only allow the smallest fee to be extracted.
Politeia Voting Rights
Politeia voting rights currently rely on the address recorded in the largest commitment output by value being able to sign a message to signal the vote choice.
This is currently incompatible to P2SH addresses needed for the MRTTREE structure however, therefore split tickets built this way would be unable to participate in votes.
In order to allow participation and in particular to prevent influence amplification scenarios, the layout of ticket transactions would need to be changed to include a special Politeia voting right address and the determination of who controls this address would need to be done in a manner similar to the on-chain voting rights attribution.
The changes on the layout of ticket transactions will be the subject of the next post.
Assuming the VSP is involved in assembling the split ticket, then pool fees can be paid directly via LN instead of having to be paid on-chain. This can be done either during the build up of the ticket, as a direct fee paid to the VSP or by conceding some of the funds in the MRTTREE constructed for the ticket commitment to the VSP.
If the VSP is also the provider of the on-chain funds used to build the ticket, it also makes sense to use it as the provider to redeem the returned funds therefore relying on only a single third party, which users implicitly trust given they are willing to allow it to cast votes in their behalf.
Another possible future benefit of moving split tickets off-chain is that they become more similar to regular solo voter tickets.
This could possibly allow the funding transaction to be used with Decred’s CSPP service so that split tickets could also make use of the extended privacy granted by participating in these sessions.
Along with building the MRTTREE anonymously this would grant a great degree of privacy even for users unable to participate in the full ticket version of the ticket buying CSPP.
Note that it would take additional changes to how public key scripts are encoded and how the signature scripts for regular solo voter tickets are issued in order to completely obfuscate the fact that a particular vote is for a solo vs multiple original funders.
As a quick preliminary hint of the work involved, there’d have to be no difference between P2PKH and P2SH pkscripts and ideally a feature such as TAPROOT would need to be used by all voters such that eventually redeeming the funds off-chain does not leak information that there was the on-chain tree of transactions available.
Disadvantages and Sourcing of the funding UTXO
The biggest disadvantage of split tickets done over LN is the fact that it takes at least twice as much funds during the interaction rounds to purchase and to redeem the returned amounts versus the on-chain version.
This follows from the fact that the on-chain provider of the UTXO spent to buy the ticket needs to have both the on-chain funds (in previously unused outputs) and off-chain inbound bandwidth (implying other nodes opened channels to it).
In the scenario where the VSP is working as on-chain provider, users could maintain long-lived channels opened to it such that their funds are initially spent to purchase the ticket and then return (via the same channel) to them.
VSPs would still need to commit at least some funds on their end to be able to send the additional reward for votes, though given the diminishing returns offered by staking this can probably be an order of magnitude less than the funds used by the user. For example, VSPs could have a policy of opening a channel with 0.1 DCR of outbound bandwidth for every 10 DCR of inbound bandwidth users provide it.
The on-chain funds used by the VSP would also need to either be directly under the VSP control (from previous ticket purchases) or be sourced during the construction of the ticket (for example by running a DEX node and swapping some other coin for on-chain liquidity).
Nevertheless, this does represent the major drawback for this construction method. Given this situation, we should probably not completely eliminate the possibility of on-chain splits.
Another considerable difficulty is the coordination required to fund and assemble the tickets and to eventually redeem them. Experience with the existing split ticket service shows that gathering enough people to fund the ticket is difficult enough and adding the requirement of gathering even more funds (on previously opened channels and to directly publish the initial transactions) will probably represent the biggest barrier for actually using this in practice.
Split tickets done over the Lightning Network open up the opportunity of stakeholders with very little amount of dcr to participate in the staking process by massively reducing fees while still maintaining the safety and non-custodial aspects of the on-chain splits and reducing the on-chain footprint as a bonus.
The next post will propose some changes to the layout of ticket transactions meant to eliminate features that become redundant given the LN-based splits and that are mostly no longer used. It will also suggest how to deal with the Politeia voting rights problem.
↩ Note that the `SIGHASH_NOINPUT` mode has some transaction malleability and can cause replay attacks on funds, therefore individual users **must** ensure any keys contributed towards the group key are used only once (which should be a standard practice anyway for privacy reasons). A single honest user using a unique key is sufficient to ensure the group key is not repeated (assuming a safe group key construction protocol like MuSig is used). However every user should generate unique keys as a defense against a colluding group of users and malicious provider replaying the redeeming transaction of a different MRTTREE session.
↩ This could be a fixed-point decimal or use some other representation. A good enough resolution to target in the context of split tickets is to be able to use a percentage that encodes a 1 atom participation in the maximum stable-state ticket price (~ 512.7 DCR) or some other similar number.
↩ Such as the output index is out of bounds, etc. There are _plenty_ of possible failure modes both directly in the execution of the opcode and in the higher level context of the script (such as having multiple instances of this opcode which try to redeem an amount larger than exists in the input). All of these need to be checked by verifying the full redeem script before users sign the corresponding funding transaction.