LoginSignup
4
1

More than 5 years have passed since last update.

ChainAPIの使い方

Last updated at Posted at 2016-12-15

Chainとは

VISA主導で進められていたブロックチェーンプロジェクトで、
Chain Core Developer Editionというエディションに限りオープンソース化されている。
Chainの環境構築までは、構築手順を記事にしているのでそちらを参照。
Chain環境構築

公式ページのドキュメントにサンプルコードもあるので参考に。
https://chain.com/docs/core/get-started/introduction

基本的にはサンプルコードやJavadoc、SDKのソースコードを見れば、何をすればいいかはわかるはず。

まだコントラクトなどは実装されていない。

実行したトランザクションや作成したアカウントの情報などは、Chainを実行しているサーバの1999ポートにブラウザアクセスして見るダッシュボード上でも確認できる。

API解説

分類

大きくわけて、検索クエリの実行と、送金などのトランザクション実行がある。
クエリの実行には認証のためのKeyは不要だが、出金などトランザクションの発生するAPIの実行には認証Keyが必要となる。

また、Chainでは内部的なIDなども見ることはできるが、
ほとんどはalias(別名)を通貨やアカウントなどに定義して作成し、
クエリやトランザクションの実行もaliasを使って実行する。

事前準備

Chainを実行しているサーバのURL、ポート、アクセスキーをメモしておく。

    public static final String TARGET_URL = "http://XX.XXX.XX.XXX:1999/";
    public static final String TOKEN = "client:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
    public static final String KEY_ALIAS = "test";

まず、サーバに接続するためのClientクラスを生成。

Client client = new Client(TARGET_URL,TOKEN);

検索クエリを実行する場合はClientだけでよいが、トランザクションを実行する場合は下記の認証キーの設定が必要。

MockHsm.Key key = null;
MockHsm.Key.Items keys = new MockHsm.Key.QueryBuilder().addAlias(ApiSettings.KEY_ALIAS).execute(client);
if (keys.hasNext()){
    key = keys.next();
}else{
    key = MockHsm.Key.create(client,ApiSettings.KEY_ALIAS);
}
HsmSigner.addKey(key, MockHsm.getSignerClient(client));

この認証キー作成時に指定したaliasが異なると認証エラーになり送金などはできない。
送金するアカウントを作成したときに使ったKeyを送金トランザクションの認証時にも使わなければならない。

MockHsm.Key key = MockHsm.Key.create(client);

のように、aliasを指定せずにKeyを作成した場合、無名のKeyが無制限に作られてしまうため、作るときは以下のようにaliasを指定して、特定のキーで作成する。

MockHsm.Key key = MockHsm.Key.create(client,ApiSettings.KEY_ALIAS);

これにより、作成済みのキーはクエリで検索し、再利用することができる。
逆に、aliasを指定しておかないと、そのアカウントの認証に必要なキーをトランザクション実行時に明示的に指定できなくなる為注意。
その後、アカウント作成やアセット(通貨)定義、送金などを実行する。

個別にAPI実装例を。

あくまで参考だが、公式ページのサンプルをそのまま使っていくと、認証の扱いでエラーに悩まされると思われる。

アカウント作成

@Service
public class AccountService extends BaseService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AccountService.class.getName());

    public String create(String account) throws ChainException{
        Client client = createClient();
        MockHsm.Key key = createKey(client);
        HsmSigner.addKey(key, MockHsm.getSignerClient(client));

        Account.Items accounts = new Account.QueryBuilder()
            .setFilter("alias=$1")
            .addFilterParameter(account)
            .execute(client);
        Account created = null;
        if (accounts.hasNext()){
            created = accounts.next();
            LOGGER.info("account is exists.");
        }else{
            created = new Account.Builder()
                .setAlias(account)
                .addRootXpub(key.xpub)
                .setQuorum(1)
                .create(client);
            LOGGER.info("account created.");
        }
        return created.alias;
    }
}

