Ethereum(15) - クラウドファンディング用のスマートコントラクト(キャンペーン失敗)

前回、Mist Walletを使って、キャンペーンが成功するケースの動作確認を行いました。

今回はキャンペーンが失敗するケース、つまり投資総額が目標金額に達しなかった場合の動作確認を行います。

デプロイ時の設定

デプロイ時(コンストラクタのコール時)の設定値は下記の通りです。

  • FROM
    Main Accountを選択
  • SELECT CONTRACT TO DEPLOY
    ‘Crowd Funding’を選択。
  • _duration
    テスト用に15分とする。秒指定のため 900 を設定。
  • _goal amount
    目標額を20etherとする。
    Mist Walletで設定する場合はwei表記のため 20000000000000000000 を設定。

[デプロイ時の設定値イメージ]

アカウントごとの役割

アカウントの役割は次のようにします。

  • MAIN ACCOUNT (eth.accounts[0])
    オーナー
  • ACCOUNT1 (eth.accounts[1])
    投資家1
  • ACCOUNT3 (eth.accounts[3])
    マイナー(マイニングするアカウント)

キャンペーンに失敗するケース

Mist Walletを使って全ての操作と確認を行っていきます。

まずコントラクトの状態を確認します。

[コンストラクト]

まだ投資総額(Total Amount)が0であることや、キャンペーンが終了していないことが確認できます。


次に、アカウントの残高を確認します。

[残高]

赤枠で囲まれたアカウントが、スマートコントラクタの動作確認に使用するアドレスです。

投資

投資家1から、fund関数を選択しコントラクトに19ether投資します。

[投資1]



確認画面ではパスワードを入力します。

コントラクタの状態確認

コントラクトの状態を確認します。

投資総額(Total Amount)が19etherになっていることと、まだ締め切り期限になっていないことが分かります。

[コントラクト]

キャンペーンの終了

締め切り期限が過ぎるのを待って、オーナーからcheckGoalReached関数を呼び出しキャンペーンを終了させます。

[キャンペーンの終了]



確認画面ではパスワードを入力します。


コントラクトの状態を確認します。

[コントラクト]

キャンペーンが終了しており、statusからキャンペーンが失敗していることを確認できます。


最後に各アカウントの残高を確認します。

[残高]

投資家1のアカウントからは、投資した額の19etherが戻されているためもとの残高に戻っています。

オーナーの残高もクラウドファンディングの金額が振り込まれることなく、残高に増減はありません。

想定通りの結果となりました。

Ethereum(14) - クラウドファンディング用のスマートコントラクト(キャンペーン成功・4回目)

前回、Mist Walletを使って、キャンペーンが成功するケースの動作確認を行いました。

ほとんどうまくいったのですが、最後にキャンペーンが終了できないという問題にぶつかりました。

キャンペーンが終了できない原因

キャンペーンが終了できない原因ですが、とても単純なことでした。

コンストラクトをデプロイする時にMain AccountではなくAccount2を間違って指定していたのです。

キャンペーンの終了は、オーナー(デプロイしたアカウント)だけが行えるようにコーディングしているので、それ以外のユーザではエラーになっていたという訳です。

凡ミスですね。

これまで発生した問題まとめ

これまでにつまづいた問題と対応は以下の通りです。

  • 終了期限の単位を勘違い
    (対策)分単位ではなく秒単位で指定する。
  • マイニングしてしまって残高の増減が分からなくなった
    (対策)オーナー、投資家以外のアカウントでマイニングを行う。
  • コンストラクタのデプロイユーザ間違い
    (対策)デプロイ時のFROMにMain Accountを指定する。

以上、3点に気を付けながらもう一度、キャンペーン成功のケースを実行していきます。

デプロイ時の設定

デプロイ時(コンストラクタのコール時)の設定値は下記のようになります。

各処理を実行してブロックに取り込まれるまでに意外と時間がかかるので、期限(duration)を15分に増やしました。

  • FROM
    Main Accountを選択
  • SELECT CONTRACT TO DEPLOY
    ‘Crowd Funding’を選択。
  • _duration
    テスト用に15分とする。秒指定のため 900 を設定。
  • _goal amount
    目標額を20etherとする。
    Mist Walletで設定する場合はwei表記のため 20000000000000000000 を設定。

[デプロイ時の設定値イメージ]

アカウントごとの役割

4つのアカウントの役割は前回同様です。

  • MAIN ACCOUNT (eth.accounts[0])
    オーナー
  • ACCOUNT1 (eth.accounts[1])
    投資家1
  • ACCOUNT2 (eth.accounts[2])
    投資家2
  • ACCOUNT3 (eth.accounts[3])
    マイナー(マイニングするアカウント)

キャンペーンに成功するケース(4回目)

Mist Walletを使って全ての操作と確認を行っていきます。

まずコントラクトの状態を確認します。

[コンストラクト]

まだ投資総額(Total Amount)が0であることや、キャンペーンが終了していないことが確認できます。


