MongoDB(3) - コレクション操作編

今回はMongoDBのコレクションに関する操作を行ってみます。

コレクションは、RDBのテーブルに相当します。

コレクション作成

コレクションの作成を行います。コレクション名はgroupとします。

DB(データベース名:db1)を選択してから操作を行います。

[Mongoシェル]

1
2
3
4
5
> use db1
switched to db db1

> db.createCollection("group")
{ "ok" : 1 }

コレクションの一覧表示

コレクションの一覧を表示します。

[Mongoシェル]

1
2
> show collections
group

さきほど作成したgroupというコレクションが存在することを確認できます。

コレクションの統計情報

コレクションの統計情報を表示します。

各種性能を計測したり、運用時にデータ量を確認する場合に便利です。

[Mongoシェル]

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
> db.group.stats()
{
"ns" : "db1.group",
"size" : 0,
"count" : 0,
"storageSize" : 4096,
"freeStorageSize" : 0,
"capped" : false,
"wiredTiger" : {
"metadata" : {
"formatVersion" : 1
},
"creationString" : "access_pattern_hint=none,allocation_size=4KB,app_metadata=(formatVersion=1),assert=(commit_timestamp=none,durable_timestamp=none,read_timestamp=none,write_timestamp=off),block_allocation=best,block_compressor=snappy,cache_resident=false,checksum=on,colgroups=,collator=,columns=,dictionary=0,encryption=(keyid=,name=),exclusive=false,extractor=,format=btree,huffman_key=,huffman_value=,ignore_in_memory_cache_size=false,immutable=false,import=(enabled=false,file_metadata=,repair=false),internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=q,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=64MB,log=(enabled=true),lsm=(auto_throttle=true,bloom=true,bloom_bit_count=16,bloom_config=,bloom_hash_count=8,bloom_oldest=false,chunk_count_limit=0,chunk_max=5GB,chunk_size=10MB,merge_custom=(prefix=,start_generation=0,suffix=),merge_max=15,merge_min=0),memory_page_image_max=0,memory_page_max=10m,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,readonly=false,source=,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,tiered_object=false,tiered_storage=(auth_token=,bucket=,bucket_prefix=,local_retention=300,name=,object_target_size=10M),type=file,value_format=u,verbose=[],write_timestamp_usage=none",
"type" : "file",
"uri" : "statistics:table:collection-7-156884005367683298",
"LSM" : {
"bloom filter false positives" : 0,
"bloom filter hits" : 0,
"bloom filter misses" : 0,
"bloom filter pages evicted from cache" : 0,
"bloom filter pages read into cache" : 0,
"bloom filters in the LSM tree" : 0,
"chunks in the LSM tree" : 0,
"highest merge generation in the LSM tree" : 0,
"queries that could have benefited from a Bloom filter that did not exist" : 0,
"sleep for LSM checkpoint throttle" : 0,
"sleep for LSM merge throttle" : 0,
"total size of bloom filters" : 0
},
"block-manager" : {
"allocations requiring file extension" : 0,
"blocks allocated" : 0,
"blocks freed" : 0,
"checkpoint size" : 0,
"file allocation unit size" : 4096,
"file bytes available for reuse" : 0,
"file magic number" : 120897,
"file major version number" : 1,
"file size in bytes" : 4096,
"minor version number" : 0
},
"btree" : {
"btree checkpoint generation" : 0,
"btree clean tree checkpoint expiration time" : 0,
"column-store fixed-size leaf pages" : 0,
"column-store internal pages" : 0,
"column-store variable-size RLE encoded values" : 0,
"column-store variable-size deleted values" : 0,
"column-store variable-size leaf pages" : 0,
"fixed-record size" : 0,
"maximum internal page key size" : 368,
"maximum internal page size" : 4096,
"maximum leaf page key size" : 2867,
"maximum leaf page size" : 32768,
"maximum leaf page value size" : 67108864,
"maximum tree depth" : 0,
"number of key/value pairs" : 0,
"overflow pages" : 0,
"pages rewritten by compaction" : 0,
"row-store empty values" : 0,
"row-store internal pages" : 0,
"row-store leaf pages" : 0
},
"cache" : {
"bytes currently in the cache" : 182,
"bytes dirty in the cache cumulative" : 0,
"bytes read into cache" : 0,
"bytes written from cache" : 0,
"checkpoint blocked page eviction" : 0,
"checkpoint of history store file blocked non-history store page eviction" : 0,
"data source pages selected for eviction unable to be evicted" : 0,
"eviction walk passes of a file" : 0,
"eviction walk target pages histogram - 0-9" : 0,
"eviction walk target pages histogram - 10-31" : 0,
"eviction walk target pages histogram - 128 and higher" : 0,
"eviction walk target pages histogram - 32-63" : 0,
"eviction walk target pages histogram - 64-128" : 0,
"eviction walk target pages reduced due to history store cache pressure" : 0,
"eviction walks abandoned" : 0,
"eviction walks gave up because they restarted their walk twice" : 0,
"eviction walks gave up because they saw too many pages and found no candidates" : 0,
"eviction walks gave up because they saw too many pages and found too few candidates" : 0,
"eviction walks reached end of tree" : 0,
"eviction walks restarted" : 0,
"eviction walks started from root of tree" : 0,
"eviction walks started from saved location in tree" : 0,
"hazard pointer blocked page eviction" : 0,
"history store table insert calls" : 0,
"history store table insert calls that returned restart" : 0,
"history store table out-of-order resolved updates that lose their durable timestamp" : 0,
"history store table out-of-order updates that were fixed up by reinserting with the fixed timestamp" : 0,
"history store table reads" : 0,
"history store table reads missed" : 0,
"history store table reads requiring squashed modifies" : 0,
"history store table truncation by rollback to stable to remove an unstable update" : 0,
"history store table truncation by rollback to stable to remove an update" : 0,
"history store table truncation to remove an update" : 0,
"history store table truncation to remove range of updates due to key being removed from the data page during reconciliation" : 0,
"history store table truncation to remove range of updates due to out-of-order timestamp update on data page" : 0,
"history store table writes requiring squashed modifies" : 0,
"in-memory page passed criteria to be split" : 0,
"in-memory page splits" : 0,
"internal pages evicted" : 0,
"internal pages split during eviction" : 0,
"leaf pages split during eviction" : 0,
"modified pages evicted" : 0,
"overflow pages read into cache" : 0,
"page split during eviction deepened the tree" : 0,
"page written requiring history store records" : 0,
"pages read into cache" : 0,
"pages read into cache after truncate" : 0,
"pages read into cache after truncate in prepare state" : 0,
"pages requested from the cache" : 0,
"pages seen by eviction walk" : 0,
"pages written from cache" : 0,
"pages written requiring in-memory restoration" : 0,
"tracked dirty bytes in the cache" : 0,
"unmodified pages evicted" : 0
},
"cache_walk" : {
"Average difference between current eviction generation when the page was last considered" : 0,
"Average on-disk page image size seen" : 0,
"Average time in cache for pages that have been visited by the eviction server" : 0,
"Average time in cache for pages that have not been visited by the eviction server" : 0,
"Clean pages currently in cache" : 0,
"Current eviction generation" : 0,
"Dirty pages currently in cache" : 0,
"Entries in the root page" : 0,
"Internal pages currently in cache" : 0,
"Leaf pages currently in cache" : 0,
"Maximum difference between current eviction generation when the page was last considered" : 0,
"Maximum page size seen" : 0,
"Minimum on-disk page image size seen" : 0,
"Number of pages never visited by eviction server" : 0,
"On-disk page image sizes smaller than a single allocation unit" : 0,
"Pages created in memory and never written" : 0,
"Pages currently queued for eviction" : 0,
"Pages that could not be queued for eviction" : 0,
"Refs skipped during cache traversal" : 0,
"Size of the root page" : 0,
"Total number of pages currently in cache" : 0
},
"checkpoint-cleanup" : {
"pages added for eviction" : 0,
"pages removed" : 0,
"pages skipped during tree walk" : 0,
"pages visited" : 0
},
"compression" : {
"compressed page maximum internal page size prior to compression" : 4096,
"compressed page maximum leaf page size prior to compression " : 131072,
"compressed pages read" : 0,
"compressed pages written" : 0,
"page written failed to compress" : 0,
"page written was too small to compress" : 0
},
"cursor" : {
"Total number of entries skipped by cursor next calls" : 0,
"Total number of entries skipped by cursor prev calls" : 0,
"Total number of entries skipped to position the history store cursor" : 0,
"Total number of pages skipped without reading by cursor next calls" : 0,
"Total number of pages skipped without reading by cursor prev calls" : 0,
"Total number of times a search near has exited due to prefix config" : 0,
"bulk loaded cursor insert calls" : 0,
"cache cursors reuse count" : 0,
"close calls that result in cache" : 1,
"create calls" : 1,
"cursor next calls that skip due to a globally visible history store tombstone" : 0,
"cursor next calls that skip greater than or equal to 100 entries" : 0,
"cursor next calls that skip less than 100 entries" : 1,
"cursor prev calls that skip due to a globally visible history store tombstone" : 0,
"cursor prev calls that skip greater than or equal to 100 entries" : 0,
"cursor prev calls that skip less than 100 entries" : 0,
"insert calls" : 0,
"insert key and value bytes" : 0,
"modify" : 0,
"modify key and value bytes affected" : 0,
"modify value bytes modified" : 0,
"next calls" : 1,
"open cursor count" : 0,
"operation restarted" : 0,
"prev calls" : 0,
"remove calls" : 0,
"remove key bytes removed" : 0,
"reserve calls" : 0,
"reset calls" : 2,
"search calls" : 0,
"search history store calls" : 0,
"search near calls" : 0,
"truncate calls" : 0,
"update calls" : 0,
"update key and value bytes" : 0,
"update value size change" : 0
},
"reconciliation" : {
"approximate byte size of timestamps in pages written" : 0,
"approximate byte size of transaction IDs in pages written" : 0,
"dictionary matches" : 0,
"fast-path pages deleted" : 0,
"internal page key bytes discarded using suffix compression" : 0,
"internal page multi-block writes" : 0,
"internal-page overflow keys" : 0,
"leaf page key bytes discarded using prefix compression" : 0,
"leaf page multi-block writes" : 0,
"leaf-page overflow keys" : 0,
"maximum blocks required for a page" : 0,
"overflow values written" : 0,
"page checksum matches" : 0,
"page reconciliation calls" : 0,
"page reconciliation calls for eviction" : 0,
"pages deleted" : 0,
"pages written including an aggregated newest start durable timestamp " : 0,
"pages written including an aggregated newest stop durable timestamp " : 0,
"pages written including an aggregated newest stop timestamp " : 0,
"pages written including an aggregated newest stop transaction ID" : 0,
"pages written including an aggregated newest transaction ID " : 0,
"pages written including an aggregated oldest start timestamp " : 0,
"pages written including an aggregated prepare" : 0,
"pages written including at least one prepare" : 0,
"pages written including at least one start durable timestamp" : 0,
"pages written including at least one start timestamp" : 0,
"pages written including at least one start transaction ID" : 0,
"pages written including at least one stop durable timestamp" : 0,
"pages written including at least one stop timestamp" : 0,
"pages written including at least one stop transaction ID" : 0,
"records written including a prepare" : 0,
"records written including a start durable timestamp" : 0,
"records written including a start timestamp" : 0,
"records written including a start transaction ID" : 0,
"records written including a stop durable timestamp" : 0,
"records written including a stop timestamp" : 0,
"records written including a stop transaction ID" : 0
},
"session" : {
"object compaction" : 0,
"tiered operations dequeued and processed" : 0,
"tiered operations scheduled" : 0,
"tiered storage local retention time (secs)" : 0,
"tiered storage object size" : 0
},
"transaction" : {
"race to read prepared update retry" : 0,
"rollback to stable history store records with stop timestamps older than newer records" : 0,
"rollback to stable inconsistent checkpoint" : 0,
"rollback to stable keys removed" : 0,
"rollback to stable keys restored" : 0,
"rollback to stable restored tombstones from history store" : 0,
"rollback to stable restored updates from history store" : 0,
"rollback to stable sweeping history store keys" : 0,
"rollback to stable updates removed from history store" : 0,
"transaction checkpoints due to obsolete pages" : 0,
"update conflicts" : 0
}
},
"nindexes" : 1,
"indexBuilds" : [ ],
"totalIndexSize" : 4096,
"totalSize" : 8192,
"indexSizes" : {
"_id_" : 4096
},
"scaleFactor" : 1,
"ok" : 1
}

