はじめてのアプリケーションを作成

このガイドでは Symbol Developer Documentation 開発サイクルについて説明します。

はじめに Symbol Developer Documentation で利用可能な組み込み機能の モザイクアカウント を組み合わせてソリューションを設計します。このガイドを修了すれば、ブロックチェーンでトランザクションの発行および監視する方法を理解しているでしょう。

ユースケース

チケット二次市場は、再販市場としても知られており、最初の販売者からチケットを購入した後に個人間で行われるチケット交換です。最初の販売者は、イベントウェブサイト、オンラインチケット販売プラットフォーム、イベントの入口にあるショップまたは売店であったりします。

最初の販売者ではない人からチケットを購入しても、必ずしもそのチケットの追加料金を支払うという意味ではありません。これは最初の販売者が問題の解決に何もできず、偽造もしくは複製されたチケットを購入してしまう、被害者となる機会です。

私達は何を解決したいのでしょう?

../_images/getting-started.png

認証モデル

チケット販売者はシステムのセットアップを望んでいます:

  1. 各チケットと購入者の識別。

  2. チケット転売の防止。

  3. 未認証チケットとその複製の防止。

なぜ Symbol は正しい選択なのでしょう?

ブロックチェーンテクノロジーはこのようなケースに適用できます:

  • 様々な参加者が関係します。

  • 参加者はお互いに信頼する必要があります。

  • 不変のイベントの集合を追跡し続ける必要があります。

Symbol は フレキシブルなブロックチェーン テクノロジーです。すべてのアプリケーションロジックをブロックチェーンにアップロードする代わりに、 API 呼び出し によってテスト済みの機能を使用して、価値、認可、トレーサビリティ、そして認証の移転と格納を行うことができます。

残りのコードは オフチェーン のままです。これにより必要に応じてプロセスを変更できるため、固有の不変性リスクが軽減されます。

各参加者のアカウントを作成

まず、解決したいユースケースに関与するアクターを特定します:

  • チケット販売者

  • 購入者

チケット販売者と顧客を別々の アカウント として表現することにしました。アカウントとは対応する秘密鍵で変更できる、ブロックチェーン上の預金金庫と考えてください。各アカウントは一意であり、アドレスによって識別されます。

テスト symbol.xym をアカウントへ入金しましたか? 前のガイド では Symbol CLI を使用してアカウントの作り方を学習しました。このアカウントは チケット販売者 アカウントとして表現します。

  1. 次のコマンドを実行して、チケット販売者のアカウントに symbol.xym 単位があることを確認します。

    symbol-cli account info --profile testnet
    

    このような行がスクリーンに表示されているはずです:

    Account Information
    ┌───────────────────┬────────────────────────────────────────────────┐
    │ PropertyValue                                          │
    ├───────────────────┼────────────────────────────────────────────────┤
    │ Address           │ TCWYXK-VYBMO4-NBCUF3-AXKJMX-CGVSYQ-OS7ZG2-TLI  │
    ├───────────────────┼────────────────────────────────────────────────┤
    │ Address Height    │ 1                                              │
    ├───────────────────┼────────────────────────────────────────────────┤
    │ Public Key        │ 203...C0A                                      │
    ├───────────────────┼────────────────────────────────────────────────┤
    │ Public Key Height │ 3442                                           │
    ├───────────────────┼────────────────────────────────────────────────┤
    │ Importance        │ 0                                              │
    ├───────────────────┼────────────────────────────────────────────────┤
    │ Importance Height │ 0                                              │
    └───────────────────┴────────────────────────────────────────────────┘
    
    Balance Information
    ┌──────────────────┬─────────────────┬─────────────────┬───────────────────┐
    │ Mosaic Id        │ Relative Amount │ Absolute Amount │ Expiration Height │
    ├──────────────────┼─────────────────┼─────────────────┼───────────────────┤
    │ 5E62990DCAC5BE8A │ 750.0           │ 750000000       │ Never             │
    └──────────────────┴─────────────────┴─────────────────┴───────────────────┘
    

    アカウントは 750 symbol.xym 相対単位を保有しています。もし "Balance Information" の次の行が空の場合は、テスト用通貨を手に入れるために 前のガイド に従ってください。

  2. 購入者 を識別するための2つ目のアカウントを CLI で作成します。

    symbol-cli account generate --network TEST_NET --save \
               --url <NODE_URL> --profile customer
    
    New Account
    
    ┌─────────────┬────────────────────────────────────────────────┐
    │ PropertyValue                                          │
    ├─────────────┼────────────────────────────────────────────────┤
    │ Address     │ TCHBDE-NCLKEB-ILBPWP-3JPB2X-NY64OE-7PYHHE-32I  │
    ├─────────────┼────────────────────────────────────────────────┤
    │ Public Key  │ E59...82F                                      │
    ├─────────────┼────────────────────────────────────────────────┤
    │ Private Key │ 111...111                                      │
    └─────────────┴────────────────────────────────────────────────┘
    

