前回作成した抽選を行うスマートコントラクトの動作確認を行います。
アカウントごとの役割
アカウントの役割は次の通りです。
- MAIN ACCOUNT (eth.accounts[0])
コントラクト生成者。抽選会の主催者。 - ACCOUNT1 (eth.accounts[1])
抽選申込者1。 - ACCOUNT2 (eth.accounts[2])
抽選申込者2。 - ACCOUNT3 (eth.accounts[3])
抽選申込者3。 - ACCOUNT4 (eth.accounts[4])
マイナー。
デプロイ
まずはデプロイを行います。デプロイアカウントはMAIN ACCOUNTです。
デプロイするのはLotteryです。
[デプロイ]
デプロイ後のコントラクトの状態を確認します。
[コントラクト状態]
主催者(Owner)がMain accountで、応募者の数(Num applicants)が0であることが確認できます。
抽選会に応募
アカウント1,アカウント2、アカウント3から抽選会に応募します。
各アカウントからEnter関数を指定しトランザクションを発行します。
[アカウント1から抽選会に応募]
[アカウント2から抽選会に応募]
[アカウント3から抽選会に応募]
応募したあとのコントラクトの状態を確認します。
応募者(Applicants)は入力欄の右端にある小さい三角ボタンをクリックすると、インデックスを変えて応募者を確認することができます。
[応募1の確認]
[応募2の確認]
[応募3の確認]
応募者の人数(Num applicants)が3で、各応募者(Applicants)がアカウント1,アカウント2、アカウント3であることが確認できます。
抽選(当選者を決める)
抽選の開催者(Main Account)から、Hold関数をコールして当選者を決定します。
[開催者が当選者を決める]
コントラクトの状態を参照し、当選者を確認します。
[当選者の確認]
当選者(Winner address)が、アカウント2であることが確認できます。
また、当選者を決めるパラメータとなったTimestampは1627990495であることが分かります。
抽選時のトランザクション確認
抽選を行った時のトランザクションを確認します。
[当選者を決めた際のトランザクション]
このトランザクションは15909番目のブロックに取り込まれています。
このブロックのタイムスタンプをgethコンソールから確認します。
1 | > eth.getBlock(15909).timestamp |
15909番目のブロックのタイムスタンプは1627990495となっています。
この値は、コントラクトで抽選を行った時のタイムスタンプと同じです。
つまり、スマートコントラクトで参照しているblock.timestampはトランザクションが発行されたときのタイムスタンプではなく、ブロックに取り込まれた時のタイムスタンプだということになります。
ブロックのタイムスタンプ
ブロックのタイムスタンプはマイナーがブロックを生成する際に設定するもので、どんな値が設定されるのかはマイナー次第となります。
抽選会にマイナーが応募していたとすると、マイナーは抽選時のトランザクションを検知し自分自身が当選者となるようなタイムスタンプを設定し、ブロックを作成するということが可能です。
今回のサンプルソースは、抽選結果がタイムスタンプにより決定されるといった仕様のため、タイムスタンプを操作され当選者を自由に決められてしまうという脆弱性があったといことになります。
タイムスタンプを操作されると困るような場合は、タイムスタンプだけに依存した仕様にしないように気を付けましょう。