Inside IOST #06: ストレージの利用方法

Takao Wada
50 min readSep 12, 2022

--

Inside IOST for Developers

1. キー/値での利用

IOSTのストレージは、キー/値またはキー /フィールド /値の構造になっています。それぞれについて、

1.1 サンプルの作成

ストレージを利用するサンプルコントラクト storage1.js を用意します。

キー/値でストレージを利用する場合は、storage APIのget/put関数を利用します。ストレージへの書き込みは文字列のみですので、文字列に変換して書き込みます。

ここでは、後で修正可能なように can_update 関数も実装しておきます。また、アノーテーションでABI生成時の引数の型を指定しています。

class Storage1 {
init() {
storage.put("string1", "TEST");
storage.put("number1", "0");
storage.put("bool1", "false");
}
can_update(data) {
return blockchain.requireAuth(blockchain.contractOwner(), "active");
}
putString(s) {
storage.put("string1", s);
}
getString() {
return storage.get("string1");
}
/**
*
* @param {number} n
*/
putNumber(n) {
storage.put("number1", n.toString());
}
getNumber() {
return storage.get("number1");
}
/**
*
* @param {bool} b
*/
putBool(b) {
storage.put("bool1", b).toString();
}
getBool() {
return storage.get("bool1");
}
}
module.exports = Storage1;

ABIファルを作成します。

$ docker exec -t iserver iwallet compile storage1.js
Successfully generated abi file as: storage1.js.abi

ABIファイル(storage1.js.abi)は、次のようになります。アノーテーションで指定した引数の型が反映されていることがわかります。

{
"lang": "javascript",
"version": "1.0.0",
"abi": [
{
"name": "can_update",
"args": [
"string"
],
"amountLimit": []
},
{
"name": "putString",
"args": [
"string"
],
"amountLimit": []
},
{
"name": "getString",
"args": [],
"amountLimit": []
},
{
"name": "putNumber",
"args": [
"number"
],
"amountLimit": []
},
{
"name": "getNumber",
"args": [],
"amountLimit": []
},
{
"name": "putBool",
"args": [
"bool"
],
"amountLimit": []
},
{
"name": "getBool",
"args": [],
"amountLimit": []
}
]
}

コントラクトをローカルノードに公開します。

$ docker exec -t iserver iwallet publish storage1.js storage1.js.abi -a admin --chain_id 1020
Sending transaction...
Transaction:
{
"time": "1662970438013624426",
"expiration": "1662970528013624426",
"gasRatio": 1,
"gasLimit": 1000000,
"delay": "0",
"chainId": 1020,
"actions": [
{
"contract": "system.iost",
"actionName": "updateCode",
"data": "[\"{\\\"ID\\\":\\\"ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc\\\",\\\"info\\\":{\\\"lang\\\":\\\"javascript\\\",\\\"version\\\":\\\"1.0.0\\\",\\\"abi\\\":[{\\\"name\\\":\\\"can_update\\\",\\\"args\\\":[\\\"string\\\"]},{\\\"name\\\":\\\"putString\\\",\\\"args\\\":[\\\"string\\\"]},{\\\"name\\\":\\\"getString\\\"},{\\\"name\\\":\\\"putNumber\\\",\\\"args\\\":[\\\"number\\\"]},{\\\"name\\\":\\\"getNumber\\\"},{\\\"name\\\":\\\"putBool\\\",\\\"args\\\":[\\\"bool\\\"]},{\\\"name\\\":\\\"getBool\\\"}]},\\\"code\\\":\\\"class Storage1 {\\\\n init() {\\\\n storage.put(\\\\\\\"string1\\\\\\\", \\\\\\\"TEST\\\\\\\");\\\\n storage.put(\\\\\\\"number1\\\\\\\", \\\\\\\"0\\\\\\\");\\\\n storage.put(\\\\\\\"bool1\\\\\\\", \\\\\\\"false\\\\\\\");\\\\n }\\\\n\\\\n can_update(data) {\\\\n return blockchain.requireAuth(blockchain.contractOwner(), \\\\\\\"active\\\\\\\");\\\\n }\\\\n\\\\n putString(s) {\\\\n storage.put(\\\\\\\"string1\\\\\\\", s);\\\\n }\\\\n\\\\n getString() {\\\\n return storage.get(\\\\\\\"string1\\\\\\\");\\\\n }\\\\n\\\\n /**\\\\n * \\\\n * @param {number} n\\\\n */\\\\n putNumber(n) {\\\\n storage.put(\\\\\\\"number1\\\\\\\", n.toString());\\\\n }\\\\n\\\\n getNumber() {\\\\n return storage.get(\\\\\\\"number1\\\\\\\");\\\\n }\\\\n\\\\n /**\\\\n * \\\\n * @param {bool} b\\\\n */\\\\n putBool(b) {\\\\n storage.put(\\\\\\\"bool1\\\\\\\", b.toString());\\\\n }\\\\n\\\\n getBool() {\\\\n return storage.get(\\\\\\\"bool1\\\\\\\");\\\\n }\\\\n}\\\\nmodule.exports = Storage1;\\\\n\\\"}\",\"\"]"
}
],
"amountLimit": [
{
"token": "*",
"value": "unlimited"
}
],
"signers": [],
"signatures": [],
"publisher": "admin",
"publisherSigs": [
{
"algorithm": "ED25519",
"signature": "02cO4K9Pp/lVWo9Humtm8EQqkbLQPyhElo+wBopH2QXYd7x76pzEGq3sNTdz5KG+MTdrFkI/u22vYi6FRzB7DA==",
"publicKey": "6BK1LqmtXLqamvA6/MbylCpFJLDfPANE3BlQcoMWcMQ="
}
]
}
Connecting to server localhost:30002 ...
Transaction has been sent.
The transaction hash is: 5fcRFqxdB5TtwPSoYTXR66PTXP9empDAzQ8D6pU2Tg9Y
Checking transaction receipt...
Transaction receipt:
{
"txHash": "5fcRFqxdB5TtwPSoYTXR66PTXP9empDAzQ8D6pU2Tg9Y",
"gasUsage": 296158,
"ramUsage": {},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[]"
],
"receipts": []
}
SUCCESS! Transaction has been irreversible
The contract id is: ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc

1.2 初期状態

発行したコントラクトの状態を調べてみます。

$ curl -X POST http://127.0.0.1:30001/getContractStorage -d '{"id":"ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc","key":"string1","by_longest_chain":true}' | jq .
curl -X POST
http://127.0.0.1:30001/getContractStorage -d '{"id":"ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc","key":"number1","by_longest_chain":true}' | jq .
curl -X POST
http://127.0.0.1:30001/getContractStorage -d '{"id":"ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc","key":"bool1","by_longest_chain":true}' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 201 100 100 100 101 1751 1769 --:--:-- --:--:-- --:--:-- 4020
{
"data": "TEST",
"block_hash": "E3VWQaRKbjWiQtgamiKU2yoViwbEKGXQfzarGbmJ622f",
"block_number": "72634"
}
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 198 100 97 100 101 3509 3654 --:--:-- --:--:-- --:--:-- 13200
{
"data": "0",
"block_hash": "E3VWQaRKbjWiQtgamiKU2yoViwbEKGXQfzarGbmJ622f",
"block_number": "72634"
}
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 200 100 101 100 99 8170 8009 --:--:-- --:--:-- --:--:-- 28571
{
"data": "false",
"block_hash": "E3VWQaRKbjWiQtgamiKU2yoViwbEKGXQfzarGbmJ622f",
"block_number": "72634"
}

1.3 データの書き込み

  • 引数 “UPDATED”を渡して、putString 関数を呼び出して、データを書き込み
  • 引数 1 を渡して、putNumber 関数を呼び出して、データを書き込み
  • 引数 trueを渡して、putBool 関数を呼び出して、データを書き込み