かなり多めの統計情報が表示されますが、よく参照する情報は下記の通りです。

フィールド名説明
sizeメモリに展開されるデータの合計サイズ
countコレクションに入っているデータ件数
storageSizeストレージのサイズ(データ圧縮後のサイズ)
cappedCappedコレクションかどうか(true:Cappedコレクション)
nindexesインデックス数
totalIndexSizeインデックスのデータ量の合計
indexSizes各インデックスのデータ量

コレクションの削除

コレクションを削除します。

[Mongoシェル]

1
2
> db.group.drop()
true

これでコレクションが削除されました。

データ件数によりますが、コレクションの中のデータを全て削除したいときは、コレクションを削除してその後コレクションを再作成する方が早いです。

Pythonで操作

上記の操作をPythonでまとめて行うと、次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pymongo import MongoClient

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得(存在しない場合は作成)
db1 = client.db1

# コレクションの作成(ドキュメントが追加されるまで作成されない)
cl = db1.group

# コレクションの一覧作成
lst_cl = db1.list_collection_names()
print(lst_cl)

# コレクションの統計情報取得
cl_info = db1.command("collstats", "group")
print(cl_info)

# コレクションの削除
db1.drop_collection('group')

次回はコレクションのとして特別なCappedコレクションに関する操作を行います。