次に、アカウントの残高を確認します。

[残高]

赤枠で囲まれたアカウントが今回使用するアドレスです。

投資

投資家1から、fund関数を選択しコントラクトに10ether投資します。

確認画面ではパスワードを入力します。

[投資1]


投資家2からは、fund関数を選択しコントラクトに20ether投資します。

確認画面ではパスワードを入力します。

[投資2]

コントラクタの状態確認

コントラクトの状態を確認します。

投資総額(Total Amount)が30etherになっていることと、まだ締め切り期限になっていないことが分かります。

[コントラクト]

キャンペーンの終了

締め切り期限が過ぎるのを待って、オーナーからcheckGoalReached関数を呼び出しキャンペーンを終了させます。

[キャンペーンの終了]


コントラクトの状態を確認します。

[コントラクト]

キャンペーンが終了しており、statusからキャンペーンが成功していることを確認できます。


最後に各アカウントの残高を確認します。

[残高]

投資家1のアカウントからは投資した額の10etherが減り、投資家2のアカウントからは投資した額の20etherが減っています。

オーナーの残高にはクラウドファンディングで成功した額(30ether)が増えていることが分かります。

今回は無事にキャンペーンを成功させることができました😅


Mist Walletを使って全ての操作を行いましたが、gethコマンドを使うより操作が簡単ですし、情報をまとめて見れるので便利だと思いました。


次回は、キャンペーンに失敗するケースを試したいと思います。

Ethereum(13) - クラウドファンディング用のスマートコントラクト(キャンペーン成功・3回目)

前回、クラウドファンディング用のスマートコントラクトに対して、キャンペーンが成功するケースの動作確認ができました。

ただ、オーナーアカウントでマイニングをしていたために、キャンペーンが成功していくらetherが増えたのかを確認できませんでした。

今回は、オーナーと投資家以外のアカウントを使ってマイニングを行い、キャンペーン成功でどのくらいetherが増えたのかを確認します。

デプロイ時の設定

デプロイ時(コンストラクタのコール時)の設定値は前回と同様です。

  • SELECT CONTRACT TO DEPLOY
    ‘Crowd Funding’を選択。
  • _duration
    テスト用に10分に設定。秒指定のため 600 を設定。
  • _goal amount
    目標額に20etherを設定。
    Mist Walletで設定する場合はwei表記のため 20000000000000000000 を設定。

[デプロイ時の設定値イメージ]

アカウントごとの役割

3つのアカウントの役割は前回同様です。

今回は3つのアカウントとは別にマイニングするアカウント(ACCOUNT3)を用意しました。

  • MAIN ACCOUNT (eth.accounts[0])
    オーナー
  • ACCOUNT1 (eth.accounts[1])
    投資家1
  • ACCOUNT2 (eth.accounts[2])
    投資家2
  • ACCOUNT3 (eth.accounts[3])
    マイニングする

キャンペーンに成功するケース(3回目)

これまではgethコンソールを使って動作確認していましたが、今回は試しに全ての操作をMist Walletを使って行ってみます。

まずコントラクトの状態を確認します。

[コンストラクト]

まだ投資総額(Total Amount)が0であることや、キャンペーンが終了していないことが確認できます。


次に、アカウントごとの残高を確認します。

[残高]

赤枠で囲まれたアカウントが今回使用するアドレスです。

投資

投資家1から、fund関数を選択しコントラクトに10ether投資します。

確認画面ではパスワードを入力します。

[投資1]


投資家2からも、fund関数を選択しコントラクトに10ether投資します。

確認画面ではパスワードを入力します。

[投資2]


コントラクトの状態を確認します。

投資総額(Total Amount)が20etherになっていることと、もう締め切り期限になっていることが分かります。

[コントラクト]

キャンペーンの終了

締め切り期限が過ぎているので、オーナーからcheckGoalReached関数を呼び出しキャンペーンを終了させます。

(確認画面でエラーが表示されているのが気になりますが・・・・)

[キャンペーンの終了]


コントラクトの状態を確認します。

[コントラクト]

・・・だめですね。

キャンペーンが終了になっていません。(Endedが「No」でStatusが「Funding」のまま)

関数の選択(checkGoalReached)アカウントの選択(MAIN ACCOUNT)は間違っていないはずなんですが・・・・


次回はこの「キャンペーンが終了しない」問題を調査・解決したいと思います。

Ethereum(12) - クラウドファンディング用のスマートコントラクト(キャンペーン成功・2回目)

前回、クラウドファンディング用のスマートコントラクトに対して、キャンペーンが成功するケースでテストしてみましたが、うまく動作しませんでした。

今回はその原因を調べてみます。

原因調査①

設定値を見直したところ1点、勘違いしていたことが分かりました。

それは設定値の締め切り(deadline)の単位は分ではなく秒ということです。

とりあえずこれだけ変更してもう一度トライしてみます。

デプロイ時の設定