$ docker exec -t iserver iwallet call "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc" "putString" "[\"UPDATED\"]" --account admin --chain_id 1020
Sending transaction...
Transaction:
{
"time": "1662970235303612346",
"expiration": "1662970325303612346",
"gasRatio": 1,
"gasLimit": 1000000,
"delay": "0",
"chainId": 1020,
"actions": [
{
"contract": "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc",
"actionName": "putString",
"data": "[\"UPDATED\"]"
}
],
"amountLimit": [
{
"token": "*",
"value": "unlimited"
}
],
"signers": [],
"signatures": [],
"publisher": "admin",
"publisherSigs": [
{
"algorithm": "ED25519",
"signature": "kOgHA1EQhIXBJb0FTLkMPV5K3OnuAZEK5bDESjHgCd3E537jNIp3F3HPXSU3LeMyoE9FrHm9L1eMP+DVDpBCDg==",
"publicKey": "6BK1LqmtXLqamvA6/MbylCpFJLDfPANE3BlQcoMWcMQ="
}
]
}
Connecting to server localhost:30002 ...
Transaction has been sent.
The transaction hash is: 5PFZkaKrXUFePis8QBN3KBbhPKaEHfmSeWHnZXWe6o8V
Checking transaction receipt...
Transaction receipt:
{
"txHash": "5PFZkaKrXUFePis8QBN3KBbhPKaEHfmSeWHnZXWe6o8V",
"gasUsage": 33906,
"ramUsage": {
"ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc": "3"
},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[\"\"]"
],
"receipts": []
}
SUCCESS! Transaction has been irreversible$ docker exec -t iserver iwallet call "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc" "putNumber" "[1]" --account admin --chain_id 1020
Sending transaction...
Transaction:
{
"time": "1662970280501499631",
"expiration": "1662970370501499631",
"gasRatio": 1,
"gasLimit": 1000000,
"delay": "0",
"chainId": 1020,
"actions": [
{
"contract": "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc",
"actionName": "putNumber",
"data": "[1]"
}
],
"amountLimit": [
{
"token": "*",
"value": "unlimited"
}
],
"signers": [],
"signatures": [],
"publisher": "admin",
"publisherSigs": [
{
"algorithm": "ED25519",
"signature": "YbJtsL1mQCmOCLPoU1+ORXUJprbKKXFUZzJaa+u6xN4IkUDbiceIdtXfJAME3BpTz7MVGiOD/Ut/Q+L9Aq7hBg==",
"publicKey": "6BK1LqmtXLqamvA6/MbylCpFJLDfPANE3BlQcoMWcMQ="
}
]
}
Connecting to server localhost:30002 ...
Transaction has been sent.
The transaction hash is: Bnp3kMGbqHTrwvr7p264769sbhe8SAmcqf6ETVpQmomA
Checking transaction receipt...
Transaction receipt:
{
"txHash": "Bnp3kMGbqHTrwvr7p264769sbhe8SAmcqf6ETVpQmomA",
"gasUsage": 33834,
"ramUsage": {},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[\"\"]"
],
"receipts": []
}
SUCCESS! Transaction has been irreversible$ docker exec -t iserver iwallet call "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc" "putBool" "[true]" --account admin --chain_id 1020
Sending transaction...
Transaction:
{
"time": "1662970699176934089",
"expiration": "1662970789176934089",
"gasRatio": 1,
"gasLimit": 1000000,
"delay": "0",
"chainId": 1020,
"actions": [
{
"contract": "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc",
"actionName": "putBool",
"data": "[true]"
}
],
"amountLimit": [
{
"token": "*",
"value": "unlimited"
}
],
"signers": [],
"signatures": [],
"publisher": "admin",
"publisherSigs": [
{
"algorithm": "ED25519",
"signature": "LmeZ5LtSPRnMyF+Z9k7XAM9sBji9guspH6fEwlH9qGVlBWJ5XptAd2PRKpJQaOX4sdsVp+vkTVm6Kzhdbsh1Dw==",
"publicKey": "6BK1LqmtXLqamvA6/MbylCpFJLDfPANE3BlQcoMWcMQ="
}
]
}
Connecting to server localhost:30002 ...
Transaction has been sent.
The transaction hash is: 2Rice6hnKRsHJ87LwNWfHiUqEDm7JkkfidTkRmkXFVre
Checking transaction receipt...
Transaction receipt:
{
"txHash": "2Rice6hnKRsHJ87LwNWfHiUqEDm7JkkfidTkRmkXFVre",
"gasUsage": 33844,
"ramUsage": {
"ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc": "-1"
},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[\"\"]"
],
"receipts": []
}
SUCCESS! Transaction has been irreversible

では、値を確認してみましょう。文字列で “1” になっています。

1.4 データの読み出し

まず、iwalletでデータを読み出してみます。

  • string1 データ
  • number1 データ
  • bool1 データ