MongoDB(2) - データベース操作編

前回はMongoDBを使えるようにセッティングを行いました。

今回はMongoDBのデータベースに関する操作を行ってみます。

データベース作成

データベースの作成を行います。データベース名はdb1とします。

[Mongoシェル]

1
2
> use db1
switched to db db1

useコマンドは、データベースの選択を行います。

ただしデータベースを選択しただけでデータベースが作成されるわけではありません。

データベースを選択した後にコレクションの作成などを行って、はじめてデータベースが作成されます。

データベースの一覧表示

データベースの一覧を表示します。

[Mongoシェル]

1
2
3
4
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB

admin、config、local という3つのデータベースが存在することを確認できます。

データベースの統計情報

データベースの統計情報を表示します。

各種性能を計測したり、運用時にデータ量を確認する場合に便利です。

(useコマンドで選択されているデータベースの情報が表示されます。)

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
> db.stats()
{
"db" : "db1",
"collections" : 0,
"views" : 0,
"objects" : 0,
"avgObjSize" : 0,
"dataSize" : 0,
"storageSize" : 0,
"totalSize" : 0,
"indexes" : 0,
"indexSize" : 0,
"scaleFactor" : 1,
"fileSize" : 0,
"fsUsedSize" : 0,
"fsTotalSize" : 0,
"ok" : 1
}

