メインコンテンツまでスキップ
バージョン: 3.10

簡単な銀行口座申請

注記

このページは英語版のページが機械翻訳されたものです。英語版との間に矛盾または不一致がある場合は、英語版を正としてください。

概要

これは単純な銀行口座アプリケーションであり、scalardl リポジトリ にあります。ユーザーが実行できるアクションは、アカウントの作成、アカウント履歴の表示、アカウントへの資金の入金、アカウントからの資金の引き出し、アカウント間の資金の転送です。 アカウントで実行されたすべてのアクションは ScalarDL に記録されます。これは、ブロックチェーンがブロックを記録する方法と同様に、アカウント履歴が改ざん明示的な方法で記録されることを意味します。 これは、アカウント履歴が (意図的かどうかに関わらず) 変更された場合、これを検出することが可能であることを意味します。

ここでは物事を簡単にするために、銀行がすべてのコントラクトを実行するための秘密鍵を保持していると仮定します (これがどのように機能するかについては、以下を参照してください)。 おそらくこれは、この銀行アプリケーションを実際に使用する方法ではありません。 この場合、悪意のあるアカウント マネージャーがユーザーのアカウント履歴を実際に変更する可能性があります。たとえば、単にアカウントを再作成し、偽のデータを入力するだけです。 より意味のある設定は、銀行が口座に入金するための秘密鍵を所有し、各ユーザーが自分の秘密鍵を使用して引き出しと送金のコントラクトを登録することです。 その場合、銀行のみが口座に資金を移動でき、ユーザーのみが口座から資金を移動できます。

このアプリケーションでは、次の 5 つのコントラクトを使用します。

これらのコントラクトは銀行によって登録され、銀行はそれぞれ、口座履歴の表示、口座の作成、口座への入金、口座間の資金移動、口座からの資金の引き出しを行うことができます。

このアプリケーションの全体的なアーキテクチャは次のようになります。 (この使用例は簡素化のためのものであり、実際には少し異なる可能性があることに再度注意してください。)

architecture

このサンプルアプリケーションの前提条件

注記
  • 上記の LTS バージョンの使用をお勧めしますが、他の非 LTS バージョンでも動作する可能性があります。
  • また、他の JDK もこのサンプル アプリケーションで動作するはずですが、テストは行っていません。

アプリケーションを試してみる

ScalarDL Client SDKをダウンロードします。 ScalarDL が実行されていることを確認し、必要なコントラクトをすべて実行して登録します。

./gradlew build
cd contract
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 を使用してアプリケーションからこれらのコントラクトを呼び出す方法について説明します。

コントラクト

コントラクトは、 JacksonBasedContract クラスを拡張し、 invoke メソッドをオーバーライドする Java クラスです。 Deposit.java コントラクトを詳しく見てみましょう。

package com.scalar.application.bankaccount.contract;

import com.fasterxml.jackson.databind.JsonNode;
import com.scalar.dl.ledger.statemachine.Asset;
import com.scalar.dl.ledger.contract.JacksonBasedContract;
import com.scalar.dl.ledger.exception.ContractContextException;
import com.scalar.dl.ledger.statemachine.Ledger;
import java.util.Optional;
import javax.annotation.Nullable;

public class Deposit extends JacksonBasedContract {
@Override
public JsonNode invoke(
Ledger<JsonNode> ledger, JsonNode argument, @Nullable JsonNode properties) {
if (!argument.has("id") || !argument.has("amount")) {
throw new ContractContextException("a required key is missing: id and/or amount");
}

String id = argument.get("id").asText();
long amount = argument.get("amount").asLong();

if (amount < 0) {
throw new ContractContextException("amount is negative");
}

Optional<Asset<JsonNode>> asset = ledger.get(id);

if (!asset.isPresent()) {
throw new ContractContextException("account does not exist");
}

long oldBalance = asset.get().data().get("balance").asLong();
long newBalance = oldBalance + amount;

ledger.put(id, getObjectMapper().createObjectNode().put("balance", newBalance));
return getObjectMapper()
.createObjectNode()
.put("status", "succeeded")
.put("old_balance", oldBalance)
.put("new_balance", newBalance);
}
}

このコントラクトが適切に機能するためには、ユーザーはアカウントの idamount を指定する必要があります。 したがって、最初に行うことは、引数にこれら 2 つのキーが含まれているかどうかを確認し、含まれていない場合は ContractContextException をスローすることです。

注記: ContractContextException はコントラクト内で唯一スロー可能な例外であり、回復不可能なエラーが発生した場合は常にスローされる必要があります。

したがって、idamount があると仮定して、amount に対して簡単な非負のチェックを行い、負の場合は再度 ContractContextException をスローします。 これで、ledger を操作する準備が整いました。

ledger で呼び出すことができるメソッドは 3 つあります: get(String s)put(String s, JsonNode jsonNode)、および scan(AssetFilterassetFilter) です。 get(String s) は Ledger からアセット s を取得します。 put(String s, JsonNode jsonNode) は、アセット s をデータ jsonNode に関連付け、アセットの年齢を増やします。 scan(AssetFilterassetFilter) は、AssetFilter で指定されたアセットの履歴のバージョンを返します。

注記: Ledger ではブラインド書き込みは許可されていません。つまり、特定のアセットに対して put を実行する前に、まずそのアセットを get する必要があります。 さらに、 scan は読み取り専用コントラクトでのみ許可されます。つまり、1 つのコントラクトで scanput の両方を行うことはできません。