$ docker exec -t iserver iwallet call "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc" "getString" "[]" --account admin --chain_id 1020
Sending transaction...
Transaction:
{
"time": "1662970876894353046",
"expiration": "1662970966894353046",
"gasRatio": 1,
"gasLimit": 1000000,
"delay": "0",
"chainId": 1020,
"actions": [
{
"contract": "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc",
"actionName": "getString",
"data": "[]"
}
],
"amountLimit": [
{
"token": "*",
"value": "unlimited"
}
],
"signers": [],
"signatures": [],
"publisher": "admin",
"publisherSigs": [
{
"algorithm": "ED25519",
"signature": "h9XtQ7l3xXlYznp/DlVs2bUA541mzvlnFzBrM2Yv4X9b4bsijrK6vXesLMg35BSInHTWRJDi6MG2Idm7mpO9Bw==",
"publicKey": "6BK1LqmtXLqamvA6/MbylCpFJLDfPANE3BlQcoMWcMQ="
}
]
}
Connecting to server localhost:30002 ...
Transaction has been sent.
The transaction hash is: 5AgbiJpeZyFf5krpS3ckbngysBwdgrajN1xU2N5m5v5g
Checking transaction receipt...
Transaction receipt:
{
"txHash": "5AgbiJpeZyFf5krpS3ckbngysBwdgrajN1xU2N5m5v5g",
"gasUsage": 33886,
"ramUsage": {},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[\"UPDATED\"]"
],
"receipts": []
}
SUCCESS! Transaction has been irreversible$ docker exec -t iserver iwallet call "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc" "getNumber" "[]" --account admin --chain_id 1020
Sending transaction...
Transaction:
{
"time": "1662971005424463633",
"expiration": "1662971095424463633",
"gasRatio": 1,
"gasLimit": 1000000,
"delay": "0",
"chainId": 1020,
"actions": [
{
"contract": "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc",
"actionName": "getNumber",
"data": "[]"
}
],
"amountLimit": [
{
"token": "*",
"value": "unlimited"
}
],
"signers": [],
"signatures": [],
"publisher": "admin",
"publisherSigs": [
{
"algorithm": "ED25519",
"signature": "477nXsc75VEH9f6FToSJWwxGdtA+DOAPYzALHc15J4TEYpvv0jQTWEyMN7C8r1mpigP3WjZj7gIx9b6TsjWYAA==",
"publicKey": "6BK1LqmtXLqamvA6/MbylCpFJLDfPANE3BlQcoMWcMQ="
}
]
}
Connecting to server localhost:30002 ...
Transaction has been sent.
The transaction hash is: EQMyF3ydwqB4sF6imd4RUmucAyDCMjfWGW2UPdyzijMW
Checking transaction receipt...
Transaction receipt:
{
"txHash": "EQMyF3ydwqB4sF6imd4RUmucAyDCMjfWGW2UPdyzijMW",
"gasUsage": 33826,
"ramUsage": {},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[\"1\"]"
],
"receipts": []
}
SUCCESS! Transaction has been irreversible$ docker exec -t iserver iwallet call "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc" "getBool" "[]" --account admin --chain_id 1020
Sending transaction...
Transaction:
{
"time": "1662971050816067501",
"expiration": "1662971140816067501",
"gasRatio": 1,
"gasLimit": 1000000,
"delay": "0",
"chainId": 1020,
"actions": [
{
"contract": "ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc",
"actionName": "getBool",
"data": "[]"
}
],
"amountLimit": [
{
"token": "*",
"value": "unlimited"
}
],
"signers": [],
"signatures": [],
"publisher": "admin",
"publisherSigs": [
{
"algorithm": "ED25519",
"signature": "SrPcZX6OgjaJS75NjD5NQQzVuuDhPNrMjeOSZeHYP0g+MBVkkuFYzPK3K7fa3tXXx5LIVt6Sjwv5dbgHaE9qCQ==",
"publicKey": "6BK1LqmtXLqamvA6/MbylCpFJLDfPANE3BlQcoMWcMQ="
}
]
}
Connecting to server localhost:30002 ...
Transaction has been sent.
The transaction hash is: GGz3oPdrap1fZxHsUqUsxZgFceg2r6vgvuLN2WXw8oTM
Checking transaction receipt...
Transaction receipt:
{
"txHash": "GGz3oPdrap1fZxHsUqUsxZgFceg2r6vgvuLN2WXw8oTM",
"gasUsage": 33836,
"ramUsage": {},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[\"true\"]"
],
"receipts": []
}
SUCCESS! Transaction has been irreversible

1.5 JSON-RPC で読み出し

JSON-RPC API のgetContractStorage API でストレージの値を読み出して見ます。キーとフィールドを指定して、ストレージの個別のフィールドデータを取得します。この場合は、フィールドに直接データが入っている構造のなので、指定したフィールドの情報は無視されますので、指定しなくてもかまいません。

この方法であれば、GAS 代が不要なので、読み出しの場合は、できるだけこちらを利用するようにします。