ブロックチェーンの監視

アカウントはトランザクションを通じてブロックチェーンの状態を変更します。アカウントがトランザクションをアナウンスすると、正しく構成されている場合、サーバーは OK のレスポンスを返します。

ただし OK レスポンスを受信して​​も、トランザクションが有効であったり、ブロックに含まれるわけではありません。たとえば、発行者に十分な symbol.xym がない、メッセージセットが大きすぎる、手数料の指定が少なすぎるなど、トランザクションが拒否される可能性があります。

ネットワークによって承認または拒否されるタイミングを検知するために、アナウンスする前に トランザクションを監視する ことを推奨します。

  1. In a new terminal, monitor the transactions involving the ticket vendor's account to know if they are confirmed or rejected by the network.

    symbol-cli monitor all --address TCWYXK-VYBMO4-NBCUF3-AXKJMX-CGVSYQ-OS7ZG2-TLI
    

チケットの作成

Symbol モザイク でチケットを表現します。この機能は物体、チケット、クーポン、株式に相当するもの、あなたの暗号通貨さえも、いかなるアセットをブロックチェーン上に表現することができます。

モザイクは作成時に定義される変更可能なプロパティを持ちます。例えば 転送可能プロパティを false に設定します。これは、チケットの転売を防止するために、購入者がモザイク作成者だけに送り返すことしかできないことを意味します。

  1. CLI を使用し、チケット販売者アカウントでチケットを表現する新しいモザイクを作成します。この新しいモザイクは次のように構成します:

    プロパティ

    説明

    Divisibility

    0

    “0.5 tickets” が送信できないように、モザイクは可分できないようにします。

    Duration

    1000

    モザイクは 1000 ブロック登録されます。

    Amount

    99

    作成するチケットの量

    Supply mutable

    True

    モザイク供給量は後に変更可能です。

    Transferable

    False

    モザイクはモザイク作成者だけに送り返すことしかできません。

    symbol-cli transaction mosaic --amount 99 --supply-mutable \
               --divisibility 0 --duration 1000 --max-fee 2000000 \
               --sync --profile testnet
    
  2. トランザクションをアナウンスしたら、ターミナルに表示された MosaicID コピーしてください。

    The new mosaic id is: 7cdf3b117a3c40cc
    

    トランザクションは最大30秒で承認済みとして現れてきます。ターミナルでエラーが起きた場合、エラーコードの意味は こちら を参照してください。

チケットの送信

