For fun and practice I want to build a contract that picks a number between 1 and 100, and give a sender their ether back if they can guess it in 7 tries.
I'm quite sure I don't understand enough to know if this is possible, but that's my purpose for trying.
My first snag was generating a "random" number. There's an op-code for blk_nonce which sounds promising, but how is that op-code even possible?
Is that the nonce of the last block? It can't be the nonce of the "current" block -- unless it's the nonce for just the miner running this script, which might be different from miner to miner. But wouldn't that cause a blockchain fork if, for example, a script did something like "if blk_nonce is even then send ether to A, else send ether to B" ?
Clearly there's still big holes in my full understanding of all this. Basic questions for now:
- What is blk_nonce exactly?
- What other way might there be to get a secret random-like number?
3 ·
Comments
I can't think of any solution for this problem right now.
Afaik that should be fairly good psuedorandom data. *However* block miners could collude; hold back blocks that dont have the winner they want. It lowers their chance of winning a block, so it is costly, but then bets could have a lot at stake too.
This can be prevented by both parties having a secret S1,S2, initially they reveal H(S1), H(S2), then they wait a block, which has a checksum H(B). After that the 'game' or program can use the number R=H(S1 ... S2 ... H(B)) as random. Since neither knew S1,S2 of the other side, neither can collude with miners to affect R.
Still have a problem though, one of the two has to release the secret first, and the second one can then figure out what the result of the game is before he gives his secret. This can be fixed by giving forfeitures at this point the 'maximum loss' in the game, and having a time frame for the players to respond.(presumably they can get their transactions in a block by that time)
"What is blk_nonce exactly?"
From a reasonably up-to-date version of the source code we see this in VM.h (this is the "code and builds" forum after all
case Instruction::BLK_NONCE:
m_stack.push_back(_ext.previousBlock.nonce);
This is clearly the nonce of the previous block. In case the naming is just wonky (which it is, actually), here is some related code:
case Instruction::BLK_PREVHASH:
m_stack.push_back(_ext.previousBlock.hash);
break;
case Instruction::BLK_COINBASE:
m_stack.push_back((u160)_ext.currentBlock.coinbaseAddress);
break;
case Instruction::BLK_TIMESTAMP:
m_stack.push_back(_ext.currentBlock.timestamp);
break;
case Instruction::BLK_DIFFICULTY:
m_stack.push_back(_ext.currentBlock.difficulty);
So we can see that most of the BLK_ functions are referencing the current block, except for BLK_PREVHASH and BLK_NONCE. I would suggest that BLK_NONCE should really be BLK_PREVNONCE.
That's the pragmatic answer. You already hit on the theoretical answer via reductio ad absurdum with your question of "how is that op-code even possible?". It's not possible, it has to refer to the previous block.
It probably refers to the previous nonce so that miners dont have to re-run the code every nonce. The code would have run again because the outcome might change with the nonce, and other full-nodes(and miners) dont accept invalidly run code. (Another use of nonces is against replay attacks, but not here, afaik)
I agree on that the naming should be BLK_PREVNONCE.
On the larger question of how to get a random number... At first I thought the design would make this impossible on the understanding that different miners running the script with different random numbers would create blockchain forks. But that kind of fork is short-lived and anticipated -- the miner who "wins" the block will also have picked the "winning" random number.
So I'm back to thinking that a rand() function should be theoretically possible, but I haven't determined how to make one with what's currently available.
I think as the network grows this problem may be minimized since there will be so many people mining that it would be difficult to mine the block that benefits the transaction that ran the contract that the "untrustworthy" miner will try to give a non random number to. Even though it will be very difficult it would be nice if we had a full proof verifiable random number generator for our contracts.
The problem, using a single transaction is that the psuedorandom values are predicatable. Everyone can see how it would turn out, because the random seed(likely block.hash) is known by anyone.
With two transactions, you can fix it; you could have a 'lottery script' where you buy a ticket, it stores X=H(block.hash .. sender.addr), Y=block.number, then later, if someone pokes it to execute, it can use R= H(X ... blocks_list[Y+5].hash) can be used as random seeds.
But that one has the problem that miners can affect it. However, only at the cost of not using winning blocks. I'll try to figure out what sort of amounts of bets, and if many tiny bets are also cheatable later..
And that problem finally is solved by parties deciding on secrets beforehand. Thing that can still be done with just two transactions. If trust is one-way, only one side has to choose a secret. Problem is that i dont think scripts have secrets. Someone would have to run one on a centralized computer somewhere to supply proof-of-secret and the secrets as people come in.
The best solution I came up with involves two messages to the contract:
1. User picks a (possibly huge) random number "A".
2. User sends message #1 to the contract, containing a cryptographic commitment to the number he just rolled.
3. Contract stores the user's commitment
4. Contract computes a pseudo-random number "B" using prevhash and timestamp and coinbase as seed.
5. Contract stores that pseudo-random number next to the user's commitment.
6. User send message #2 to the contract, sending the number "A" he initially picked.
7. Contract verifies that the number "A" the user sent conforms to his previous commitment and uses it together with "B" to compute a random number "C".
The only problems I can see are:
1. The user can know the final random number BEFORE he can send message #2, and thus can abort the protocol at that points.
2. If the user has enough mining power, he can cheat by trying to mine a solution with a coinbase and timestamp so that the resulting random number "C" is favorable to him.
https://github.com/dennismckinnon/Ethereum-Contracts/blob/master/Dennys Lotto/Lottery.lsp#L45
You can have a single party give crypto commitments, or both, whoever gives commitments is protected, if it is ensured that forfeitures(never providing your secret) are not to the forfeitings' side benefit.
Contracts themselves cannot really protect themselves against miner collusion, though perhaps they can solicit help from non-contract parties.
Contracts are callable now, so a contract could aim to be a random function. If you can get a single contract to have a psuedorandom function, that cannot be predicted or affected, we have our random function without fuss. That was true when it was EXTRO too, i suppose, although now the contract can get paid for such a service.
@2 expands to (sload 2)
there's also [1] 0xa and [[2]] 0xb for (mstore 1 0xa) and (sstore 2 0xb) respectively, but I find readability suffers quickly when using those.
see https://github.com/ethereum/cpp-ethereum/wiki/LLL-PoC-4
and https://github.com/ethereum/cpp-ethereum/wiki/LLL-Examples-for-PoC-4
I think it is necessary to think outside the box to find a solution. On is to have a ‘trusted’ source outside ethereum.
But what if, we had a second block chain, which is very quick. This chain could be used to provide as example a timestamp. Therefore if the timestamp is not provided by the miner it could be used to create as seed number for the contract.
This would be no big deal, except if he would know which contract requested the number. With the contract he can calculate beforehand whatever the contract will do.