Author

Topic: Playing Dice with transactions (Read 424 times)

newbie
Activity: 1
Merit: 0
January 03, 2021, 01:38:21 PM
#11
I've recently made a try at designing a similar contract.

I figured that you can use OP_SIZE to differentiate two outcomes.

Because the preimage of the hash can vary in size, we can use hash preimages to lock participants into deterministic bet outcome, and use the size of these preimages to decide who wins.

Here's what I came up with:

Code:
A contract for the "even-odd" bet on Bitcoin, realized via pre-signed transactions:

Alice and Bob both possess 1 btc each

Alice and Bob want to enter a "even-odd" bet where
odd means Alice wins 1 btc and even means Bob wins 1 btc

Alice generates secretA: a string of bytes, either 31 or 32 bytes in length
Bob generates secretB: a string of bytes, either 31 or 32 bytes in length

The length of the string is used as differentiator because there's no bit operations
in Bitcoin script, but there's OP_SIZE that allows to measure the size of data
on the stack

Notes:
    - "T" denotes the block number sufficiently in the future for participants to
       exchange data and sent tx_move_1
    - "O" denotes the number of blocks enough for Bob to send tx_move_2
    - Policy language is used here for illustration, but "sha256bet" is a special
      fragment that does not exist in miniscript - it allows two sizes for preimage
      (31 and 32) instead of just 32

Transactions:

tx_lock_A: send 1 btc to the script:
    or(thresh(pk(A),pk(B),sha256bet(secretA)),  # demand Alice's secret to be revealed
       and(after(T),pk(A)))  # allow Alice to back out of the bet after T

tx_refund_A just spends the 1 btc locked in tx_lock_A after T

tx_lock_B: send 1 btc to the script:
    or(thresh(pk(A),pk(B),sha256bet(secretA)), # here the demand is for Alice's secret, too
       and(after(T),pk(B)))  # allow Bob to back out of the bet after T

tx_refund_B just spends the 1 btc locked in tx_lock_B after T

tx_move_1: reveals secretA when spent
    - 2 intputs of 1 btc each from tx_lock_A and tx_lock_B
    - 1 output of 2 btc to the script:
        or(thresh(pk(A),pk(B),sha256bet(secretB)),  # demand Bob's secret to be revealed
           and(older(O),pk(A)))  # if Bob does not send tx_move_2 in time
                                 # (that will reveal secretB), he loses

tx_move_2: reveals secretB when spent
    - 1 intput of 2 btc from tx_move_1
    - 1 output of 2 btc to the "bet outcome" script:
        check len(secretA) in [31, 32] and len(secretB) in [31, 32]
        check sha256(secretA) and sha256(secretB) are valid with provided preimages
        If len(premage_A) + len(preimage_B) in [62, 64]
            allow spend with Alice's key (and with Bob's key after big timeout)
        If len(premage_A) + len(preimage_B) is 63
            allow spend with Bob's key (and with Alice's key after big timeout)

