簡単な銀行口座申請
このページは英語版のページが機械翻訳されたものです。英語版との間に矛盾または不一致がある場合は、英語版を正としてください。
概要
これは単純な銀行口座アプリケーションであり、scalardl
リポジトリ にあります。ユーザーが実行できるアクションは、アカウントの作成、アカウント履歴の表示、アカウントへの資金の入金、アカウントからの資金の引き出し、アカウント間の資金の転送です。 アカウントで実行されたすべてのアクションは ScalarDL に記録されます。これは、ブロックチェーンがブロックを記録する方法と同様に、アカウント履歴が改ざん明示的な方法で記録されることを意味します。 これは、アカウント履歴が (意図的かどうかに関わらず) 変更された場合、これを検出することが可能であることを意味します。
ここでは物事を簡単にするために、銀行がすべての契約を実行するための秘密鍵を保持していると仮定します (これがどのように機能するかについては、以下を参照してください)。 おそらくこれは、この銀行アプリケーションを実際に使用する方法ではありません。 この場合、悪意のあ るアカウント マネージャーがユーザーのアカウント履歴を実際に変更する可能性があります。たとえば、単にアカウントを再作成し、偽のデータを入力するだけです。 より意味のある設定は、銀行が口座に入金するための秘密鍵を所有し、各ユーザーが自分の秘密鍵を使用して引き出しと送金の契約を登録することです。 その場合、銀行のみが口座に資金を移動でき、ユーザーのみが口座から資金を移動できます。
このアプリケーションでは、次の 5 つのコントラクトを使用します。
これらの契約は銀行によって登録され、銀行はそれぞれ 、口座履歴の表示、口座の作成、口座への入金、口座間の資金移動、口座からの資金の引き出しを行うことができます。
このアプリケーションの全体的なアーキテクチャは次のようになります。 (この使用例は簡素化のためのものであり、実際には少し異なる可能性があることに再度注意してください。)
アプリケーションを試してみる
ScalarDL Client SDKをダウンロードします。 ScalarDL が実行されていることを確認し、必要なコントラクトをすべて実行して登録します。
$ SCALAR_SDK_HOME=/path/to/scalardl-client-sdk ./register
IntelliJ (または選択した IDE) を使用するか、プロジェクトのホーム ディレクトリで gradle bootRun
を実行して、アプリケーションを実行します。 アプリと対話するために HTTP リクエストを送信できるサーバーを localhost:8080
上に作成する必要があります。 詳細については、API ドキュメント を参照してください。 HTTP リクエストを作成するには、Postman が非常に優れていることがわかりました。
ScalarDL アプリケーションの作成に関する短いチュートリアル
Spring Boot を使用して、コントラクトと対話する Web サービスを作成することにしました。 もちろん、これが唯一の選択肢ではありません。 別の選択肢は、たとえば 資産管理アプリケーション で行われたように、コマンド ライン インタ ーフェイスを作成することです。 そこには、ScalarDL 用のアプリケーションを作成するための非常に優れたチュートリアルもあります。
このチュートリアルでは、Web サービスまたはコマンド ライン インターフェイスのレベルでの詳細については説明せず、代わりにアプリケーションと ScalarDL の間の対話に焦点を当てます。 コントラクトの作成方法、コントラクトの登録方法、そして ScalarDL SDK を使用してアプリケーションからこれらのコントラクトを呼び出す方法について説明します。
契約
コントラクトは、 Contract
クラスを拡張し、 invoke
メソッドをオーバーライドする Java クラスです。 Deposit.java
コントラクトを詳しく見てみましょう。
package com.scalar.application.bankaccount.contract;
import com.scalar.dl.ledger.asset.Asset;
import com.scalar.dl.ledger.contract.Contract;
import com.scalar.dl.ledger.exception.ContractContextException;
import com.scalar.dl.ledger.database.Ledger;
import java.util.Optional;
import javax.json.Json;
import javax.json.JsonObject;
public class Deposit extends Contract {
@Override
public JsonObject invoke(Ledger ledger, JsonObject argument, Optional<JsonObject> property) {
if (!(argument.containsKey("id") && argument.containsKey("amount"))) {
throw new ContractContextException("a required key is missing: id and/or amount");
}
String id = argument.getString("id");
long amount = argument.getJsonNumber("amount").longValue();
if (amount < 0) {
throw new ContractContextException("amount is negative");
}
Optional<Asset> response = ledger.get(id);
if (!response.isPresent()) {
throw new ContractContextException("account does not exist");
}
long oldBalance = response.get().data().getInt("balance");
long newBalance = oldBalance + amount;
ledger.put(id, Json.createObjectBuilder().add("balance", newBalance).build());
return Json.createObjectBuilder()
.add("status", "succeeded")
.add("old_balance", oldBalance)
.add("new_balance", newBalance)
.build();
}
}
この契約が適切に機能するためには、ユーザーはアカウントの id
と amount
を指定する必要があります。 したがって、最初に行うことは、引数にこれら 2 つのキーが含まれているかどうかを確認し、含まれていない場合は ContractContextException
をスローすることです。
注記: ContractContextException
はコントラクト内で唯一スロー可能な例外であり、回復不可能なエラーが発生した場合は常にスローされる必要があります。
したがって、id
と amount
があると仮定して、amount
に対して簡単な非負のチェックを行い、負の場合は再度 ContractContextException
をスローします。 これで、ledger
を操作する準備が整いました。
ledger
で呼び出すことができるメソッドは 3 つあります: get(String s)
、 put(String s, JsonObject jsonObject)
、および scan(AssetFilterassetFilter)
です。 get(String s)
は Ledger から資産 s
を取得します。 put(String s, JsonObject argument)
は、アセット s
をデータ jsonObject
に関連付け、アセットの年齢を増やします。 scan(AssetFilterassetFilter)
は、AssetFilter
で指定されたアセットの履歴のバージョンを返します。
注記: Ledger ではブラインド書き込みは許可されていません。つまり、特定の資産に対して put
を実行する前に、まずその資産を get
する必要があります。 さらに、 scan
は読み取り専用コントラクトでのみ許可されます。つまり、1 つのコントラクトで scan
と put
の両方を行うことはできません。
契約の残りの部分は単純な方法で進められます。 まず Ledger から資産を get
し、現在の残高を取得し、それに預金額を追加し、最後に新しい残高で資産を Ledger に put
。
最後に JsonObject
を返す必要があります。 JsonObject
に何が含まれるかは、コントラクトの設計者次第です。 ここでは、status
メッセージ、old_balance
、および new_balance
を含めることにしました 。
必要に応じて、このアプリケーションが使用する他のコントラクトを scalardl
リポジトリ内のこのサンプルの contract
フォルダー で表示できます。
契約書を作成したら、それを編集する必要があります。 これは次のようにして実行できます。
$ ./gradlew build
認定資格と契約の登録
これで契約書を作成し、まとめたはずです。 ただし、それらを実行する前に、ScalarDL ネットワークに登録する必要があります。 ScalarDL Client SDK client/bin
ディレクトリで利用可能なツールを利用して、コントラクトを登録して実行します。 このディレクトリにアクセスできることを確認してください。
ここで、証明書 (例: client.pem
) とそれに対応する秘密鍵 (例: client-key.pem
)、および ScalarDL を起動して実行する必要があります。 構成に合わせて client.properties
(conf
ディレクトリにあります)を編集します。 次のような行が含まれている必要があります。
scalar.dl.client.server.host=localhost
scalar.dl.client.server.port=50051
scalar.dl.client.cert_holder_id=alice
scalar.dl.client.cert_path=conf/client.pem
scalar.dl.client.private_key_path=conf/client-key.pem
すべてが正しく設定されていれば、次のように証明書を ScalarDL ネットワークに登録できるはずです。
$ ${SCALAR_SDK_HOME}/client/bin/register-cert --properties ./conf/client.properties
成功すると、ステータス コード 200 が返されます。
契約を登録するには、次の形式を使用して conf
ディレクトリに contracts.toml
ファイルを作成します。
[[contracts]]
contract-id = "create-account"
contract-binary-name = "com.scalar.application.bankaccount.contract.CreateAccount"
contract-class-file = "build/classes/java/main/com/scalar/application/bankaccount/contract/CreateAccount.class"
[[contracts]]
contract-id = "deposit"
contract-binary-name = "com.scalar.application.bankaccount.contract.Deposit"
contract-class-file = "build/classes/java/main/com/scalar/application/bankaccount/contract/Deposit.class"
[[contracts]]
contract-id = "transfer"
contract-binary-name = "com.scalar.application.bankaccount.contract.Transfer"
contract-class-file = "build/classes/java/main/com/scalar/application/bankaccount/contract/Transfer.class"
この例では、 CreateAccount.java
、Deposit.java
、Transfer.java
の 3 つのコントラクトを登録します。 contract-binary-name
と contract-class-file
は決まっていますが、 contract-id
は自由に選択できます。 以下で説明するように、contract-id
は、 ClientService
を使用して特定のコントラクトを参照する方法です。
toml ファイルを作成したら、次のように指定したすべてのコントラクトを登録できます。
$ ${SCALAR_SDK_HOME}/client/bin/register-contracts --properties ./conf/client.properties --contracts-file ./conf/contracts.toml
正常に登録された各契約はステータス コード 200 を返す必要があります。
契約の締結
必要に応じて、登録されている契約を実行できるようになります。 たとえば、登録契約を使用していくつかの口座を作成し、そのうちの 1 つの口座に資金を入金し、これらの資金の一部をもう一方の口座に転送します。
ID が a111
と b222
の 2 つのアカウントを作成します。 (契約 ID には任意の文字列を指定できます。)
$ ${SCALAR_SDK_HOME}/client/bin/execute-contract --properties ./conf/client.properties --contract-id create-account --contract-argument '{"id": "a111"}'
$ ${SCALAR_SDK_HOME}/client/bin/execute-contract --properties ./conf/client.properties --contract-id create-account --contract-argument '{"id": "b222"}'
ここで、アカウント a111
に 100 を入金します。
$ ${SCALAR_SDK_HOME}/client/bin/execute-contract --properties ./conf/client.properties --contract-id deposit --contract-argument '{"id": "a111", "amount": 100}'
最後に、25 を a111
から b222
に転送します。
$ ${SCALAR_SDK_HOME}/client/bin/execute-contract --properties ./conf/client.properties --contract-id transfer --contract-argument '{"from": "a111", "to": "b222", "amount": 100}'
アプリケーション自体を実行している場合は、API エンドポイント を使用してこれらのコマンドを実行できます。