Ethereum(25) - 攻撃を回避するオークション用スマートコントラクト①(実装編)

今回は攻撃を回避するためのオークション用スマートコントラクトを実装していきます。

回避策

攻撃を回避するためにはどのような仕様にすればよいでしょうか。

入札(bid)と同時に返金を行うと、返金時に問題が発生し入札自体が失敗してしまうことがあるので、入札と返金の処理を分ける仕様にしてみたいと思います。

実装

入札と返金の処理を分けたコードは次の通りです。

[ソースコード]

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
pragma solidity ^0.4.11;
contract AuctionWithdraw {
address public highestBidder; // 最高額提示アドレス
uint public highestBid; // 最高提示額
// 返金額を管理するマップ
mapping(address => uint) public userBalances;

/// コンストラクタ
function AuctionWithdraw() payable {
highestBidder = msg.sender;
highestBid = 0;
}

/// Bid用の関数
function bid() public payable {
// bidが現在の最高額よりも大きいことを確認
require(msg.value > highestBid);

// 最高額提示アドレスの返金額を更新する
userBalances[highestBidder] += highestBid;

// ステート更新
highestBid = msg.value;
highestBidder = msg.sender;
}

function withdraw() public{
// 返金額が0より大きいことを確認
require(userBalances[msg.sender] > 0);

// 最高額提示者でないことを確認
require(userBalances[msg.sender] < highestBid);

// 返金額を退避
uint refundAmount = userBalances[msg.sender];

// 返金額を更新
userBalances[msg.sender] = 0;

// 返金処理
if(!msg.sender.send(refundAmount)) {
throw;
}
}
}

このコードのポイントは、bid部分と返金部分が独立し、もしwithdrawで失敗したとしてもその後の入札(bid)に影響を与えないことです。

まとめ

etherの返金は、送り先のコントラクトに悪意や実装の問題があると予期せぬ動作を引き起こされる可能性があります。

返金をするときは、他の処理とは独立させ返金専用の関数を作り、etherを取りにきてもらうのが安全だということになります。

Ethereum(24) - オークションを攻撃するスマートコントラクト②(実行編)

今回はオークションを攻撃するスマートコントラクトを実行していきます。

アカウントごとの役割

アカウントの役割は次の通りです。

  • MAIN ACCOUNT (eth.accounts[0]) “0xec3b01f36b44182746ca109230567c4915512e35”
    オークションの オーナー。
  • ACCOUNT1 (eth.accounts[1]) “0x63f25b9bbd974fdfa07477cb99c60d2073cfe560”
    Bidder1(入札者1)。アカウント1。⇒悪意のあるコンストラクトから攻撃を行う。
  • ACCOUNT2 (eth.accounts[2]) “0xd5adf1e9fbc1ed869ed4c7372961238fddc760a5”
    Bidder2(入札者2)。アカウント2。
  • ACCOUNT3 (eth.accounts[3])
    マイナー。

デプロイ

まずはデプロイを行います。デプロイアカウントはアカウント1です。

[デプロイ時のイメージ]

悪意のあるコントラクトから入札(30 ether)

入札を行う前にアカウント1の残高を確認します。


アカウント1から、悪意のあるコンストラクト(EvilBidder)を使って入札(bid関数)を行います。

入札額は30 etherです。

to - addressには、オークション用コントラクトのアドレスを設定しています。



アカウント1の残高を確認します。

30 ether減っていることが確認できます。

攻撃された後のオークション用コントラクト

オークション用コントラクトの状態を確認します。



最高提示額が30 etherになっていることが確認できます。

また最高額提示者がEvilBidderのアドレスになっていることも確認できます。

通常の入札(40 ether)

アカウント2から再び入札を行います。

入札額は40 etherです。



オークションコントラクトの状態を確認します。



最高入札額が30 etherで、最高額提示者がEvilBidderのアドレスになっていることから、40 etherの入札が失敗していることを確認できます。

入札が失敗した理由

入札失敗の流れは次の通りです。

  1. オークション用コントラクトのbid関数がコールされる。
  2. 最高提示額が更新されたため、オークションコントラクトから悪意のあるコントラクトへ返金処理が実行される。
  3. 悪意のあるコントラクトのFallback関数がコールされ、意図的なエラーが発生する。(エクセプションがスローされる)
  4. オークションコントラクトでエラーが発生し、bid関数のロールバック処理が行われる。

つまり、今後だれがオークションコン用トラクトへbid(入札)しても最高提示額の更新は行うことができない状態になってしまっています。