Procedure:
    1. Alice sends 1 btc in tx_lock_A
    2. Bob sends 1 btc in tx_lock_B
       If Bob does not send tx_lock_B before block "T", tx_refund_A can be sent
    3. Alice creates signature for tx_move_2, shares with Bob
    4. Bob creates signatures for tx_move_1, shares with Alice
    5. Alice broadcasts tx_move_1 (which reveals Alice's secret)
       If Alice does not send tx_move_1 before block "T", tx_refund_B can be sent
    6. Bob sends tx_move_2 (which reveals Bob's secret)
       or, after timeout "O", Alice takes 2 btc (either because Bob forgets to send tx_move_2,
       or because he sees that he loses anyway)
    7. If Bob has sent tx_move_2, the winner takes 2 btc according to the "bet outcome script"
       or, if the winner did not act, the other side takes 2 btc after timeout
copper member
Activity: 821
Merit: 1992
December 30, 2020, 12:59:35 PM
#10
Quote
well this is a big problem. the loser is never going to get the funds anyways so if they don't provide their seed or sign the transaction they make it so that the winner also doesn't get the funds
It is still better than in currently existing centralized solutions when casino can disappear just like that and get the funds from the player. It is of course possible to replace 2-of-2 multisig with some kind of 2-0f-3 multisig, but then a lot of trust is put into some escrow service that may or may not be honest. By freezing the funds, both players are incentivized to remain honest, as they both lose if they stop cooperating: the winner will lose the coins, but will have the proof that somebody was dishonest, so the loser will lose some reputation. And everyone else's coins will be worth more, because these coins will be thrown out of circulation.
legendary
Activity: 2114
Merit: 1293
There is trouble abrewing
December 30, 2020, 11:57:15 AM
#9
Why? We have OP_ADD that can add two numbers, so we can simply add two seeds and then hash the result.
that would work but since the numbers in OP_ADD are limited to 4 bytes and 4 bytes is considered small for seed the design can be considered a little weak.

Quote
We don't need it. By using OP_SHA256 on some number we can get some hash. All that is needed is checking if it is greater/less than some other number pushed on the stack.
you have to convert the 32 byte hash to a number and use OP_GREATERTHAN to compare them. there is no OP code for that initial conversion and has to be defined.

Quote
Why? Player and casino work together to choose the inputs, the outputs and the amounts of the same transaction. Both parties have to sign the whole transaction and agree upfront on the whole bet.
because i think that this way the design would be a lot safer since the rules are going to be enforced by the smart contract and can not be broken. the losing party can refuse to sign the transaciton too

Quote
there is also another issue here which is the revelation of seeds
Both parties can reveal hashes of their seeds. If anyone won't reveal its seed, then funds will be frozen on the winner's address forever. That's the same what will happen when they send these coins to 2-of-2 multisig address and the loser refuses to sign the transaction in favour of the winner. Funds have to be frozen to make sure they are assigned to the winner.
[/quote]
well this is a big problem. the loser is never going to get the funds anyways so if they don't provide their seed or sign the transaction they make it so that the winner also doesn't get the funds.
copper member
Activity: 821
Merit: 1992
December 30, 2020, 11:43:35 AM
#8
Quote
this needs a bunch of new OP codes not just one OP_BET
Why? We have OP_ADD that can add two numbers, so we can simply add two seeds and then hash the result. Nobody knows other party's seed, so it is safe to do so. Of course it is only a kind of thought experiment to explain what we can do if we would have unlimited script. For now I think that by having 2-of-2 multisig we can get exactly the same as by any script or any opcode we can introduce to make trustless betting and it won't be any better than that.

Quote
there is also no OP code that could take x bits out of a hash and convert it to number
We don't need it. By using OP_SHA256 on some number we can get some hash. All that is needed is checking if it is greater/less than some other number pushed on the stack. The exact number depends on house edge, amount being placed by both parties and in general casino and player can choose any target here. There is not that much different than calculating some number in range from 0 to 10000 and checking if it is less/greater than some number, just the numbers have more hex digits and are calculated in different ways allowed by the Script, that's all.

Quote
2 additional ones are also needed to check the amounts and the destinations
Why? Player and casino work together to choose the inputs, the outputs and the amounts of the same transaction. Both parties have to sign the whole transaction and agree upfront on the whole bet. If they don't, then transaction won't be created, because it needs signatures from the casino and from the player. They also decide what should be the target number and if the hash of the (for example sum) of the seeds should be above or below the target (so is the player betting HI or LO), what amount should be locked in the winning address and how likely it should be to win it by the casino or by the player.

Quote
there is also another issue here which is the revelation of seeds
Both parties can reveal hashes of their seeds. If anyone won't reveal its seed, then funds will be frozen on the winner's address forever. That's the same what will happen when they send these coins to 2-of-2 multisig address and the loser refuses to sign the transaction in favour of the winner. Funds have to be frozen to make sure they are assigned to the winner.

Quote
if the casino reveals its seed the player can take it and on his machine compute whether he can win or not
But then this transaction will be confirmed and there will be no way of changing anything.

Quote
i also don't think we need OP_CHECKSIG in this script since there is no need for any pubkeys
It should be possible to know the result of the bet without being able to get the reward. Without signature there would be a problem to determine who should show its seed first. By having signatures, any party can do it safely, because there is no way to get the reward without being the winner, even when having both seeds.
legendary
Activity: 2114
Merit: 1293
There is trouble abrewing
December 30, 2020, 08:49:56 AM
#7
Then, our winner's output can be done exactly like it is done in "provably fair" scheme. So, we have player's seed and casino's seed. We can put both seeds' hashes in the output script to make sure nobody is tampering with them. Then, we need to reveal both seeds in the input script and provide the winner's signature. For example, " OP_BET" can return some hash as a result, as it is done typically by HMAC-SHA512 or something similar, here we can construct something using what we have in the Script, without adding anything new. Then, we can simply check if that hash is higher/lower than some target and decide which public key we should left on the stack. Then, OP_CHECKSIG after that should do the thing.

this needs a bunch of new OP codes not just one OP_BET.
for starters we have no OP_hash function that takes 2 inputs like HMAC functions do. each hash removes one item from the stack and replaces it with its hash. there is also no OP code to combine the hashes either (like adding or XORing them). there is also no OP code that could take x bits out of a hash and convert it to number.
so that's at least 3 new OP codes apart from the OP_BET.
2 additional ones are also needed to check the amounts and the destinations.

there is also another issue here which is the revelation of seeds. lets assume player loses in the end. if the casino reveals its seed the player can take it and on his machine compute whether he can win or not. if he didn't he refuses to reveal his seed. of the opposite in case user revealed it and the casino was losing.
the reason why provably fair system works in centralized gambling sites is that casinos don't reveal their seed until the bet is over.

i also don't think we need OP_CHECKSIG in this script since there is no need for any pubkeys.
copper member
Activity: 821
Merit: 1992
December 30, 2020, 08:31:03 AM
#6
To see what is possible, I skip for a moment all standardness limits. Let's assume that there is OP_BET that can be used for betting (we don't need another opcode, it can be just a combination of existing ones, this new opcode is only to explain it easier). Doing it purely on-chain, both parties should create a transaction locking some amount in the winner's output. So, there should be player's input, casino's input, player's change and casino's change, and finally there should be some output spendable only by the winner. That's the minimal setup I can think of.

Then, our winner's output can be done exactly like it is done in "provably fair" scheme. So, we have player's seed and casino's seed. We can put both seeds' hashes in the output script to make sure nobody is tampering with them. Then, we need to reveal both seeds in the input script and provide the winner's signature. For example, " OP_BET" can return some hash as a result, as it is done typically by HMAC-SHA512 or something similar, here we can construct something using what we have in the Script, without adding anything new. Then, we can simply check if that hash is higher/lower than some target and decide which public key we should left on the stack. Then, OP_CHECKSIG after that should do the thing.

When we consider our OP_BET opcode's behaviour as what we need, we can make some conclusions out of that. Replacing that hypothetical OP_BET by just 2-of-2 multisig is sufficient. Rejecting to reveal the seed by any party would be the same as rejecting to sign a transaction by the loser. Assuming that we only decrease amounts from our multisig output and increase amounts on any party's output, nobody will ever publish the old state of the channel, as it will just lock more coins on 2-of-2 multisig output, so there is no reason to do so.

I think no timelock is needed here. Both parties should lock some amount and no party should have any chance of leaving the game if the initial transaction is confirmed. The only thing I worry about is that rebalancing the channel will be needed many times, because the amount on 2-of-2 multisig is just the maximum amount of coins that can be wagered. And because a player that have 1 BTC can usually wager 10 BTC or even more when playing correctly and reusing already won coins for betting, then many times both parties have to move some coins on-chain to rebalance the channel they share. But since I have no better idea, I will try to implement it.
jr. member
Activity: 32
Merit: 77
December 22, 2020, 03:32:57 PM
#5
One modification to this approach: all that is needed is single 2-of-2 multisig address, shared by both parties, so there are only two on-chain transactions needed. In the first closing transaction there should be at least one multisig input and at least three outputs: one placing almost all funds in another 2-of-2 multisig address, one owned only by the player and one owned only by the casino, both with minimum required amount above the dust limit. Then, all that is needed is decreasing amount from multisig and increasing amount in the winner's address. Then, if the loser refuse to sign the transaction, nothing will happen, but funds will still be locked in 2-of-2 multisig and parties will have enough time to cooperate. If they don't, everything they won will be returned to the winners from all honest bets and the rest will be frozen in the multisig until they start cooperating again (which could be never, but then they both lose, because initially they both put the same amount of coins in the multisig and the rest depends on winning or losing the honest bets accepted by both sides).

Also, if both parties put the same amount of coins in the multisig, then no timelock is needed, because freezing 1 BTC from casino costs freezing 1 BTC from the attacker, so it is not profitable to attack by any party. But maybe there is a way to guarantee no losses on both sides, I am still trying to figure it out.
legendary
Activity: 3472
Merit: 10611
December 11, 2020, 03:34:55 AM
#4
^ The main issue with this is that the casino after seeing the player seed and knowing that it has lost can avoid revealing its own seed and instead claim the coins with the timeout to avoid loss. Then there is the problem with the locktime too, who will it favor? If it is equal time for both parties then a race condition can happen where 2 conflicting transactions spend the same output. If it is not equal then it is unfair because the one who is capable of spending the output sooner will always have the upper hand and will always win no matter what.
copper member
Activity: 901
Merit: 2244
December 11, 2020, 03:03:12 AM
#3
Nice system!

I think that both parties can generate signatures upfront and use their hashes as seeds. Then, it can be done typically:
1) casino reveals seed hash
2) player reveals seed
3) casino then knows who is the winner, but seed alone is not sufficient, signature matching that seed is needed
4) casino reveals seed, so the player can also check who is the winner
5) now both parties know who is the winner, but none of them can move the coins alone