ここで、BaseServiceは以下のようになっている。

@Service
public class BaseService {

    /**
     * 接続Clientを取得する
     * @return client
     * @throws ChainException
     */
    protected Client createClient() throws ChainException{
        return new Client(ApiSettings.TARGET_URL,ApiSettings.TOKEN);
    }

    /**
     * 共通認証Keyを取得する
     * @param client
     * @return key
     * @throws ChainException
     */
    protected MockHsm.Key createKey(Client client) throws ChainException{
        MockHsm.Key key = null;
        MockHsm.Key.Items keys = new MockHsm.Key.QueryBuilder().addAlias(ApiSettings.KEY_ALIAS).execute(client);
        if (keys.hasNext()){
            key = keys.next();
        }else{
            key = MockHsm.Key.create(client,ApiSettings.KEY_ALIAS);
        }
        return key;
    }
}

まず、QueryBuilderを使ってアカウントが存在するか検索している。

        Account.Items accounts = new Account.QueryBuilder()
            .setFilter("alias=$1")
            .addFilterParameter(account)
            .execute(client);

AccountやAsset、Balance、MockHsm.Keyなど主要なクラスにはすべてQueryBuilder()メソッドが用意されており、
同じような書き方で検索できる。

setFilter("条件")には検索条件を文字列で指定する。
ここに指定できる条件は公式ドキュメントのリファレンスのAPI Objectのページに書かれている。
https://chain.com/docs/core/reference/api-objects

次のaddFilterParameter(account)には、setFilterで指定した\$1,$2などに埋め込む文字列を指定する。
ここでは引数で渡されたアカウント名文字列(alias)を指定している。
filterを設定したらexecuteで実行する。

クエリについては以下のページに書かれている。
https://chain.com/docs/core/build-applications/queries