この手法を使うと、1回だけ最高提示額を提示すればそのオークションで落札することが可能になります。


次回は、このような攻撃を避けるためのオークション用コントラクトを作成していきます。

Ethereum(23) - オークションを攻撃するスマートコントラクト①(実装編)

今回はオークションを攻撃するスマートコントラクトを実装していきます。

実装

オークションを攻撃するためのコードは次の通りです。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pragma solidity ^0.4.11;
contract EvilBidder {
/// Fallback関数(返金時にコールされる)
function() payable{
revert(); // エラーを発生する(Exceptionをスローする)
}

/// bid用の関数
function bid(address _to) public payable {
// bidを行う
if(!_to.call.value(msg.value)(bytes4(sha3("bid()")))) {
throw;
}
}
}

各関数の説明は下記のとおりです。

  • Fallback関数(4行目)
    無名の関数となっていて、返金が発生した場合に呼び出されます。
  • revert関数(5行目)
    意図的にエラーを発生させます。
  • bid関数(9行目)
    引数で指定されたアドレス(_to)に対して送金処理を行う関数です。
    オークション用のコントラクトに対して入札を行います。
    (_toアドレスを持つコンストラクトのbid関数を、msg.valueを送金金額として呼び出す。)
    Solidityで記述された関数はコンパイルされると、関数のSHA3ハッシュ値の最初の4バイトで識別されます。

次回は、この悪意のあるコントラクトを使ってオークション用のコントラクトを攻撃していきます。

Ethereum(22) - オークションのスマートコントラクト②(実行編)

オークション用のコントラクトを実行していきます。

アカウントごとの役割

アカウントの役割は次の通りです。

  • MAIN ACCOUNT (eth.accounts[0]) “0xec3b01f36b44182746ca109230567c4915512e35”
    オークションの オーナー。
  • ACCOUNT1 (eth.accounts[1]) “0x63f25b9bbd974fdfa07477cb99c60d2073cfe560”
    Bidder1(入札者1)。アカウント1。
  • ACCOUNT2 (eth.accounts[2]) “0xd5adf1e9fbc1ed869ed4c7372961238fddc760a5”
    Bidder2(入札者2)。アカウント2。
  • ACCOUNT3 (eth.accounts[3])
    マイナー

デプロイ

まずはデプロイを行います。デプロイアカウントはMAIN ACCOUNTです。

[デプロイ時のイメージ]


デプロイ直後のコンストラクトの状態を確認します。

[コンストラクトの状態]

  • コンストラクトの残高確認
    0 ether
  • 最高提示者のアドレス確認
    Main account
  • 最高提示額の確認
    0 wei

まだ入札されていないためコンストラクトの残高が0etherで、最高提示者がオーナーアドレス(Main account)になっています。

最初の入札

アカウント1から最初の入札を行ってみます。

入札前にアカウント1の残高確認を確認します。

[アカウント1の残高]


アカウント1からBid関数をコールして、10etherをbid(入札)します。

[最初の入札]


入札後のアカウント1の残高は次の通りです。

[アカウント1の残高]

10ether減っていることが確認できます。


最初の入札後のコンストラクトの状態を確認します。

[コンストラクトの状態]

  • コンストラクトの残高確認
    10 ether
  • 最高提示者のアドレス確認
    Account1
  • 最高提示額の確認
    10000000000000000000 wei

アカウント1が最高提示者となり、コンストラクトの残高が10ehterに増えていることが確認できます。

2回目の入札

アカウント2から、2回目の入札を行います。

入札前のアカウント2の残高は次の通りです。

[アカウント2の残高]


アカウント2からBid関数をコールして、20etherをbid(入札)します。

[2回目の入札]


入札後のアカウント2の残高は次の通りです。

[アカウント2の残高]

20ether減っていることが確認できます。


2回目の入札後のコンストラクトの状態を確認します。

[コンストラクトの状態]

  • コンストラクトの残高確認
    20 ether
  • 最高提示者のアドレス確認
    Account2
  • 最高提示額の確認
    20000000000000000000 wei

アカウント2が最高提示者となり、コンストラクトの残高が20ehterに増えていることが確認できます。

最初の入札者への返金確認

アカウント2が最高提示額となったため、アカウント1へ返金処理が行われているはずです。

アカウント1の残高を確認します。

[アカウント1の残高]

10etherが返金され、アカウント1の残高が入札前の状態に戻っていることが確認できました。