モザイクを定義したので、1 つのチケット単位を TransferTransaction をアナウンスして顧客に送信します。

  1. 新しいファイルを開き、次の値で TransferTransaction を定義してください。

    プロパティ

    説明

    Deadline

    デフォルト (2 時間)

    トランザクションがブロックチェーンに取り込まれるまで待機できる最大時間。この時間を経過しても未確認の場合、トランザクションはドロップされます。パラメタは時間単位で定義され、 1 〜 6 の範囲です ( アグリゲートボンド トランザクションの場合は 1 〜 48)

    Recipient

    TCHBDE...32I

    受信のアカウントアドレス。このケースでは顧客のアドレスです。

    Mosaics

    [1 7cdf3b117a3c40cc]

    送信するモザイクの配列

    Message

    enjoy your ticket

    添付されたメッセージ

    Network

    TEST_NET

    ネットワークタイプ

    // replace with mosaic id
    const mosaicIdHex = '7cdf3b117a3c40cc';
    const mosaicId = new MosaicId(mosaicIdHex);
    // replace with customer address
    const rawAddress = 'TCHBDE-NCLKEB-ILBPWP-3JPB2X-NY64OE-7PYHHE-32I';
    const recipientAddress = Address.createFromRawAddress(rawAddress);
    // replace with network type
    const networkType = NetworkType.TEST_NET;
    
    const transferTransaction = TransferTransaction.create(
      Deadline.create(epochAdjustment),
      recipientAddress,
      [new Mosaic(mosaicId, UInt64.fromUint(1))],
      PlainMessage.create('enjoy your ticket'),
      networkType,
      UInt64.fromUint(2000000),
    );
    
    // replace with mosaic id
    const mosaicIdHex = '7cdf3b117a3c40cc';
    const mosaicId = new symbol_sdk_1.MosaicId(mosaicIdHex);
    // replace with customer address
    const rawAddress = 'TCHBDE-NCLKEB-ILBPWP-3JPB2X-NY64OE-7PYHHE-32I';
    const recipientAddress = symbol_sdk_1.Address.createFromRawAddress(rawAddress);
    // replace with network type
    const networkType = symbol_sdk_1.NetworkType.TEST_NET;
    const transferTransaction = symbol_sdk_1.TransferTransaction.create(
      symbol_sdk_1.Deadline.create(epochAdjustment),
      recipientAddress,
      [new symbol_sdk_1.Mosaic(mosaicId, symbol_sdk_1.UInt64.fromUint(1))],
      symbol_sdk_1.PlainMessage.create('enjoy your ticket'),
      networkType,
      symbol_sdk_1.UInt64.fromUint(2000000),
    );
    
            // replace with node endpoint
            try (final RepositoryFactory repositoryFactory = new RepositoryFactoryVertxImpl(
                    "NODE_URL")) {
                // replace with mosaic id
                final String mosaicIdHex = "7cdf3b117a3c40cc";
                final MosaicId mosaicId = new MosaicId(mosaicIdHex);
    
                // replace with customer address
                final String rawAddress = "TCHBDE-NCLKEB-ILBPWP-3JPB2X-NY64OE-7PYHHE-32I";
                final UnresolvedAddress recipientAddress = Address.createFromRawAddress(rawAddress);
                final NetworkType networkType = repositoryFactory.getNetworkType().toFuture().get();
    
                final Mosaic mosaic = new Mosaic(mosaicId, BigInteger.valueOf(1));
                final TransferTransaction transferTransaction = TransferTransactionFactory
                        .create(
                                networkType,
                                recipientAddress,
                                Collections.singletonList(mosaic),
                                PlainMessage.create("Enjoy your ticket"))
                        .maxFee(BigInteger.valueOf(2000000)).build();
    

    トランザクションは定義されましたが、まだネットワークへはアナウンスされていません。

  2. ネットワークがトランザクションの信頼性を検証できるように チケット販売者のアカウント でトランザクションに署名をします。

    注釈

    Include the network generation hash to make the transaction only valid for your network. Open NODE_URL /node/info in a new browser tab and copy the meta.networkGenerationHash value.

    // replace with ticket vendor private key
    const privateKey =
      'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
    const account = Account.createFromPrivateKey(privateKey, networkType);
    // replace with meta.networkGenerationHash (nodeUrl + '/node/info')
    const networkGenerationHash =
      '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
    const signedTransaction = account.sign(
      transferTransaction,
      networkGenerationHash,
    );
    
    // replace with ticket vendor private key
    const privateKey =
      'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
    const account = symbol_sdk_1.Account.createFromPrivateKey(
      privateKey,
      networkType,
    );
    // replace with meta.networkGenerationHash (nodeUrl + '/node/info')
    const networkGenerationHash =
      '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
    const signedTransaction = account.sign(
      transferTransaction,
      networkGenerationHash,
    );
    
                // replace with ticket vendor private key
                final String privateKey = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
                // replace with network generation hash
                final String generationHash = repositoryFactory.getGenerationHash().toFuture().get();
    
                final Account account = Account
                        .createFromPrivateKey(privateKey, networkType);
                final SignedTransaction signedTransaction = account
                        .sign(transferTransaction, generationHash);
    
  3. 署名が終わったら、トランザクションをネットワークへアナウンスします。

    // replace with node endpoint
    const nodeUrl = 'NODE_URL';
    const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
    
    const transactionHttp = repositoryFactory.createTransactionRepository();
    
    transactionHttp.announce(signedTransaction).subscribe(
      (x) => console.log(x),
      (err) => console.error(err),
    );
    
    // replace with node endpoint
    const nodeUrl = 'NODE_URL';
    const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
    const transactionHttp = repositoryFactory.createTransactionRepository();
    transactionHttp.announce(signedTransaction).subscribe(
      (x) => console.log(x),
      (err) => console.error(err),
    );
    
                final TransactionRepository transactionRepository = repositoryFactory
                        .createTransactionRepository();
                transactionRepository.announce(signedTransaction).toFuture().get();
            }
    
    symbol-cli transaction transfer --recipient-address TCHBDE-NCLKEB-ILBPWP-3JPB2X-NY64OE-7PYHHE-32I --mosaics 7cdf3b117a3c40cc::1 --message enjoy_your_ticket --max-fee 2000000 --sync
    
  4. トランザクションを監視しているターミナルウィンドウを確認します。トランザクションが承認されたら、次のコマンドで顧客がチケットを受け取ったかどうかを確認できます。

    symbol-cli account info --profile customer
    

このユースケースを解決できたのでしょうか?

  • ✅ 各購入者の識別: 各購入者の Symbol アカウントの作成

  • ✅ チケットの転売防止: 転送不可能なモザイクの作成

  • ✅ 未認証なチケットとその複製: 一意なモザイクの作成

Symbol ビルドイン機能 の学習を続ける。