参照頻度の高い情報を下記の一覧にまとめます。

フィールド名説明
collectionsデータベースの中にあるコレクション数
dataSize圧縮される前のデータサイズ
storageSizeコレクションに割り当てられたスペースの合計サイズ
(データ圧縮されたあとのサイズ)
indexesインデックス数
indexSizeインデックスのデータ量の合計
fileSizeストレージに保存されるファイルのサイズ

データベースの削除

データベースを削除します。

[Mongoシェル]

1
2
> db.dropDatabase()
{ "ok" : 1 }

MongoDBでは簡単にデータベースを作成できるので、一時的なデータベースや不要になったデータベースは削除しておきましょう。

Pythonで操作

上記の操作をPythonでまとめて行うと、次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pymongo import MongoClient

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得(存在しない場合は作成)
db1 = client.db1

# データベースの一覧取得
lst_db = client.database_names()
print(lst_db)

# データベースの統計情報取得
db_info = db1.command("dbstats")
print(db_info)

# データベースの削除
client.drop_database('db1')

次回はコレクションに関する操作を行います。

MongoDB(1) - 準備編

今回からMongoDBに関する記事を書いていきます。

コンソールからの操作とその操作をPythonで行う場合の2種類でMongoDBの動作確認を行います。

(最近Python関連の記事を全く書いていないことに気づきました・・・😨😨)

MongoDBとは

MongoDBは、NoSQLの1つです。

NoSQLの中でもMongoDBはシェアがトップという調査結果があります。

MongoDBが選ばれる理由としては下記が挙げられます。

  • スキーマレスにより保守性が高い
  • 高パフォーマンス
  • RDBと似た操作により学習効率が高い
  • オープンソースのため無料
  • 簡単な設定でスケールアウト(負荷分散)が可能

MongoDBのインストール

下記サイトからMongoDBのインストーラをダウンロードし、MongoDBをインストールします。