Then, if the loser don't want to show the signature, the bet is cancelled. But still: it is impossible to continue playing unless that conflict will be somehow resolved. Both parties can still cooperate and agree on their balances. Both parties can publish the latest state on-chain and left the game. One party can still wait for the timeout and then collect the latest state from one output and almost everything from another output if another party will vanish. And when one party decide to left the game, the other party (and nobody else) can also publish the latest transaction related to their output, and then game will be closed as if the latest dishonest bet never happened.

I am still trying to figure out if it is somehow possible to force to reveal the loser signature without revealing the winner signature while checking who is the winner.
copper member
Activity: 2856
Merit: 3071
https://bit.ly/387FXHi lightning theory
December 10, 2020, 01:12:28 PM
#2
Does each side need some sort of "maintenance limit" too though then above the dust limit to ensure the user is missing something if they refuse to sign the transaction.

For example, someone gambles with 10mbtc and lose all of it, there's no reason they'd sign a new transaction rather than chancing their luck with the htlc (timelocked transaction). I'm not sure how high you'd place this margin at though...
jr. member
Activity: 32
Merit: 77
December 09, 2020, 01:58:47 PM
#1
Doing everything on-chain is not scalable. But doing everything off-chain means it is usually centralized, because you have to deposit coins somewhere, then play without touching any real coins, and then finally withdraw it from somewhere. There are lots of gambling sites and almost all of them already implemented "provably fair" mechanism to show to the user that all bets are honest. However, from time to time these sites are not protected enough from being down for a while, and some of them are just scams created to steal coins from users by allowing all deposits but no withdraws, so I started wondering if there is a pure P2P solution for trustless betting by making transactions that can scale properly.