以上で、一通りのオークションとしての動作が確認できたことになります。

脆弱性

実はこのコンストラクトでは悪意のあるコンストラクトからの攻撃に弱いという問題があります。

次回は、悪意のあるコンストラクトを作成し、このオークション用のコンストラクトに対して攻撃を行ってみます。

Ethereum(21) - オークションのスマートコントラクト①(実装編)

今回はオークションの機能をスマートコントラクトで実装していきます。

実装

オークションを実現するためのコードは次の通りです。

[ソースコード]

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
pragma solidity ^0.4.11;
contract Auction {
address public highestBidder; // 最高額提示アドレス
uint public highestBid; // 最高提示額

/// コンストラクタ
function Auction() payable {
highestBidder = msg.sender;
highestBid = 0;
}

/// Bid用の関数
function bid() public payable {
// bidが現在の最高額よりも大きいことを確認
require(msg.value > highestBid);

// 返金額退避
uint refundAmount = highestBid;
// これまで最高額を提示していたアドレス退避
address currentHighestBidder = highestBidder;

// ステート更新(新しい落札者を表す)
highestBid = msg.value;
highestBidder = msg.sender;

// これまで最高額を提示していたbidderに返金
if(!currentHighestBidder.send(refundAmount)) {
throw;
}
}
}

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

  • 入札(bid)したいユーザは、現在の最高提示額を超えるetherの送金を伴う形でbid関数を呼び出す。
  • bid関数が呼ばれると、それまで最高額を提示していたアカウントにbidしていた金額を返金する。
  • 返金をするためにsend関数を使う。
    send関数は、送金対象のアドレス.send(送金額)という形式で実行する。
    送金単位はweiとなる。
  • オークションとはいっても、最高額を提示している金額とアドレスを管理するだけで、締め切り期限になったら何か権利が得られる・・・といったことはやっていません🙇‍♀️

次回は、このスマートコントラクトの動作を確認していきます。

Ethereum(20) - IoTで利用するスイッチを制御するコントラクト(実行編)

IoTで利用するスイッチを制御するコントラクトを実行していきます。

アカウントごとの役割

アカウントの役割は次の通りです。

  • MAIN ACCOUNT (eth.accounts[0]) “0xec3b01f36b44182746ca109230567c4915512e35”
    オーナー
  • ACCOUNT1 (eth.accounts[1]) “0x63f25b9bbd974fdfa07477cb99c60d2073cfe560”
    IoTスイッチ
  • ACCOUNT2 (eth.accounts[2]) “0xd5adf1e9fbc1ed869ed4c7372961238fddc760a5”
    利用者
  • ACCOUNT3 (eth.accounts[3])
    マイナー

デプロイ

まずはデプロイを行います。

デプロイアカウントはMAIN ACCOUNTで、IoTのアドレスとしてaccounts[1]のアドレスを設定します。

[デプロイ時のイメージ]


デプロイ直後のコントラクトの状態を確認します。

[デプロイ時のイメージ]

IoTステートのアドレスにAccount1が設定され、OwnerステートのアドレスにMain accountが設定されていることを確認できます。

利用登録

利用者のアドレス(Acctount 2)からpayToSwitch関数を使って、1etherを送金します。

[利用登録]


コントラクタを開いて、スイッチの状態を確認します。

[スイッチの状態確認]

支払回数(Num paid)が1となり、スイッチの状態(Switches)にAccount2が支払いをしたことや、利用できる状態であること(Status=YES)、利用終了時間(End Time)が4分後であることが確認できます。

????

前回のコンストラクトの確認では、構造体の情報(ステート)がMist Walletから確認できなかったのですが、なぜか今回は確認できています。

少々困惑しておりますが、「Mist Walletでステートが確認できればそれでOK。ダメな時はgethコンソールから確認してみる」ということにしておきたいと思います。

利用ステータスの変更

利用終了時刻(利用登録から5分後)になったら、IoTスイッチ(Account 1)よりupdateStatus関数を呼び出して、statusをfalseに変更します。

[利用ステータスの変更]


変更したステータスを確認します。

[ステータスの確認]

利用状況が不可(Status=NO)になっていることが分かります。

etherの回収

利用料の1etherをオーナーアドレスから回収します。

回収前のオーナーの残高を確認しておきます。

[オーナーの残高(回収前)]


回収処理のwithdrawFunds関数を、オーナーアドレスからコールします。

[利用料の回収]


回収後のオーナーの残高を確認します。