各環境に合わせてインストールを行ってください。

MongoDB Community Server - https://www.mongodb.com/try/download/community

(Community Serverが無料版になります。)

コンソールの準備(Mongoシェル)

インストール先にbinフォルダ(ディレクトリ)があり、そこにパスを通しておくとコンソールからMongoシェルを実行することができます。

コンソールからmongoと入力すると、Mongoシェルが起動します。

[コンソール]

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
>mongo
MongoDB shell version v5.0.2
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("958c42c1-e0fe-45ec-b53e-d1a0db49c950") }
MongoDB server version: 5.0.2
================
Warning: the "mongo" shell has been superseded by "mongosh",
which delivers improved usability and compatibility.The "mongo" shell has been deprecated and will be removed in
an upcoming release.
We recommend you begin using "mongosh".
For installation instructions, see
https://docs.mongodb.com/mongodb-shell/install/
================
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
https://docs.mongodb.com/
Questions? Try the MongoDB Developer Community Forums
https://community.mongodb.com
---
The server generated these startup warnings when booting:
2021-08-14T09:24:46.540+09:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
---
---
Enable MongoDB's free cloud-based monitoring service, which will then receive and display
metrics about your deployment (disk utilization, CPU, operation statistics, etc).

The monitoring data will be available on a MongoDB website with a unique URL accessible to you
and anyone you share the URL with. MongoDB may use this information to make product
improvements and to suggest MongoDB products and deployment options to you.

To enable free monitoring, run the following command: db.enableFreeMonitoring()
To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
>

上記のように表示されたら、成功です。

このMongoシェルから、MongoDBを操作することができます。

Python用の準備

PythonからMongoDBを操作するためにはpymongoライブラリを使用します。

pipコマンドでインストールします。

[コンソール]

1
2
3
4
5
6
>pip install pymongo
Collecting pymongo
Downloading pymongo-3.12.0-cp38-cp38-win_amd64.whl (397 kB)
|████████████████████████████████| 397 kB 6.8 MB/s
Installing collected packages: pymongo
Successfully installed pymongo-3.12.0

以上で、コンソールとPythonからMongoDBを利用する準備ができました。


次回から、MongoDBをいろいろと操作していきます。

Ethereum(52) - オーバーフロー⑦(uintの最大値チェック)

今回はuintの最大値に関する動作を確認していきます。

ソースコード

最大値のチェックを行うためのソースコードは下記の通りです。

[ソースコード]

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
pragma solidity ^0.4.11;
contract MarketPlaceOverflow {
address public owner;
uint public stockQuantity;

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

/// 追加在庫数を表示するイベント
event AddStock(uint _addedQuantity);

/// コンストラクタ
function MarketPlaceOverflow() {
owner = msg.sender;
stockQuantity = 0; // 最大値をチェックするため100を0に変更
}

/// 在庫の追加処理
function addStock(uint _addedQuantity) public onlyOwner {
// オーバーフローチェック
require(stockQuantity + _addedQuantity > stockQuantity);

AddStock(_addedQuantity);
stockQuantity += _addedQuantity;
}
}

17行目で初期の在庫数を100から0に変更しています。

これは在庫の追加数としてuintの最大値を設定できるかどうかをテストするためです。

デプロイ

上記ソースのデプロイを行います。

[デプロイ]


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

[コントラクト状態]

初期の在庫数(Stock quantity)が0であることが確認できます。

在庫の追加(最大値)

Add Stcok関数を使って、uintの最大値を設定します。

uintの最大値は115792089237316195423570985008687907853269984665640564039457584007913129639935です。

[在庫数を追加]

追加後の在庫数を確認します。

[在庫数を確認]

問題なくuintの最大値が在庫数(Stock quantity)に設定されていることが分かります。

また画像一番下のイベント情報より正しい数値(uintの最大値)が引数として渡されていることも分かります。

在庫の追加(最大値+1)

次に、uintの最大値を超える数値を設定してみます。

[uintの最大値]+1の数値は115792089237316195423570985008687907853269984665640564039457584007913129639936になります。

[在庫数を追加]

[EXECUTE]ボタンを押すと、トランザクションを発行するための画面が表示されます。

ここでパスワードを入力し、[SEND TRANSACTION]ボタンを押したところ次のようなメッセージが表示されました。

[トランザクション実行画面]