結果はAccount.Items や Balance.Itemsなどitelatorで帰ってくるので

        if (accounts.hasNext()){
            created = accounts.next();

で取得している。

これは例えば、

        while (balances.hasNext()) {
          Balance b = balances.next();
          LOGGER.info("balance of " + b.sumBy.get("asset_alias") + ": " + b.amount);
        }

のようにwhileで回すなり、

        assets.forEachRemaining(s -> {
              assetList.add(new AssetDto(s.id,s.alias));
        });

のようにラムダで回すなりしてもいい。

最後に、検索結果がなかった場合にアカウントを新規作成している。

            created = new Account.Builder()
                .setAlias(account)
                .addRootXpub(key.xpub)
                .setQuorum(1)
                .create(client);
            LOGGER.info("account created.");

この処理時に使われるMockHsm.Keyが事前準備で作ったKeyとなるが、このアカウントに関連したトランザクションの実行にも同じKeyを使う必要がある。
アカウント別に作成すべきだが、管理が複雑になるためここでは共通キー(alias)を使っている。

アセット(通貨)生成

ここではトランザクションを実行している。
通貨の生成と同時にその通貨を指定したアカウントに渡している。
入力と出力は必ずセットになる。

Issueアクションで通貨を定義し、
ControlWithAccountアクションで、生成した通貨を指定のアカウントに渡している。

@Service
public class IssueService extends BaseService {

    private static final Logger LOGGER = LoggerFactory.getLogger(IssueService.class.getName());

    public void issue(String assetName,String issueAccount,Long amount) throws ChainException{

        Client client = createClient();
        MockHsm.Key key = createKey(client);
        HsmSigner.addKey(key, MockHsm.getSignerClient(client));

        // assetNameの通貨が定義されていない場合は生成する。
        if (!isExistAsset(assetName,client)){
            new Asset.Builder()
              .setAlias(assetName)
              .addRootXpub(key.xpub)
              .setQuorum(1)
              .create(client);
        }
        // 通貨定義トランザクション。
        // IssueアクションからControlWithAccountアクションでissueAccountにassetNameの通貨をamount分配っている。
        Transaction.Template issuanceToProgram = new Transaction.Builder()
          .addAction(new Transaction.Action.Issue()
            .setAssetAlias(assetName)
            .setAmount(amount)
          ).addAction(new Transaction.Action.ControlWithAccount()
            .setAccountAlias(issueAccount)
            .setAssetAlias(assetName)
            .setAmount(amount)
          ).build(client);
        // トランザクション署名と実行
        Transaction.Template signedIssuanceToProgram = HsmSigner.sign(issuanceToProgram);
        Transaction.submit(client, signedIssuanceToProgram);
    }

    private boolean isExistAsset(String assetName,Client client) throws ChainException {

        Asset.Items assets = new Asset.QueryBuilder()
          .setFilter("alias=$1")
          .addFilterParameter(assetName)
          .execute(client);

        if (assets.hasNext()){
            return true;
        }
        return false;
    }
}

定義済みアセット(通貨)検索

ほかのクエリ同様の書き方。

@Service
public class AssetQueryService extends BaseService {

    public List<AssetDto> getAssetList(String assetName) throws ChainException {

        Client client = createClient();

        Asset.Items assets = new Asset.QueryBuilder()
          .setFilter("alias=$1")
          .addFilterParameter(assetName)
          .execute(client);

        List<AssetDto> assetList = new ArrayList<>();
        assets.forEachRemaining(s -> {
              assetList.add(new AssetDto(s.id,s.alias));
        });
        return assetList;
    }
}

残高確認

これもBalanceクラスを使う以外は同様の書き方。
以下の例では2つの検索パラメータを使っている。

@Service
public class BalanceService extends BaseService {

    private static final Logger LOGGER = LoggerFactory
            .getLogger(BalanceService.class.getName());

    public ResponseBalanceDto getBalance(String assetName, String account)
            throws ChainException {
        Client client = createClient();

        Balance.Items balances = new Balance.QueryBuilder()
            .setFilter("account_alias=$1 AND asset_alias=$2")
            .addFilterParameter(account)
            .addFilterParameter(assetName)
            .execute(client);
        Balance balance = null;
        ResponseBalanceDto responseDto = new ResponseBalanceDto();
        if (balances.hasNext()){
            balance = balances.next();
            responseDto.setBalance(balance.amount);
            LOGGER.info("account:{} asset:{} amount:{} ",account,assetName,balance.amount);
        }
        return responseDto;
    }
}

送金

SpendFromAccountアクションで、送金元から通貨を出金し、
ControlWithAccountアクションで、送金先に通貨を渡している。
aliasだけでトランザクションが実行されているわけではないので、Keyなどに注意すること。

@Service
public class TransferService extends BaseService {

    private static final Logger LOGGER = LoggerFactory.getLogger(TransferService.class.getName());

    /**
     * 送金実行
     * @param fromAccount 送金元アカウント
     * @param toAccount   送金先アカウント
     * @param assetName   通貨名
     * @param amount      金額
     * @throws ChainException
     */
    public void transfer(String fromAccount, String toAccount,
            String assetName, Long amount) throws ChainException {
        Client client = createClient();
        MockHsm.Key key = createKey(client);
        HsmSigner.addKey(key, MockHsm.getSignerClient(client));
        Transaction.Template transfertran = new Transaction.Builder()
                .addAction(
                        new Transaction.Action.SpendFromAccount()
                                .setAccountAlias(fromAccount)
                                .setAssetAlias(assetName).setAmount(amount))
                .addAction(
                        new Transaction.Action.ControlWithAccount()
                                .setAccountAlias(toAccount)
                                .setAssetAlias(assetName).setAmount(amount))
                .build(client);

        Transaction.Template signedTransfertran = HsmSigner.sign(transfertran);
        Transaction.submit(client, signedTransfertran);

    }
}

key

Chain,blockchain,API

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1