[オーナーの残高(回収後)]

オーナーの残高が1ether増えていることが確認できました。

IoTで利用するスイッチを制御するコントラクトの動作確認は問題なく終了できました。

Ethereum(19) - IoTで利用するスイッチを制御するコントラクト(実装編)

スマートコントラクトはIoTの領域でも活用が期待されています。

カーシェアリングを例にとってみます。

カーシェアリングでは、利用者は車を利用するたびに利用時間に応じた金額を支払う必要があります。

スマートコントラクトを使うと、そのコントラクトに送金することで車が送金状況を確認してドアを自動で開けるようなことが可能になります。

実装

IoTで利用するスイッチを制御するコントラクトを実装します。

[ソースコード]

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
pragma solidity ^0.4.11;
contract SmartSwitch {
// スイッチ用の構造体
struct Switch {
address addr; // 利用者のアドレス
uint endTime; // 利用終了時刻(UnixTime)
bool status; // trueの場合は利用可能
}

address public owner; // サービスオーナーのアドレス
address public iot; // IoTのアドレス

mapping (uint => Switch) public switches; // Switchを格納するマップ
uint public numPaid; // 支払いが行われた回数

/// サービスオーナーの権限チェック
modifier onlyOwner() {
require(msg.sender == owner);
_;
}

/// IoTの権限チェック
modifier onlyIoT() {
require(msg.sender == iot);
_;
}

/// コンストラクタ
/// IoTのアドレスを登録
function SmartSwitch(address _iot) {
owner = msg.sender;
iot = _iot;
numPaid = 0;
}

/// 支払い時に呼ばれる
function payToSwitch() public payable {
// 1 etherでなければ処理を終了
require(msg.value == 1000000000000000000);

// Switchを設定
Switch s = switches[numPaid++];
s.addr = msg.sender;
s.endTime = now + 300; // 300秒⇒5分使用可能にする
s.status = true;
}

/// statusを変更する
/// 利用終了時刻になったら呼び出される。引数はswitchesのkey値
function updateStatus(uint _index) public onlyIoT {
// 対象のindexに対してSwitchが設定されていなければ処理を終了
require(switches[_index].addr != 0);

// 利用終了時刻に達していなければ処理を終了
require(now > switches[_index].endTime);

// statusを更新
switches[_index].status = false;
}

/// 支払われたetherを引き出す
function withdrawFunds() public onlyOwner {
if (!owner.send(this.balance))
throw;
}

/// コントラクトを破棄
function kill() public onlyOwner {
selfdestruct(owner);
}
}

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

  • サービスオーナーがコンストラクタでIoTスイッチを登録します。
  • IoTスイッチの利用者はpayToSwitch関数でetherを支払います。
    1etherを支払うと5分間使用できるIoTスイッチを想定しています。
  • IoTスイッチは利用終了時刻になるとupdateStatus関数でstatusを更新してサービスを利用できなくします。
  • サービスオーナーはwithdrawFunds関数をコールし、支払われたetherを引き出すことができます。

次回から、このスマートコントラクトの動作を確認していきます。

Ethereum(18) - コントラクト名とアドレスを管理するスマートコントラクト(実行編②)

前回は、コントラクト名とアドレスを管理するスマートコントラクトの動作確認をMist Walletを使って実施しましたがうまくいきませんでした。

今回は、gethコンソールを使って動作確認を行ってみます。

事前準備

動作確認を行うために、下記のような準備をしておきます。

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

コンストラクト名の登録

まずはコントラクト名の登録を行います。登録するコントラクト名はcon1です。

登録する前に、アドレスのロックを解除する必要があります。

[コントラクト名の登録]

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

> nr.register.sendTransaction("con1",{from:eth.accounts[0], gas:5000000})
"0xc81cfecc89a0f698859249eb03a7a2b8560cdea437dcfa7df4a90bcfc5a11ea2"

問題なくトランザクションを発行できました。

ブロックに取り込まれるまで少し待って登録内容を確認します。

[登録内容の確認]

1
2
3
4
5
6
7
8
> nr.numContracts()
1

> nr.getOwner("con1")
"0xec3b01f36b44182746ca109230567c4915512e35"

> eth.accounts[0]
"0xec3b01f36b44182746ca109230567c4915512e35"

コントラクト名が1つ登録され、登録されたコントラクト名のオーナーアドレス(eth.accounts[0])であることが分かります。

アドレスの登録

次にコンストラクタ名にアドレスを結びつけます。

登録するアドレス情報はダミーです。

[アドレスの登録]

1
2
> nr.setAddr.sendTransaction("con1", "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",{from:eth.accounts[0], gas:5000000})
"0x68df1e01241aa57f5e12ba5f419400b03bbb6b993d95b8d9294ea9396ed76667"

問題なくトランザクションを発行できました。

ブロックに取り込まれるまで少し待って登録内容を確認します。

[登録内容の確認]

1
2
> nr.getAddr("con1")
"0x00000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

問題なくアドレスが登録されています。

説明の登録

コントラクト名に対応する説明を登録します。

[説明の登録]

1
2
> nr.setDescription.sendTransaction("con1", "This is description of con1.",{from:eth.accounts[0], gas:5000000})
"0xe838840f9e5f8e37414b731a0d0a9065c6b0fdab04f10f6692570d6aa4f92bf4"

問題なくトランザクションを発行できました。

ブロックに取り込まれるまで少し待って登録内容を確認します。

[登録内容の確認]

1
2
3
4
5
> nr.getDescription("con1")
"0x54686973206973206465736372697074696f6e206f6620636f6e312e00000000"

> web3.toUtf8(nr.getDescription("con1"))
"This is description of con1."

こちらも問題なく登録されていることを確認できました。

オーナーの変更

オーナーの変更を行います。

accounts[0]からaccounts[1]へ変更します。

[オーナーの変更]

1
2
> nr.changeOwner.sendTransaction("con1", eth.accounts[1],{from:eth.accounts[0], gas:5000000})
"0x905b2a21fd1741b51c86fd11b56cae0b68b9e04985ae69473601ddabfda7ce3f"

問題なくトランザクションを発行できました。

ブロックに取り込まれるまで少し待って変更内容を確認します。

[オーナーの変更]

1
2
3
4
5
> nr.getOwner("con1")
"0x63f25b9bbd974fdfa07477cb99c60d2073cfe560"

> eth.accounts[1]
"0x63f25b9bbd974fdfa07477cb99c60d2073cfe560"

オーナー情報がeth.accounts[1]のアドレスに変更されていることを確認できました。

登録内容の解除

最後に登録内容の解除を行います。

オーナーがeth.accounts[1]に変更されているので、そのアドレスのロックを解除しておく必要があります。

[登録解除]

1
2
3
4
5
6
7
> personal.unlockAccount(eth.accounts[1])
Unlock account 0x63f25b9bbd974fdfa07477cb99c60d2073cfe560
Passphrase:
true

> nr.unregister.sendTransaction("con1",{from:eth.accounts[1], gas:5000000})
"0x9615b4f95c35c771f2b8f07dd7d1ebb61172497f0096a01dbeb6e3552f684ca1"

問題なくトランザクションを発行できました。

ブロックに取り込まれるまで少し待って内容を確認します。

[解除内容確認]

1
2
> nr.numContracts()
0

登録されているコントラクトがなくなっていることを確認できます。

前回Mist Walletでうまくいかなかった理由

gethコンソールでの動作確認では全く問題がありませんでした。

ではどうしてMist Walletでは同じコントラクタの動作確認ができなかったのでしょうか?

gethコンソールで正常に動作確認している最中に、Mist Walletでもコントラクト情報を参照してみたのですが、情報が全く反映されていませんでした・・・いやNum Contractsだけはきちんと反映されてました。

また、Mist Walletで各登録処理を行ってみたところ、Mist Wallet画面では登録内容が確認できませんでしたが、gethコンソール上では登録内容の確認ができました

つまりMist Walletだと構造体のステートや引数のある関数の情報は表示できないということになります。

まとめて情報を参照できるMist Walletは大変便利だと思っていたのですが、ちょっと残念です。


次回は、また別のスマートコントラクトを作成していきたいと思います。

Ethereum(17) - コントラクト名とアドレスを管理するスマートコントラクト(実行編)

前回、コントラクト名とアドレスを管理するスマートコントラクトを実装しました。

今回はそのスマートコントラクトのデプロイと動作確認を行います。

アカウントごとの役割

アカウントの役割は次の通りです。

  • MAIN ACCOUNT (eth.accounts[0])
    オーナー
  • ACCOUNT1 (eth.accounts[1])
    利用者
  • ACCOUNT3 (eth.accounts[3])
    マイニングする

デプロイ

まずはデプロイを行います。

デプロイアカウントはMAIN ACCOUNTで、デプロイ時に必要なパラメータはありません。