First of all, at least three on-chain transactions are needed to make it. One that deposit casino and player coins to two separated 2-of-2 multisig outputs and two separate transactions that withdraw each of these coins to some address selected by the casino and the player respectively. These two transactions can be prepared and signed upfront, because Segwit allows us to attach signatures without changing transaction inputs' hashes. These two transactions have to use some timelock, set for example to two weeks, one month or more time in the future (it depends how long the casino and the player would like to freeze their coins for betting, but it should be quite long time and quite high amount to scale properly). At first, these two transactions should be signed by both parties and kept by each of them to allow getting the funds in case the other party will be gone in the future.

Then, the player and the casino can safely sign the first transaction and broadcast it on-chain. When it will get at least one confirmation, the game can be started. Each party has its own 2-of-2 multisig input that provides liquidity. If casino wins, then the user signs a transaction spending the first multisig output that gives the casino more coins and just change numbers in both outputs. If player wins, then casino does the same in favor of the player, but with the second multisig output. In this way, both parties receive transactions related to separate 2-of-2 multisig outputs and in this way no watchtower is ever needed, and the whole system is safe as long as the final transaction will be broadcasted before timeout from the first transaction will expire.

Because each party will sign only transactions giving other party more coins, there will be no reason (or possibility) to publish the old state of the channel (except publishing the first transaction). The timelock (and the other party signature) in the first transaction is only needed, because in other case there will be no way to get the coins from both outputs back if the other party will be gone. But since both parties will know about it upfront, both of them will be prepared for it and withdraw the latest transaction (that will always be the most profitable for them) before the timelock expiration (especially if it will be quite long, it should give them enough time to get it confirmed cheaply when the mempool will be almost empty). And if some party will be gone, it will be no worse than in bidirectional LN channels.
Jump to: