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:
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