[デプロイ時のイメージ]

コントラクト名の登録

register関数をコールして、コントラクト名を登録します。

コントラクト名はASCIIで指定する必要があります。

contract1を設定したいのでそれをASCII変換した0x636f6e747261637431を入力します。

[コントラクト名の登録]

登録内容の確認

コントラクト名が登録されていることを確認します。

[登録内容の確認]

早速問題が発生しています。

Num Contractsが1になっているので登録処理は実行されているのですが、コントラクト名contract1がどこにも見つかりません。

ContractsステートのOwnerのアドレスも0x0x0000000000000000000000000000000000000000となっており、オーナーのアドレスになっていません。


次回は、コントラクト名やオーナーアドレスがきちんと登録されるように調査・改善していきたいと思います。

Ethereum(16) - コントラクト名とアドレスを管理するスマートコントラクト(実装編)

これまではクラウドファンディングを実現するスマートコントラクトを試してきましたが、今回からは別のスマートコントラクトを作成してみます。

コントラクト名とアドレスを管理するスマートコントラクト

コントラクト名とそのアドレスを管理するスマートコントラクトを作成します。

コントラクトを一意で認識するためには、そのアドレスを使わなければなりません。

ただこのアドレスは16進数の長い文字列で認識しにくいため、別名(エイリアス)をつけた方が直感的で管理しやすくなります。


生成したコントラクトを誰かに教える場合、アドレスではなくコントラクトの別名を公開し、今回作成するコントラクトを通してアドレスを伝えた方が便利です。

また、仮にコントラクトに問題があり差し替える必要がある場合、別名(エイリアス)がアドレスへのポインタとなっているので移行がスムーズになるというメリットもあります。

ソースコード

コントラクト名とアドレスを管理するスマートコントラクトを実現するソースコードは、次のようになります。

各定義・処理に関しては、ソースコード内のコメントをご参照下さい。

[ソースコード]

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
pragma solidity ^0.4.11;
contract NameRegistry {
// コントラクト用の構造体
struct Contract {
address owner; // オーナー
address addr; // コントラクトのアドレス
bytes32 description; // コントラクトの説明
}

uint public numContracts; // 登録済みのレコード数

// コントラクトを保持するマップ
mapping (bytes32 => Contract) public contracts;

/// コンストラクタ
function NameRegistry() {
numContracts = 0;
}
//------------------------------------------------------
/// コントラクトの登録
function register(bytes32 _name) public returns (bool){
// 名前が利用されていなければ登録
if (contracts[_name].owner == 0) {
Contract con = contracts[_name];
con.owner = msg.sender;
numContracts++;
return true;
} else {
return false;
}
}

/// コントラクトの削除
function unregister(bytes32 _name) public returns (bool) {
if (contracts[_name].owner == msg.sender) {
contracts[_name].owner = 0;
numContracts--;
return true;
} else {
return false;
}
}
//------------------------------------------------------
/// コントラクトのオーナー変更
function changeOwner(bytes32 _name, address _newOwner) public onlyOwner(_name) {
contracts[_name].owner = _newOwner;
}

/// コントラクトのオーナー取得
function getOwner(bytes32 _name) constant public returns (address) {
return contracts[_name].owner;
}
//------------------------------------------------------
/// コントラクトのアドレス設定
function setAddr(bytes32 _name, address _addr) public onlyOwner(_name) {
contracts[_name].addr = _addr;
}

/// コントラクトのアドレス取得
function getAddr(bytes32 _name) constant public returns (address) {
return contracts[_name].addr;
}
//------------------------------------------------------
/// コントラクトの説明を設定
function setDescription(bytes32 _name, bytes32 _description) public onlyOwner(_name) {
contracts[_name].description = _description;
}

/// コントラクトの説明を取得
function getDescription(bytes32 _name) constant public returns (bytes32) {
return contracts[_name].description;
}
//------------------------------------------------------
/// 関数呼出し前に呼び出される処理であるmodifier(オーナーのみ実行可能)
modifier onlyOwner(bytes32 _name) {
require(contracts[_name].owner == msg.sender);
_;
}
}

データ型のbyte32は固定長の任意型で、任意の値を保管できます。”32”はサイズを指定しています。

今回の処理では、文字列を保管するのに使用しています。

全体的に、値を設定したり取得するだけなのでとても簡単なコントラクトになっていると思います。


次回から、Mist Walletを使ってこのスマートコントラクトの動作を確認していきます。