どうやら256バイトを超える値は設定できないようです。

uint8の場合は、それを超える数値を設定できたので、データ型の制限ではなくSolidityの引数の制限だと思われます。(ただの推測です)

以上で、オーバーフローに関する動作確認とその対策に関する記事は終わりにします。

Ethereum(51) - オーバーフロー⑥(2回目の改善確認編)

前回修正したスマートコントラクトを使ってオーバーフローが解消されているかどうかを確認します。

デプロイ

デプロイを行います。

デプロイするのは前回改善したMarket Place Overflowです。

[デプロイ]


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

[コントラクト状態]

在庫数(Stock quantity)が100であることが確認できます。

在庫の追加

Add Stcok関数を使って、(前回NGとなった)在庫数257の追加を行います。

[在庫数を追加]

追加後の在庫数を確認します。

[在庫数を確認]

前回はオーバーフローが発生したため在庫数が101となってしまいましたが、今回は問題なく357となりました。


念のためイベントをを確認し、正しい引数が渡されているかどうかを確認します。

[イベントの確認]

問題なく257が引数として渡されています。

まとめ

オーバーフローの対策として次の2点を改善しました。

  • 在庫数を追加するときに、追加後の在庫数が増えることを確認。
    (オーバーフローが発生すると追加後の在庫数が減ってしまうため)
  • 追加する在庫数と既存の在庫数のデータ型をuint8からuintに変更。
    (uint8の範囲を超える数値が引数として渡されると、意図しない数値に変換されてしまうため)

2つめの修正ですが、扱える数字が増えただけで根本的な解決にはなっていなような気がします。

指定したデータ型を超える数値を指定したら結局またオーバーフローが発生してしまうと考えられるからです。


次回はuintを超える数値を設定して動作確認を行ってみます。

Ethereum(50) - オーバーフロー⑤(2回目の改善編)

前回は、在庫追加数としてuint8の範囲を超える数値を設定してしまったため、とオーバーフローが発生し257が1に変換されてしまいました。

今回はこの問題を解消していきます。

ソースコード(改善2)

改善策としまして、在庫数を追加する関数(AddStock関数)の引数を、データ型uint8からuintに変更してみます。

uint8の範囲は0~255ですが、uintの範囲は0~2の256乗までとかなりの大きな数字となります。

[ソースコード]

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
pragma solidity ^0.4.11;
contract MarketPlaceOverflow {
address public owner;
uint public stockQuantity; // 在庫数 改善2 uint8 ⇒ uint

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

/// 追加在庫数を表示するイベント
event AddStock(uint _addedQuantity); // 改善2 uint8 ⇒ uint

/// コンストラクタ
function MarketPlaceOverflow() {
owner = msg.sender;
stockQuantity = 100;
}

/// 在庫の追加処理
function addStock(uint _addedQuantity) public onlyOwner { // 改善2 uint8 ⇒ uint
// オーバーフローチェック
require(stockQuantity + _addedQuantity > stockQuantity); // 改善1

AddStock(_addedQuantity);
stockQuantity += _addedQuantity;
}
}

追加在庫数(_addedQuantity)の修正箇所は12行目と21行目で、uint8をuintに変更しています。

また追加在庫数と合わせて、現在の在庫数(stockQuantity)のデータ型もuintに変更しておきます。(4行目)


次回はこの改善でオーバーフローが解消しているかどうかの確認してみます。

Ethereum(49) - オーバーフロー④(改善確認編)

前回修正したスマートコントラクトを使ってオーバーフローが解消されているかどうかを確認します。

デプロイ

デプロイを行います。

デプロイするのは前回改善したMarket Place Overflowです。

[デプロイ]


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

[コントラクト状態]

在庫数(Stock quantity)が100であることが確認できます。

在庫の追加①

Add Stcok関数を使って、前回同様に在庫数を156増やしてみます。

確認ダイアログではgas(手数料)に5000000を設定しました。

[在庫数を追加]

追加後の在庫数を確認します。

[在庫数を確認]

前回はオーバーフローが発生したため在庫数が0になってしまいましたが、今回は改善したので在庫の追加が行われず在庫数が100のままになっています。

いちおうオーバーフローが解消しています。


念のためトランザクションも確認します。

[トランザクションの確認]

Gas usedが指定した5000000となっており、最大手数料が消費されているのでトランザクションが失敗したことになります。