$ curl -X POST http://127.0.0.1:30001/getContractStorage -d '{"id":"ContractDvbrQ2dmQCRE6q1CwQKkqtmDMmS2s9BZYnnwFvL5sEpc","key":"number1","by_longest_chain":true}' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 198 100 97 100 101 1127 1174 --:--:-- --:--:-- --:--:-- 2506
{
"data": "1",
"block_hash": "H1nk9BbepxHbznsDBfDbBtDwrUVpuZeVTBYBDvxKa7yg",
"block_number": "71355"
}

2. キー/フィールド/値での利用

2.1 サンプルの作成

ストレージを利用するサンプルコントラクト storage2.js を用意します。

キー/フィールド/値でストレージを利用する場合は、storage APIのmapGet/mapPut関数を利用します。。

class Storage2 {
init() {
}
can_update(data) {
return blockchain.requireAuth(blockchain.contractOwner(), "active");
}
putString(f, s) {
storage.mapPut("string2", f, s);
}
getString(f) {
return storage.mapGet("string2", f);
}
/**
*
* @param {string} f
* @param {number} n
*/
putNumber(f, n) {
storage.mapPut("number2", n.toString());
}
getNumber(f) {
return storage.mapGet("number2", f);
}
/**
*
* @param {string} f
* @param {bool} b
*/
putBool(f, b) {
storage.mapPut("bool2", f, b.toString());
}
getBool(f) {
return storage.mapGet("bool2", f);
}
}
module.exports = Storage2;

同様にコンパイルして、ABIファイル(storage2.js.abi)は次のようになります。

{
"lang": "javascript",
"version": "1.0.0",
"abi": [
{
"name": "can_update",
"args": [
"string"
],
"amountLimit": []
},
{
"name": "putString",
"args": [
"string",
"string"
],
"amountLimit": []
},
{
"name": "getString",
"args": [
"string"
],
"amountLimit": []
},
{
"name": "putNumber",
"args": [
"string",
"number"
],
"amountLimit": []
},
{
"name": "getNumber",
"args": [
"string"
],
"amountLimit": []
},
{
"name": "putBool",
"args": [
"string",
"bool"
],
"amountLimit": []
},
{
"name": "getBool",
"args": [
"string"
],
"amountLimit": []
}
]
}

コントラクトを公開します。

$ docker exec -t iserver iwallet publish storage2.js storage2.js.abi -a admin --chain_id 1020
Sending transaction...
Transaction:
{
"time": "1662973954370918928",
"expiration": "1662974044370918928",
"gasRatio": 1,
"gasLimit": 1000000,
"delay": "0",
"chainId": 1020,
"actions": [
{
"contract": "system.iost",
"actionName": "updateCode",
"data": "[\"{\\\"ID\\\":\\\"ContractA7wZ6ASs64DJ3fBh2w23gWHs6t11jMDTdSM72iY9p5to\\\",\\\"info\\\":{\\\"lang\\\":\\\"javascript\\\",\\\"version\\\":\\\"1.0.0\\\",\\\"abi\\\":[{\\\"name\\\":\\\"can_update\\\",\\\"args\\\":[\\\"string\\\"]},{\\\"name\\\":\\\"putString\\\",\\\"args\\\":[\\\"string\\\",\\\"string\\\"]},{\\\"name\\\":\\\"getString\\\",\\\"args\\\":[\\\"string\\\"]},{\\\"name\\\":\\\"putNumber\\\",\\\"args\\\":[\\\"string\\\",\\\"number\\\"]},{\\\"name\\\":\\\"getNumber\\\",\\\"args\\\":[\\\"string\\\"]},{\\\"name\\\":\\\"putBool\\\",\\\"args\\\":[\\\"string\\\",\\\"bool\\\"]},{\\\"name\\\":\\\"getBool\\\",\\\"args\\\":[\\\"string\\\"]}]},\\\"code\\\":\\\"class Storage2 {\\\\n init() {\\\\n }\\\\n\\\\n can_update(data) {\\\\n return blockchain.requireAuth(blockchain.contractOwner(), \\\\\\\"active\\\\\\\");\\\\n }\\\\n\\\\n putString(f, s) {\\\\n storage.mapPut(\\\\\\\"string2\\\\\\\", f, s);\\\\n }\\\\n\\\\n getString(f) {\\\\n return storage.mapGet(\\\\\\\"string2\\\\\\\", f);\\\\n }\\\\n\\\\n /**\\\\n * \\\\n * @param {string} f\\\\n * @param {number} n\\\\n */\\\\n putNumber(f, n) {\\\\n storage.mapPut(\\\\\\\"number2\\\\\\\", f, n.toString());\\\\n }\\\\n\\\\n getNumber(f) {\\\\n return storage.mapGet(\\\\\\\"number2\\\\\\\", f);\\\\n }\\\\n\\\\n /**\\\\n * \\\\n * @param {string} f\\\\n * @param {bool} b\\\\n */\\\\n putBool(f, b) {\\\\n storage.mapPut(\\\\\\\"bool2\\\\\\\", f, b.toString());\\\\n }\\\\n\\\\n getBool(f) {\\\\n return storage.mapGet(\\\\\\\"bool2\\\\\\\", f);\\\\n }\\\\n}\\\\nmodule.exports = Storage2;\\\\n\\\"}\",\"\"]"
}
],
"amountLimit": [
{
"token": "*",
"value": "unlimited"
}
],
"signers": [],
"signatures": [],
"publisher": "admin",
"publisherSigs": [
{
"algorithm": "ED25519",
"signature": "P+YfXAo5dLwmodgW/TY7mJjx/Sbn6GP0wZoVvc34M5JcTIqnlXauq7NQJzqOh9qsWRCos0yhFY+psKxHvMrfBQ==",
"publicKey": "6BK1LqmtXLqamvA6/MbylCpFJLDfPANE3BlQcoMWcMQ="
}
]
}
Connecting to server localhost:30002 ...
Transaction has been sent.
The transaction hash is: DXDDUQ4CxL3feeX9Z1gohoEtSrkpB3jhPVz74zAnXHjC
Checking transaction receipt...
Transaction receipt:
{
"txHash": "DXDDUQ4CxL3feeX9Z1gohoEtSrkpB3jhPVz74zAnXHjC",
"gasUsage": 295668,
"ramUsage": {
"admin": "3"
},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[]"
],
"receipts": []
}
SUCCESS! Transaction has been irreversible
The contract id is: ContractA7wZ6ASs64DJ3fBh2w23gWHs6t11jMDTdSM72iY9p5to