コントラクトの残りの部分は単純な方法で進められます。 まず Ledger からアセットを get し、現在の残高を取得し、それに預金額を追加し、最後に新しい残高でアセットを Ledger に put

最後に JsonNode を返す必要があります。 JsonNode に何が含まれるかは、コントラクトの設計者次第です。 ここでは、status メッセージ、old_balance、および new_balance を含めることにしました。

必要に応じて、このアプリケーションが使用する他のコントラクトを scalardl リポジトリ内のこのサンプルの contract フォルダー で表示できます。

コントラクトを作成したら、それを編集する必要があります。 これは次のようにして実行できます。

./gradlew build

認定資格とコントラクトの登録

これでコントラクトを作成し、まとめたはずです。 ただし、それらを実行する前に、ScalarDL ネットワークに登録する必要があります。 ScalarDL Client SDK client/bin ディレクトリで利用可能なツールを利用して、コントラクトを登録して実行します。 このディレクトリにアクセスできることを確認してください。

ここで、証明書 (例: client.pem) とそれに対応する秘密鍵 (例: client-key.pem)、および ScalarDL を起動して実行する必要があります。 構成に合わせて client.propertiesconf ディレクトリにあります)を編集します。 次のような行が含まれている必要があります。

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 ネットワークに登録できるはずです。

cd contract
${SCALAR_SDK_HOME}/client/bin/scalardl 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.javaDeposit.javaTransfer.java の 3 つのコントラクトを登録します。 contract-binary-namecontract-class-file は決まっていますが、 contract-id は自由に選択できます。 以下で説明するように、contract-id は、 ClientService を使用して特定のコントラクトを参照する方法です。

toml ファイルを作成したら、次のように指定したすべてのコントラクトを登録できます。

${SCALAR_SDK_HOME}/client/bin/scalardl register-contracts --properties ../conf/client.properties --contracts-file ../conf/contracts.toml

正常に登録された各コントラクトはステータス コード 200 を返す必要があります。

コントラクトの実行

必要に応じて、登録されているコントラクトを実行できるようになります。 たとえば、登録コントラクトを使用していくつかの口座を作成し、そのうちの 1 つの口座に資金を入金し、これらの資金の一部をもう一方の口座に転送して、口座履歴を確認します。

ID が a111b222 の 2 つのアカウントを作成します。 (コントラクト ID には任意の文字列を指定できます。)

${SCALAR_SDK_HOME}/client/bin/scalardl execute-contract --properties ../conf/client.properties --contract-id create-account --contract-argument '{"id": "a111"}'
${SCALAR_SDK_HOME}/client/bin/scalardl execute-contract --properties ../conf/client.properties --contract-id create-account --contract-argument '{"id": "b222"}'

ここで、アカウント a111 に 100 を入金します。

${SCALAR_SDK_HOME}/client/bin/scalardl execute-contract --properties ../conf/client.properties --contract-id deposit --contract-argument '{"id": "a111", "amount": 100}'

最後に、25 を a111 から b222 に転送します。

${SCALAR_SDK_HOME}/client/bin/scalardl execute-contract --properties ../conf/client.properties --contract-id transfer --contract-argument '{"from": "a111", "to": "b222", "amount": 100}'

a111 の残高履歴は次のようにして確認できます。

${SCALAR_SDK_HOME}/client/bin/scalardl execute-contract --properties ../conf/client.properties --contract-id account-history --contract-argument '{"id": "a111"}'

次のような結果が表示されます:

Contract result:
{
"status" : "succeeded",
"history" : [ {
"id" : "a111",
"age" : 2,
"data" : {
"balance" : 0
}
}, {
"id" : "a111",
"age" : 1,
"data" : {
"balance" : 100
}
}, {
"id" : "a111",
"age" : 0,
"data" : {
"balance" : 0
}
} ]
}

アプリケーション自体を実行している場合は、API エンドポイント を使用してこれらのコマンドを実行できます。

顧客サービス

これで、コントラクトが ScalarDL ネットワークに登録されたはずです。 アプリケーションからこれらのコントラクトを実行するには、ScalarDL Client SDKClientService クラスを利用します。

クライアント SDK は Maven Central で入手でき、次の依存関係を追加することで Gradle を使用してアプリケーションにインストールできます。 あなたの build.gradle:

dependencies {
compile group: 'com.scalar-labs', name: 'scalardl-java-client-sdk', version: '3.9.2'
}

次のスニペットは、ClientService オブジェクトをインスタンス化する方法を示しています。ここで、propertiesclient.properties ファイルへのパスにする必要があります。

ClientServiceFactory factory = new ClientServiceFactory();
ClientService service = factory.create(new ClientConfig(new File(properties)));

ClientService には、もちろんコントラクトを実行するために使用できるメソッド executeContract(String contractId, JsonNode contractArgument) が含まれています。 例えば:

ObjectMapper mapper = new ObjectMapper();
JsonNode argument = mapper.createObjectNode().put("id", "010-123456789");
ContractExecutionResult result = clientService.executeContract("create-account", argument);

これにより、上で行ったように、引数 {"id": "010-123456789"} を指定して CreateAccount コントラクトが実行されます。 コントラクトの登録時に選択した、指定された ID create-account を使用してコントラクトを呼び出していることに注意してください。

コントラクトの実行結果は ContractExecutionResult です。 これには結果と証明が含まれており、それぞれは次のようにして取得できます。

result.getProofs();
result.getResult();

次は何ですか?

独自のアプリの作成を始めるのに十分な情報が得られたことを願っています。 次に試してみることができるアイデアをいくつか紹介します。