在庫の追加②

もう一度在庫の追加を行ってみます。今回は257追加してみます。

[在庫数を追加]

追加後の在庫数を確認します。

[在庫数を確認]

なぜか追加処理が行われ在庫数が101と増えてしまっています。

イベントも確認してみます。

[イベントの確認]

追加の在庫数として257を設定したのですが1となっています。

どうしてでしょうか。

考察

在庫数を追加するAdd Stock関数に257を設定したにも関わらず1という引数が渡されてしまった原因は、引数のデータ型がuint8となっているためです。

データ型のuint8では、0~255までの数値しか表現することができず257が指定されると1に変換されてしまったのです。(桁落ち)

その結果、前回改善した判定が満みたされ在庫追加処理が正常に実行されたということになります。


次回はこの問題を解消するための再度対策を行います。

Ethereum(48) - オーバーフロー③(改善編)

前回オーバーフローが発生したソースを修正します。

ソースコード(改善1)

前回の動作確認では、在庫数(stockQuantity)を追加しているにも関わらず、オーバーフローが発生したため在庫数が減ってしまっていました。

そこで在庫追加後の数が元の在庫数を超えることを確認して、オーバーフローを防いでみます。

[ソースコード]

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
pragma solidity ^0.4.11;
contract MarketPlaceOverflow {
address public owner;
uint8 public stockQuantity; // 在庫数

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

/// 追加在庫数を表示するイベント
event AddStock(uint8 _addedQuantity);

/// コンストラクタ
function MarketPlaceOverflow() {
owner = msg.sender;
stockQuantity = 100;
}

/// 在庫の追加処理
function addStock(uint8 _addedQuantity) public onlyOwner {
// オーバーフローチェック
require(stockQuantity + _addedQuantity > stockQuantity); // 改善1

AddStock(_addedQuantity);
stockQuantity += _addedQuantity;
}
}

23行目で追加後の在庫数が現在の在庫数より大きいことをチェックしています。

次回はこの改善でオーバーフローが解消しているかどうかの確認してみます。

Ethereum(47) - オーバーフロー②(実行編)

前回作成したスマートコントラクトを使ってオーバーフローの動作確認を行います。

アカウンとの役割

今回の動作確認では、アカウントの役割は重要ではないのですが、一応次のようにしておきます。

  • MAIN ACCOUNT (eth.accounts[0])
    コントラクト生成者。マイナー

デプロイ

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

デプロイするのはMarket Place Overflowです。

[デプロイ]


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

[コントラクト状態]

在庫数(Stock quantity)が100であることが確認できます。

在庫の追加

Add Stcok関数を使って、在庫数を156増やしてみます。

[在庫数を追加]

追加後の在庫数を確認します。

[在庫数を確認]

100 + 156 = 256 になるはずですが、0になってしまいました。

下記のイベント確認すると、在庫追加の引数としては問題なく156が渡されたことが分かります。

[イベントの確認]

ではなぜ在庫数が256にならずに0になってしまったのでしょうか。

計算が合わない理由

原因は在庫数(stockQuntity)がunit8で宣言されているためです。

uint8では8ビットの符号なし整数を表します。範囲としては0~255となります。

256になるとオーバーフローが発生し、0となってしまったのです。


次回はこの問題を解消するための対策を行います。

Ethereum(46) - オーバーフロー①(実装編)

オーバーフローは、スマートコントラクトならではの問題ではありませんが意外と見落としがちです。

オーバーフローが発生すると、データの不整合が発生するので注意が必要です。

ソースコード

オーバーフローを確認するためのサンプルソースは以下の通りです。

[ソースコード]

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
pragma solidity ^0.4.11;
contract MarketPlaceOverflow {
address public owner;
uint8 public stockQuantity; // 在庫数

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

/// 追加在庫数を表示するイベント
event AddStock(uint8 _addedQuantity);

/// コンストラクタ
function MarketPlaceOverflow() {
owner = msg.sender;
stockQuantity = 100;
}

/// 在庫の追加処理
function addStock(uint8 _addedQuantity) public onlyOwner {
AddStock(_addedQuantity);
stockQuantity += _addedQuantity;
}
}

マーケットプレイスを想定したコントラクトで、在庫数の追加を行う関数のみを実装しています。

次回は、このスマートコントラクトを使ってオーバーフローになる場合の動作を確認します。