2.1 キーに対応するフィールド一覧の取得(/getContractStorageFields)

ストレージに格納されているキーの一覧を取得します。ただし、最大256までしか取得できません。キーを指定してフィールドを取り出すだけの処理しかしないのであればいいのですが、もし、256以上ある一覧情報の取得が必要な場合は、コントラクト内部に自前で実装する必要があります。

2.2 初期状態

とりあえず、数値データの初期状態を取得してみます。

$ curl -X POST http://127.0.0.1:30001/getContractStorageFields -d '{"id":"ContractA7wZ6ASs64DJ3fBh2w23gWHs6t11jMDTdSM72iY9p5to","key":"number2","by_longest_chain":true}' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 199 100 98 100 101 1778 1833 --:--:-- --:--:-- --:--:-- 4522
{
"fields": [],
"block_hash": "A939eXnj3MYoLaad1jUHsRE7A511sHa6xreZf2J67zFv",
"block_number": "76983"
}

数値データを書きこんでみます。

$ docker exec -t iserver iwallet call "ContractA7wZ6ASs64DJ3fBh2w23gWHs6t11jMDTdSM72iY9p5to" "putNumber" "[\"NUM1\", 1]" --account admin --chain_id 1020
Sending transaction...
Transaction:
{
"time": "1662974009192567259",
"expiration": "1662974099192567259",
"gasRatio": 1,
"gasLimit": 1000000,
"delay": "0",
"chainId": 1020,
"actions": [
{
"contract": "ContractA7wZ6ASs64DJ3fBh2w23gWHs6t11jMDTdSM72iY9p5to",
"actionName": "putNumber",
"data": "[\"NUM1\",1]"
}
],
"amountLimit": [
{
"token": "*",
"value": "unlimited"
}
],
"signers": [],
"signatures": [],
"publisher": "admin",
"publisherSigs": [
{
"algorithm": "ED25519",
"signature": "IsLQUwx0vCpFTmepsKmRL8N0L0n+UKaev6shrKsJHYWEXToqJlxfmiUNyL1TjLLlT3uYcWQG7CbUbHqZjwDYBw==",
"publicKey": "6BK1LqmtXLqamvA6/MbylCpFJLDfPANE3BlQcoMWcMQ="
}
]
}
Connecting to server localhost:30002 ...
Transaction has been sent.
The transaction hash is: 4ZpxqXKSHZ97CZbeFoLywPDP2Cqu2XRznLNvj8i6B5Y4
Checking transaction receipt...
Transaction receipt:
{
"txHash": "4ZpxqXKSHZ97CZbeFoLywPDP2Cqu2XRznLNvj8i6B5Y4",
"gasUsage": 33904,
"ramUsage": {
"ContractA7wZ6ASs64DJ3fBh2w23gWHs6t11jMDTdSM72iY9p5to": "123"
},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[\"\"]"
],
"receipts": []
}
SUCCESS! Transaction has been irreversible
drjiro@M1Pro14 ~/Dropbox/Private/iost_programming