デプロイ時(コンストラクタのコール時)の設定値は下記のように設定しました。

  • SELECT CONTRACT TO DEPLOY
    ‘Crowd Funding’を選択。
  • _duration
    テスト用に10分に設定。秒指定のため 600 を設定。
  • _goal amount
    目標額に20etherを設定。
    Mist Walletで設定する場合はwei表記のため 20000000000000000000 を設定。

[デプロイ時の設定値イメージ]

アカウントごとの役割

3つのアカウントの役割は前回同様です。

  • MAIN ACCOUNT (eth.accounts[0])
    オーナー
  • ACCOUNT1 (eth.accounts[1])
    投資家1
  • ACCOUNT2 (eth.accounts[2])
    投資家2

キャンペーンに成功するケース(2回目)

geth上でcfという変数に、スマートコントラクトを割り当てます。

アドレスとインタフェースはMist Walletから取得しておきます。

[コマンド]

1
var cf = eth.contract([ { "constant": false, "inputs": [], "name": "checkGoalReached", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "ended", "outputs": [ { "name": "", "type": "bool", "value": false } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "numInvestors", "outputs": [ { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "totalAmount", "outputs": [ { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "status", "outputs": [ { "name": "", "type": "string", "value": "Funding" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "goalAmount", "outputs": [ { "name": "", "type": "uint256", "value": "20000000000000000000" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "deadline", "outputs": [ { "name": "", "type": "uint256", "value": "1625372072" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "", "type": "uint256" } ], "name": "investors", "outputs": [ { "name": "addr", "type": "address", "value": "0x0000000000000000000000000000000000000000" }, { "name": "amount", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "kill", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "owner", "outputs": [ { "name": "", "type": "address", "value": "0xec3b01f36b44182746ca109230567c4915512e35" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "fund", "outputs": [], "payable": true, "type": "function" }, { "inputs": [ { "name": "_duration", "type": "uint256", "index": 0, "typeShort": "uint", "bits": "256", "displayName": "&thinsp;<span class=\"punctuation\">_</span>&thinsp;duration", "template": "elements_input_uint", "value": "600" }, { "name": "_goalAmount", "type": "uint256", "index": 1, "typeShort": "uint", "bits": "256", "displayName": "&thinsp;<span class=\"punctuation\">_</span>&thinsp;goal Amount", "template": "elements_input_uint", "value": "20000000000000000000" } ], "payable": false, "type": "constructor" } ]).at('0xADd563073f3178AbFCDbDd3Ec707e5cd0D280f97')


deadline(終了時間)とキャンペーンのステータスを確認します。

[gethコンソール]

1
2
3
4
5
> cf.deadline()
1625372072

> cf.ended()
false


fund関数をトランザクションで呼び出します。

投資家Aと投資家Bからそれぞれ10etherを送金します。

アカウントごとにロックを解除する必要があります。

[gethコンソール]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> personal.unlockAccount(eth.accounts[1])
Unlock account 0x63f25b9bbd974fdfa07477cb99c60d2073cfe560
Passphrase:
true

> cf.fund.sendTransaction({from:eth.accounts[1], gas:5000000, value:web3.toWei(10, "ether")})
"0x8895b8f141d011453478fdfa1da51e69814cbd36e171faa60796bc8512b2eebb"

> personal.unlockAccount(eth.accounts[2])
Unlock account 0xd5adf1e9fbc1ed869ed4c7372961238fddc760a5
Passphrase:
true

> cf.fund.sendTransaction({from:eth.accounts[2], gas:5000000, value:web3.toWei(10, "ether")})
"0x7326a3446fec2628b87e1c5c18656be47a5bca238326c73834c25b5281f59b00"


投資家Aからの投資額を確認します。

[gethコンソール]

1
2
3
4
5
> cf.investors(0)[0]
"0x63f25b9bbd974fdfa07477cb99c60d2073cfe560"

> web3.fromWei(cf.investors(0)[1], "ether")
10


投資家Bからの投資額を確認します。

[gethコンソール]

1
2
3
4
5
> cf.investors(1)[0]
"0x0000000000000000000000000000000000000000"

> web3.fromWei(cf.investors(1)[1], "ether")
10

それぞれ10etherずつ投資していることが確認できます。


投資の総額を確認します。

[gethコンソール]

1
2
> web3.fromWei(cf.totalAmount(), "ether")
20


コントラクトの残高を確認します。

[gethコンソール]

1
2
> web3.fromWei(eth.getBalance(cf.address), "ether")
20

目標金額の20etherに達していることが確認できます。


キャンペーンが終了する前のオーナーの残高を確認しておきます。

[gethコンソール]

1
2
> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
33734.985904966


あとは、終了時間まで待ってcheckGoalReached関数を呼び出せば、投資された20etherがオーナーのアドレスに振り込まれるはずです。

Mist Walletの画面でコントラクトを表示するとあと何分後かを簡単に確認できます。

[コントラクト確認画面]

キャンペーンの結果確認

キャンペーンが成功したかどうか(目標金額に到達したかどうか)を確認します。

checkGoalReached関数を実行するためには手数料(gas)が必要となりますので、オーナーのアカウントをアンロックしておく必要があります。

[gethコンソール]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> personal.unlockAccount(eth.accounts[0])
Unlock account 0xec3b01f36b44182746ca109230567c4915512e35
Passphrase:
true

> cf.checkGoalReached.sendTransaction({from:eth.accounts[0], gas:5000000})
"0x674aae7f69819ec1e4112bd4694bb027d35a9c8f60b47eb160bcc5073e149878"

> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
33834.985904966

> cf.ended()
true

> cf.status()
"Campaign Succeeded"

Mist Walletでも結果を確認してみます。

[キャンペーン成功結果]

今回はうまくいったようです。

キャンペーンが正常に終了し、オーナーのアドレスが20ether増えて・・・あ、失敗しました😨😨😨

オーナーアカウントでマイニングしていたために、キャンペーンの成功でetherがいくら増えたのか確認できません。

次回は、マイニングアカウントを別にしてもう一度トライしてみます。

(3度目の正直という事で・・・・😅)

Ethereum(11) - クラウドファンディング用のスマートコントラクト(キャンペーン成功)

前回は、クラウドファンディング用のスマートコントラクトを作成とデプロイを行いました。

今回は、このスマートコントラクトに対して、キャンペーンが成功するケースでテストしていきます。

コンストラクタ

デプロイ時(コンストラクタのコール時)の設定値は下記のように設定しました。

  • SELECT CONTRACT TO DEPLOY
    ‘Crowd Funding’を選択。
  • _duration
    テスト用に10分に設定。
  • _goal amount
    目標額に20etherを設定。
    Mist Walletで設定する場合はwei表記のため 20000000000000000000 を設定。

[入力イメージ画面]

アカウントごとの役割

アカウントを3つ用意し、次のように役割分担をしておきます。

  • MAIN ACCOUNT (eth.accounts[0])
    オーナー
  • ACCOUNT1 (eth.accounts[1])
    投資家1
  • ACCOUNT2 (eth.accounts[2])
    投資家2

投資家1と投資家2のアカウントからは、etherを送る必要がありますのでマイニングするなどしてetherを増やしておいてください。

[アカウントイメージ]

キャンペーンに成功するケース

geth上でcfという変数に、スマートコントラクトを割り当てます。

アドレスとインタフェースはMist Walletから取得しておきます。

[コマンド]

1
var cf = eth.contract([ { "constant": false, "inputs": [], "name": "checkGoalReached", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "ended", "outputs": [ { "name": "", "type": "bool", "value": false } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "numInvestors", "outputs": [ { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "totalAmount", "outputs": [ { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "status", "outputs": [ { "name": "", "type": "string", "value": "Funding" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "goalAmount", "outputs": [ { "name": "", "type": "uint256", "value": "20000000000000000000" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "deadline", "outputs": [ { "name": "", "type": "uint256", "value": "1625274319" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "", "type": "uint256" } ], "name": "investors", "outputs": [ { "name": "addr", "type": "address", "value": "0x0000000000000000000000000000000000000000" }, { "name": "amount", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "kill", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "owner", "outputs": [ { "name": "", "type": "address", "value": "0x63f25b9bbd974fdfa07477cb99c60d2073cfe560" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "fund", "outputs": [], "payable": true, "type": "function" }, { "inputs": [ { "name": "_duration", "type": "uint256", "index": 0, "typeShort": "uint", "bits": "256", "displayName": "&thinsp;<span class=\"punctuation\">_</span>&thinsp;duration", "template": "elements_input_uint", "value": "" }, { "name": "_goalAmount", "type": "uint256", "index": 1, "typeShort": "uint", "bits": "256", "displayName": "&thinsp;<span class=\"punctuation\">_</span>&thinsp;goal Amount", "template": "elements_input_uint", "value": "" } ], "payable": false, "type": "constructor" } ]).at('0xF9376fc03ebdF73713d757cB8d1cd10448D1Af2f')


deadline(終了時間)とキャンペーンのステータスを確認します。

[gethコンソール]

1
2
3
4
5
> cf.deadline()
1625274319

> cf.ended()
false


fund関数をトランザクションで呼び出します。

投資家Aと投資家Bからそれぞれ10etherを送金します。

アカウントごとにロックを解除する必要があります。

[gethコンソール]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> personal.unlockAccount(eth.accounts[1])
Unlock account 0x63f25b9bbd974fdfa07477cb99c60d2073cfe560
Passphrase:
true

> cf.fund.sendTransaction({from:eth.accounts[1], gas:5000000, value:web3.toWei(10, "ether")})
"0x996b426f48dca2f4d47728863589748ab578cce7cd9267c511d66a476b582ca9"

> personal.unlockAccount(eth.accounts[2])
Unlock account 0xd5adf1e9fbc1ed869ed4c7372961238fddc760a5
Passphrase:
true

> cf.fund.sendTransaction({from:eth.accounts[2], gas:5000000, value:web3.toWei(10, "ether")})
"0x65f7cee0c725421ffeeb4265569948e1598337fe4b9c0916737e26bf87e760f2"


投資家Aからの投資額を確認します。

[gethコンソール]

1
2
3
4
5
> cf.investors(0)[0]
"0x63f25b9bbd974fdfa07477cb99c60d2073cfe560"

> web3.fromWei(cf.investors(0)[1], "ether")
10


投資家Bからの投資額を確認します。

[gethコンソール]

1
2
3
4
5
> cf.investors(1)[0]
"0xd5adf1e9fbc1ed869ed4c7372961238fddc760a5"

> web3.fromWei(cf.investors(1)[1], "ether")
10

それぞれ10etherずつ投資していることが確認できます。


投資の総額を確認します。

[gethコンソール]

1
2
> web3.fromWei(cf.totalAmount(), "ether")
20


コントラクトの残高を確認します。

[gethコンソール]

1
2
> web3.fromWei(eth.getBalance(cf.address), "ether")
20

目標金額の20etherに達していることが確認できます。


キャンペーンが終了する前のオーナーの残高を確認しておきます。

[gethコンソール]

1
2
> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
27555


あとは、終了時間まで待ってcheckGoalReached関数を呼び出せば、投資された20etherがオーナーのアドレスに振り込まれるはずです。

checkGoalReached関数を実行するためには手数料(gas)が必要となりますので、オーナーのアカウントをアンロックしておく必要があります。

[gethコンソール]

1
2
3
4
5
6
7
8
9
10
11
12
13
> personal.unlockAccount(eth.accounts[0])
Unlock account 0xec3b01f36b44182746ca109230567c4915512e35
Passphrase:
true

> cf.checkGoalReached.sendTransaction({from:eth.accounts[0], gas:5000000})
"0xf3b15cbacb1b9aeaad5a646d924fe54c78f4e34b6eda37321f8b6438626b77ef"

> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
27554.985

> cf.ended()
false

・・・なにかおかしいです。

まずcheckGoalReached関数の呼び出し自体はうまくいっているようですが、オーナーのetherが増えていません。(手数料だけはちゃんと引き落とされてます・・・)

あと修了確認のcf.ended関数を呼び出しても、キャンペーンが終わっていません。

Mist Walletで確認しても、終了にはなっておらず、投資された20etherもそのままです。
(本来であれば、目標金額に達しているのでオーナーに20etherが送金され、コントラクト上は0etherになっているはず・・)



またDeadlineを確認すると、27分前に締め切りは過ぎているようです。



今回の動きがおかしい件はスマートコントラクトの処理が問題だと思われますので、次回はデバッグを行い想定通りの処理ができるようにしたいと思います。

Ethereum(10) - クラウドファンディング用のスマートコントラクト

今回は、クラウドファンディング用のスマートコントラクトを作成します。

クラウドファンディングの仕様

スマートコントラクトの仕様は次の通りです。

  • etherで資金を募る。
  • 投資家はスマートコントラクトに対してetherを伴う形でトランザクションを発生させて投資する。
  • スマートコントラクトはキャンペーンの締め切りと目標額を設定する。
  • 締め切りを迎えた時点で目標額に達していたら、オーナーに集めたetherを送金する。
  • 締め切りを迎えた時点で目標額に満たなければ、各投資家に返金する。

クラウドファンディングのソース

クラウドファンディングのソースは以下のようになります。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
pragma solidity ^0.4.11;
contract CrowdFunding {
// 投資家
struct Investor {
address addr; // アドレス
uint amount; // 投資額
}

address public owner; // コントラクトのオーナー
uint public numInvestors; // 投資家の数
uint public deadline; // 締め切り(UnixTime)
string public status; // キャンペーンのステータス
bool public ended; // キャンペーンが終了しているかどうか
uint public goalAmount; // 目標額
uint public totalAmount; // 投資の総額
mapping (uint => Investor) public investors; // 投資家管理用のマップ

modifier onlyOwner () {
require(msg.sender == owner);
_;
}

/// コンストラクタ
function CrowdFunding(uint _duration, uint _goalAmount) {
owner = msg.sender;

// 締め切りを設定(UnixTime)
deadline = now + _duration;

goalAmount = _goalAmount;
status = "Funding";
ended = false;

numInvestors = 0;
totalAmount = 0;
}

/// 投資する際に呼び出される関数
function fund() payable {
// キャンペーンが終わっていれば処理を中断
require(!ended);

Investor inv = investors[numInvestors++];
inv.addr = msg.sender;
inv.amount = msg.value;
totalAmount += inv.amount;
}

/// 目標額に達したかどうかを確認する
/// キャンペーンの成功/失敗に応じたetherの返金をする
function checkGoalReached () public onlyOwner {
// キャンペーンが終わっていれば処理を中断
require(!ended);

// 締め切り前の場合は処理を中断
require(now >= deadline);

if(totalAmount >= goalAmount) { // キャンペーンに成功
status = "Campaign Succeeded";
ended = true;
// オーナーにコントラクト内のすべてのetherを送金する
if(!owner.send(this.balance)) {
throw;
}
} else { // キャンペーンに失敗
uint i = 0;
status = "Campaign Failed";
ended = true;

// 投資家毎にetherを返金する
while(i <= numInvestors) {
if(!investors[i].addr.send(investors[i].amount)) {
throw;
}
i++;
}
}
}

/// コントラクトを破棄
function kill() public onlyOwner {
selfdestruct(owner);
}
}
  • struct(4-7行目)
    構造体です。
    複数の変数をまとめて宣言することができます。
  • mapping(16行目)
    キーとバリューをとるデータ構造を表現します。
    キーには投資額(uint型)、バリューには投資家(Investor型)を宣言しています。
  • コンストラクタ(24-36行目)
    締め切り(deadline)、目標額(goalAmount)などの初期値を設定します。
  • fund関数(39-47行目)
    投資するときに呼び出される関数で、etherの送金を伴う形で呼び出されます。
    キャンペーンが終了していなければ、Investorを生成してマップに登録し、投資総額を更新します。
    etherを受け取る関数のためpayableを付与しています。
  • checkGoalReached(51-78行目)
    キャンペーン終了時にオーナーによってのみ呼び出される関数です。
    キャンペーンが終了し、目標額に達している場合はオーナーに集められた投資額が送金されます。
    目標額に満たなかった場合は、投資家にetherが返金されます。

デプロイ

Mist Walletでスマートコントラクトをデプロイします。

デプロイの手順は下記の記事をご参照ください。

デプロイの参考記事 - Ethereum(8) - スマートコントラクト①


次回は、キャンペーンに成功するケースで処理を実行してみます。

Ethereum(9) - gethコンソールでトランザクション発行

今回は、gethコンソールでトランザクションを発行します。

トランザクション発行の準備

トランザクションを発行するため下記のような状態にしておきます。

  • gethを起動
  • マイニングを開始
  • Mist Walletを起動
  • スマートコントラクトをデプロイ済み

必要情報の取得

まずはMist Walletより下記の情報を取得します。

  • スマートコントラクトのインターフェース
  • スマートコントラクトのアドレス

スマートコントラクトを表示した画面で①[Show Interface]をクリックします。



下記のような画面が表示されます。

これはABIと呼ばれるものでコントラクトの関数情報などを持つインターフェースです。

②をコピーしておきます。



上記の画面を閉じて、③アドレスを選択しコピーします。

コピーする際に下記のWarning画面が表示されますが、④[COPY ANYWAY]を選択しコピーを行います。

トランザクション発行(gethコンソール)

gethコンソールを使ってトランザクションを発行していきます。

最初にコントラクトの定義を行います。

[コントラクトの定義方法]

1
var con = eth.contract(インターフェース).at('アドレス')

インターフェースとアドレスには、Mist Walletで取得した情報を貼り付けます。

[コマンド]

1
var con = eth.contract([ { "constant": false, "inputs": [ { "name": "_msg2", "type": "string" } ], "name": "setMsg2", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "counter", "outputs": [ { "name": "", "type": "uint8", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "getMsg2", "outputs": [ { "name": "", "type": "string", "value": "Hello Japan" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "setCounter", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "owner", "outputs": [ { "name": "", "type": "address", "value": "0xec3b01f36b44182746ca109230567c4915512e35" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "msg1", "outputs": [ { "name": "", "type": "string", "value": "Hello World" } ], "payable": false, "type": "function" }, { "inputs": [ { "name": "_msg1", "type": "string", "index": 0, "typeShort": "string", "bits": "", "displayName": "&thinsp;<span class=\"punctuation\">_</span>&thinsp;msg1", "template": "elements_input_string", "value": "Hello World" } ], "payable": false, "type": "constructor" } ]).at('0x75697Ba2442e8A44d967fE4874f0f14bF9E2CdB3')

コントラクトがconという変数から操作可能になります。

[コントラクトの情報表示]

1
con

[結果]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
{
abi: [{
constant: false,
inputs: [{...}],
name: "setMsg2",
outputs: [],
payable: false,
type: "function"
}, {
constant: true,
inputs: [],
name: "counter",
outputs: [{...}],
payable: false,
type: "function"
}, {
constant: true,
inputs: [],
name: "getMsg2",
outputs: [{...}],
payable: false,
type: "function"
}, {
constant: false,
inputs: [],
name: "setCounter",
outputs: [],
payable: false,
type: "function"
}, {
constant: true,
inputs: [],
name: "owner",
outputs: [{...}],
payable: false,
type: "function"
}, {
constant: true,
inputs: [],
name: "msg1",
outputs: [{...}],
payable: false,
type: "function"
}, {
inputs: [{...}],
payable: false,
type: "constructor"
}],
address: "0x75697Ba2442e8A44d967fE4874f0f14bF9E2CdB3",
transactionHash: null,
allEvents: function(),
counter: function(),
getMsg2: function(),
msg1: function(),
owner: function(),
setCounter: function(),
setMsg2: function()
}

conはjsonオブジェクト形式で表現されているので、アクセスしたい情報のみの表示もできます。

[addressのみを表示]

1
con.address

[結果]

1
"0x75697Ba2442e8A44d967fE4874f0f14bF9E2CdB3"

配列の要素にアクセスする場合は次のようにします。

[配列要素へのアクセス]

1
con.abi[0].name

[結果]

1
"setMsg2"

いよいよトランザクションの実行を行います。

トランザクションの発行元アドレスのアンロックを行ってから、トランザクションを実行します。

[トランザクションの発行形式]

1
コントラクトを定義した変数名.関数名.sendTransaction(関数の引数, {from:呼び出し元アドレス , gas:Gas Limit})

[コマンド]

1
2
personal.unlockAccount(eth.accounts[0])
con.setMsg2.sendTransaction("Hello Tokyo", {from:eth.accounts[0], gas:5000000});

[結果]

1
2
3
4
Unlock account 0xec3b01f36b44182746ca109230567c4915512e35
Passphrase:
true
"0x1986edeb269889d1a617568a958bc0b2617be5cbedf6e63c11af8cca3ebb2874"

トランザクションが正常に発行されると、上記のようなアドレスが返ります。


発行したトランザクションのレシート情報を表示させます。

[コマンド]

1
eth.getTransactionReceipt("0x1986edeb269889d1a617568a958bc0b2617be5cbedf6e63c11af8cca3ebb2874")

[結果]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
blockHash: "0x045014afd90d7510443883f66b7e6957d5e829c130988847e9d2501ec43803bd",
blockNumber: 5002,
contractAddress: null,
cumulativeGasUsed: 33627,
from: "0xec3b01f36b44182746ca109230567c4915512e35",
gasUsed: 33627,
logs: [],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
root: "0xb28ad87e40c4dfcc5627c0be9a47b0ca52c1ba39cb998735857d7ffb56360792",
to: "0x75697ba2442e8a44d967fe4874f0f14bf9e2cdb3",
transactionHash: "0x1986edeb269889d1a617568a958bc0b2617be5cbedf6e63c11af8cca3ebb2874",
transactionIndex: 0
}

blockNumberから5002ブロックに取り込まれたことが確認できます。


トランザクションが取り込まれた5002ブロックの確認を行います。

[コマンド]

1
eth.getBlock(5002)

[結果]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
difficulty: 888173,
extraData: "0xda83010817846765746888676f312e31312e358777696e646f7773",
gasLimit: 20000000,
gasUsed: 33627,
hash: "0x045014afd90d7510443883f66b7e6957d5e829c130988847e9d2501ec43803bd",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0xec3b01f36b44182746ca109230567c4915512e35",
mixHash: "0xb31f011cd4dc9d896d5cfbf202efd146bcf5a48d639270e8edc5ed56732ba8a7",
nonce: "0x4dcab0b6406bcf09",
number: 5002,
parentHash: "0x69d89fe08a801520f5dcb4f52cc5a8b8925c8cb1c08b1afdc8c3d5273bc97e5a",
receiptsRoot: "0x9d588cf48eb10b0bd843d3bf8057a17e28cb832f88edb8fb8c1aaf92960c45e2",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 747,
stateRoot: "0x903878d67302c1519ad19c9ac775f22495d378e37f08d617c617c16f597d0e5b",
timestamp: 1625090230,
totalDifficulty: 2086374534,
transactions: ["0x1986edeb269889d1a617568a958bc0b2617be5cbedf6e63c11af8cca3ebb2874"],
transactionsRoot: "0x37b1ebd6abd7eb1a92b88f98938126f0b87f59b46f1c18683771d13ed704a621",
uncles: []
}

transactionsに、今回発行したコントラクトのアドレスが含まれていることを確認できます。


geth上で発行したトランザクションが、Mist Walletに反映されていることを確認します。

Get msg2がHello Tokyoとなっているので正常に反映されていることが分かります。

Ethereum(9) - Mist Walletでトランザクション発行

前回は、Mist Walletを使ってスマートコントラクトをデプロイしました。

今回は、デプロイしたスマートコントラクトに対してトランザクションを発行します。

トランザクション発行の準備(Mist Wallet)

トランザクションを発行するため下記のような状態にしておきます。

  • gethを起動
  • マイニングを開始
  • Mist Walletを起動
  • スマートコントラクトをデプロイ済み

トランザクション発行(Mist Wallet)

Mist Walletを使ってトランザクションを発行していきます。

①CONTRACTタブを選択し、②前回デプロイしたスマートコントラクトをクリックします。



③Select FunctionでSet Msg 2を選択し、④msg2に「Hello Japan」を入力します。

⑤EXECUTEボタンをクリックします。



⑥Provide maximum feeに手数料を入力できますが、今回はデフォルトのままにしました。

⑦アカウント(アドレス)のパスワードを入力し、⑧SEND TRANSACTIONをクリックします。



⑨WALLETSタブを選択し、⑩発行したトランザクションがブロックに取り込まれるのを待ちます。

ブロックに取り込まれたら⑩トランザクションを選択します。



⑪Getmsg 2に「Hello Japan」が表示されているので、問題なくトランザクションが発行され、ブロックに取り込まれたことが分かります。



次回は、gethコンソールから同じようにトランザクションの発行を行います。

Ethereum(8) - スマートコントラクト①

前回までの記事で、gethの起動とMist Walletの起動ができるようになりました。

今回はこの環境を使って、簡単なスマートコントラクトを実行してみます。

準備

スマートコントラクトを実行するために、下記の状態にしておきます。

  • gethを起動
  • アカウント(アドレス)を1つ作成
  • マイニングを開始
  • Mist Walletを起動

スマートコントラクトの内容

今回試すスマートコントラクトは下記になります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
pragma solidity ^0.4.11;
contract HelloEthereum {
string public msg1;

string private msg2;

address public owner;

uint8 public counter;

/// コンストラクタ
function HelloEthereum(string _msg1) {
// msg1に _msg1を設定
msg1 = _msg1;
// ownerに本コントラクトを生成したアドレスを設定
owner = msg.sender;
// counterに初期値として0を設定
counter = 0;
}

/// msg2の設定
function setMsg2(string _msg2) public {
// if文の例
if(owner != msg.sender) {
throw;
} else {
msg2 = _msg2;
}
}

// msg2の取得
function getMsg2() constant public returns(string) {
return msg2;
}

// カウンターの設定
function setCounter() public {
// for文のサンプル
for(uint8 i = 0; i < 3; i++) {
counter++;
}
}
}

メッセージを設定・取得したり、カウンターを設定する簡単な処理になります。

スマートコントラクトのデプロイ

Mist Walletの①Contractタブを選択し、②DEPLOY NEW CONTRACTをクリックします。



③FROMにはスマートコントラクトを生成するアカウント(オーナー)を指定します。

④SOLIDITY CONTRACT SOURCE CODEにはスマートコントラクトの内容(コード)を記述します。

⑤SELECT CONTRACT TO DEPLOYではHello Ethereumを選択します。

⑥引数msg1にはHello Worldを記入します。



画面を下にスクロールし⑦DEPLOYボタンをクリックします。



⑧Provide maximum feeには5000000を入力します。これはGas Limitに相当し、テスト環境のため大きめの値を設定しました。

⑨アカウント作成時に設定したパスワードを入力します。

⑩SEND TRANSACTIONをクリックします。この後はWALLETSタブに自動遷移します。



画面下にスクロールするとLATEST TRANSACTIONSにデプロイしたスマートコントラクトが表示されています。

Create Contractが薄い色の場合は、まだブロックに取り込まれていないので、色がはっきりするまで少々待ちます。

ブロックに取り込まれたら⑪Hello Ethereum 7569を選択します。



⑫デプロイしたスマートコントラクトの詳細が表示されます。

⑬Msg1には、デプロイ時に指定した文字が表示されています。



次回はMist Walletからこのスマートコントラクトのトランザクションを発行してみます。

Ethereum(7) - Mist Wallet(公式ウォレット)のインストールと起動(Windows編)

前回はUbuntu20.04にて、Ethereumの公式ウォレットMist Walletを起動しようとしましたが、うまくいきませんでした。

その後、解消方法を調べたのですが結局起動することができなかったので、Ubuntu上でMist Walletを起動するのは諦めました。

Windows版

Windows版でいろいろなバージョンの組み合わせで動作確認を行ったところ、(だいぶ古いバージョンになりますが)次の組み合わせでMist Walletを起動させることに成功しました。

  • geth-windows-amd64-1.8.23-c9427004.exe
  • Ethereum-Wallet-win64-0-8-10.zip

geth-windows-amd64-1.8.23-c9427004.exeのダウンロード

geth [geth-windows-amd64-1.8.23-c9427004.exe] - https://geth.ethereum.org/downloads/

Windowsタブを選択し、Show older releasesをクリックします。

画面をスクロールしGeth 1.8.2 - Installer - 64-bitをダウンロードします。

Ethereum-Wallet-win64-0-8-10.zipのダウンロード

Mist Wallet [Ethereum-Wallet-win64-0-8-10.zip] - https://github.com/ethereum/mist/releases/tag/v0.8.10

Windows版でMist Wallet起動

ダウンロードしたファイルをそれぞれインストールし、Gethを起動したあとでMist Walletを起動すると問題なく下記の画面が立ち上がりました。



次回からは、このGethとMist Walletの組み合わせでスマートコントラクトの動作確認をしていきます。