Crypto Zombieレッスン4-4翻訳
ちょっとわかりにくかったので整理のために翻訳してみる
cryptozombies.io
Chapter4: 乱数
よし!次はバトルロジックに向かおう
すべての良いゲームは、何らかのランダム性が必要だ。
なので、どうやってSolidityでランダム性のある数を生み出すかを見ていきましょう
本当のことを言うと、それはできない。いや、少なくとも安全にはできない。
では、なぜそうなのかを見ていこう
乱数をkeccak256で生み出す
Solidityでもっとも良いランダム性をうむソースコードはkeccak256ハッシュ関数を用いることです。
以下のようなコードで乱数を生み出すことができます
// Generate a random number between 1 and 100: uint randNonce = 0; uint random = uint(keccak256(now, msg.sender, randNonce)) % 100; randNonce++; uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;
まず、今のタイムスタンプとmsg.sender
(送り手のアドレス)を利用して、その後nonce
をインクリメントします(一つの数字は、一回だけ使われます。なので、hash関数を全く同じパラメータで2回呼び出せないようになっています)
keccak
を用いて、パラメータをランダムハッシュに変換します、その後hashをuint
に変換し、%100
を利用して、最後の二桁だけを取り出すことで、0-100までの間のランダムの数字を取得できる仕組みです
この方法は、不正を働こうとするノードからの攻撃に対して脆弱です
Ethereumにおいて、あるcontract
上である関数を呼ぶとき、あなたは、それをあるノードあるいは複数のノードにtransaction
としてネットワーク上にブロードキャストします。
ネットワーク上でそのノードは、transaction
の塊を集めて、最初にProof of Work
の数学的問題を解こうとします。
そのtransaction
のグループを、他のネットワークに対してblock
として、そのPoW
と一緒に公開します。
一度あるノードがPoW
をとくと、他のノードはそのPoWを解くことをやめて、他のノードのtransaction
のリストが正しいことを承認します。そして、そのblockを受け入れて、次のブロックを解き始めます
これは我々の乱数を悪用可能な状態にしています
例えば、コイントスのcontract
を考えてみましょう。(表が出れば二倍に、裏が出れば、全部失う。)
上の関数を使って、表と裏を表現しましょう( random >= 50
ならば表、 random < 50
ならば裏)
もしこのコードを動かしたとして、transaction
を自分のノードだけに公開し共有しないということができます。そして、コイントスの関数を走らせて、勝つか負けるかを見ることができ、自分で解いている次のブロックのtransactionに含めないことを選択することができます。そして、これを最後に勝利するまで続けることができ、次のブロックを解いて、利益を得られるのです。
一体どうすれば、Ethereum上で、乱数を安全に生成できるのでしょうか?
blockchainの中身は完全にすべての参加者に公開されているので、とても難しい問題です。そしてその回答はこのチュートリアルの範囲を超えています。
StackOverflowのスレッドでいくつか野愛でを見ることができます
一つのアイデアは、oracle
を使ってEthereumのblockchainの外側から乱数にアクセスする方法です。
もちろん、何万ものネットワーク上のEthereumのnodeが次のblockを解こうと競い合っているので、自分が次のブロックを解ける可能性はとてつもなく低いです。 とても多くの時間や計算するためのリソースをこの悪用に用いることになります。 しかし、もしこの報酬が十分に大きければ(例えば、一回勝つと100,000,000$もらえるとしたら)、攻撃するに値します。
この乱数生成はEthereum上では安全ではありませんが、私たちのランダム関数が多額の金銭を必要としない限り、ゲームのユーザーはそれを攻撃するのに十分なリソースを持たないでしょう。
我々は、単純なゲームをデモの目的でこのチュートリアルでは作っているにすぎないですし、実際のお金が関わるわけではないので、完全に安全ではないと知りながら、このシンプルな実装を使うことを受け入れることにしましょう
未来のLessonでは、oracles
(安全にEthereumの外側からデータをひっぱってくる方法)で安全な乱数をblockchainの外側から生成する方法をカバーするかもしれません。
テストコードを書いてみよう
乱数を制せ牛る関数を実装しましょう。我々のバトルの結果を決めるものです(完全には安全ではありませんが)
randNonce
をuint型で定義し、0
をsetしましょうrandModと言う名前の関数を作りましょう。
internal関数で、
uint型の
_modulusを一つ受け取って、
uint`を返り値として返します。- 関数は、最初に
randNonce
をインクリメントすべきです(randNonce++
を利用して). - 最後に、(ワンラインで、)
keccak256
ハッシュをnow
とmsg.seder
とrandNonce
を使って生成し、uint
にキャストして計算します。そして、%_modulus
として値を返却します(すごい!もしうまくいかなかったら上の説明をみてください。ロジックはとても似ています)