結果は次のように変わります。

{
"fields": [
"NUM1"
],
"block_hash": "G69kjwticktLEJDvAu6zE2xNK4Vfc3a5af1FbZLdsk2x",
"block_number": "77433"
}

2.2 データの読み出し

iwalletでデータを読み出してみます。

$ docker exec -t iserver iwallet call "ContractA7wZ6ASs64DJ3fBh2w23gWHs6t11jMDTdSM72iY9p5to" "getNumber" "[\"NUM1\"]" --account admin --chain_id 1020
Sending transaction...
Transaction:
{
"time": "1662974769417933097",
"expiration": "1662974859417933097",
"gasRatio": 1,
"gasLimit": 1000000,
"delay": "0",
"chainId": 1020,
"actions": [
{
"contract": "ContractA7wZ6ASs64DJ3fBh2w23gWHs6t11jMDTdSM72iY9p5to",
"actionName": "getNumber",
"data": "[\"NUM1\"]"
}
],
"amountLimit": [
{
"token": "*",
"value": "unlimited"
}
],
"signers": [],
"signatures": [],
"publisher": "admin",
"publisherSigs": [
{
"algorithm": "ED25519",
"signature": "8PdijwIWenAffsGbtWsDRYkx3lyrxRFXdMXMZUIOAZLUuPIqZnDkGs/nRYsRrOHRAocNysdoJZf6+miGWfx9AQ==",
"publicKey": "6BK1LqmtXLqamvA6/MbylCpFJLDfPANE3BlQcoMWcMQ="
}
]
}
Connecting to server localhost:30002 ...
Transaction has been sent.
The transaction hash is: ERpkBnEGSE4SstZrqCFyXXdgKQJLDXsZSNu7dSxouq1S
Checking transaction receipt...
Transaction receipt:
{
"txHash": "ERpkBnEGSE4SstZrqCFyXXdgKQJLDXsZSNu7dSxouq1S",
"gasUsage": 33886,
"ramUsage": {},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[\"1\"]"
],
"receipts": []
}
SUCCESS! Transaction has been irreversible

2.2 JSON-RPC で読み出し(/getContractStorage)

JSON-RPC API のgetContractStorage API で、ストレージの値を読み出して見ます。ストレージに格納されているキーとフィールドの値を取得します。指定したキーまたはフィールドが見つからない場合は、null になります。

$ curl -X POST http://127.0.0.1:30001/getContractStorage -d '{"id":"ContractA7wZ6ASs64DJ3fBh2w23gWHs6t11jMDTdSM72iY9p5to","key":"number2","field":"NUM1","by_longest_chain":true}' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 213 100 97 100 116 3878 4638 --:--:-- --:--:-- --:--:-- 11210
{
"data": "1",
"block_hash": "9f43v9wHCzydziYDZ1iy6NtS9xAhhQD6gbnd7V7fwivX",
"block_number": "77868"
}

3. 組み込みコントラクトのストレージ情報を取得する

3.1 アカウント情報を取得する

では、auth.iost コントラクト内のキー auth のフィールド一覧を取得してみます。これは、現在のアカウトの一覧になります。

$  curl -X POST http://127.0.0.1:30001/getContractStorageFields -d '{"id":"auth.iost","key":"auth","by_longest_chain":true}' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 219 100 164 100 55 72 24 0:00:02 0:00:02 --:--:-- 97
{
"fields": [
"admin",
"foundation",
"producer000",
"deadaddr",
"bobby",
"alice"
],
"block_hash": "FfjmBei5CTjJtw7tvShEvDhNSAFAc4QxF79NeE8G1S1F",
"block_number": "48410"
}

3. 2 アカウント情報の取得

では、auth.iost コントラクト内のキー auth 、フィールド: alice の情報を取得してみます。これは文字列ですが、実際にはJSONデータですので文字列をJSONに戻して利用します。

逆にいえば、ストレージにオブジェクトのような構造のデータを保存したい場合は、JSONを文字列化して格納する方法を使います。

$ curl -X POST http://127.0.0.1:30001/getContractStorage -d '{"id":"auth.iost","key":"auth","field":"alice","by_longest_chain":true}' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 580 100 509 100 71 16267 2269 --:--:-- --:--:-- --:--:-- 34117
{
"data": "{\"id\":\"alice\",\"referrer\":\"admin\",\"permissions\":{\"active\":{\"name\":\"active\",\"groups\":[],\"items\":[{\"id\":\"BnmmMpVRaFcC2aUuE5TeVe8fc5qTven25cjcyzdstdvM\",\"is_key_pair\":true,\"weight\":100}],\"threshold\":100},\"owner\":{\"name\":\"owner\",\"groups\":[],\"items\":[{\"id\":\"BnmmMpVRaFcC2aUuE5TeVe8fc5qTven25cjcyzdstdvM\",\"is_key_pair\":true,\"weight\":100}],\"threshold\":100}},\"groups\":{}}",
"block_hash": "EUUWQbwpJ1MXViv1KxpnqDg4DT28eEt9AiqnUiRyyb6Y",
"block_number": "48957"
}

4. コントラクト情報の取得(/getContract/{id}/{by_longest_chain})

4.1 自作コントラクト情報の取得

コントラクト自体の情報も JSON-RPC で取得できます。

取得できるのは、次の情報です。

  • id: コントラクトID
  • code: コントラクトコード
  • language: コントラクトの使用言語
  • version: コントラクトのバージョン
  • abis: コントラクトのABI
$ curl http://127.0.0.1:30001/getContract/ContractFzSQjRxekmmsoqthbThsE9oLsnHZ8msfe99DwzJWtRHV/true | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 568 100 568 0 0 7946 0 --:--:-- --:--:-- --:--:-- 9161
{
"id": "ContractFzSQjRxekmmsoqthbThsE9oLsnHZ8msfe99DwzJWtRHV",
"code": "_IOSTInstruction_counter.incr(156);class Hello {\n init() {\n }\n say(to) {\n _IOSTInstruction_counter.incr(16.7);return _IOSTBinaryOp(_IOSTBinaryOp('Hi, ', to, '+'), '.', '+');\n }\n}\n_IOSTInstruction_counter.incr(7);module.exports = Hello;",
"language": "javascript",
"version": "1.0.0",
"orig_code": "class Hello {\n init() {}\n say(to) {\n return \"Hi, \" + to + \".\"\n }\n}\nmodule.exports = Hello;\n",
"abis": [
{
"name": "say",
"args": [
"string"
],
"amount_limit": []
}
]
}

4.2 組み込みコントラクトの情報の取得

組み込みのコントラクトの情報も取得できます。例として、token.iost というトークン発行用のコントラクトの情報を表示してみます。

$ curl http://127.0.0.1:30001/getContract/token.iost/true | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1109 100 1109 0 0 59463 0 --:--:-- --:--:-- --:--:-- 79214
{
"id": "token.iost",
"code": "",
"language": "native",
"version": "1.0.0",
"orig_code": "codes",
"abis": [
{
"name": "balanceOf",
"args": [
"string",
"string"
],
"amount_limit": [
{
"token": "*",
"value": "unlimited"
}
]
},
{
"name": "can_update",
"args": [
"string"
],
"amount_limit": [
{
"token": "*",
"value": "unlimited"
}
]
},
{
"name": "create",
"args": [
"string",
"string",
"number",
"json"
],
"amount_limit": [
{
"token": "*",
"value": "unlimited"
}
]
},
{
"name": "destroy",
"args": [
"string",
"string",
"string"
],
"amount_limit": [
{
"token": "*",
"value": "unlimited"
}
]
},
{
"name": "issue",
"args": [
"string",
"string",
"string"
],
"amount_limit": [
{
"token": "*",
"value": "unlimited"
}
]
},
{
"name": "supply",
"args": [
"string"
],
"amount_limit": [
{
"token": "*",
"value": "unlimited"
}
]
},
{
"name": "totalSupply",
"args": [
"string"
],
"amount_limit": [
{
"token": "*",
"value": "unlimited"
}
]
},
{
"name": "transfer",
"args": [
"string",
"string",
"string",
"string",
"string"
],
"amount_limit": [
{
"token": "*",
"value": "unlimited"
}
]
},
{
"name": "transferFreeze",
"args": [
"string",
"string",
"string",
"string",
"number",
"string"
],
"amount_limit": [
{
"token": "*",
"value": "unlimited"
}
]
}
]
}

関連記事

--

--

Takao Wada
Takao Wada

Written by Takao Wada

Co-Founder and CEO of Eversystem Inc., Japan

No responses yet