[JAVA] How to use Chain API

What is Chain

A blockchain project led by VISA Only the edition called Chain Core Developer Edition is open sourced. Please refer to the article about the construction procedure up to the construction of the Chain environment. Chain environment construction

Please refer to the sample code in the document on the official page. https://chain.com/docs/core/get-started/introduction

Basically, if you look at the sample code, Javadoc, and SDK source code, you should know what to do.

Contracts etc. have not been implemented yet.

You can also check the executed transaction and the created account information on the dashboard that you can see by accessing the 1999 port of the server running Chain with a browser.

API explanation

Classification

Broadly speaking, there are execution of search queries and transaction execution such as remittance. An authentication key is not required to execute a query, but an authentication key is required to execute an API that generates transactions such as withdrawals.

You can also see the internal ID etc. in Chain, Most of them are created by defining alias (alias) in currency, account, etc. Execution of queries and transactions is also executed using alias.

Advance preparation

Make a note of the URL, port, and access key of the server running Chain.

	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";

First, generate a Client class to connect to the server.

Client client = new Client(TARGET_URL,TOKEN);

When executing a search query, only Client is required, but when executing a transaction, the following authentication key must be set.

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));

If the alias specified when creating this authentication key is different, an authentication error will occur and remittance will not be possible. The Key used to create the remittance account must also be used to authenticate the remittance transaction.

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

If you create a Key without specifying an alias like this, an unlimited number of anonymous Keys will be created, so when creating it, specify an alias as shown below and create it with a specific key.

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

This allows the created key to be retrieved by query and reused. On the contrary, if you do not specify alias, you will not be able to explicitly specify the key required for authentication of that account when executing a transaction. After that, account creation, asset (currency) definition, remittance, etc. are executed.

API implementation example individually.

It's just for reference, but if you use the sample on the official page as it is, you will suffer from errors in handling authentication.

Account creation

@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;
	}
}

Here, BaseService is as follows.

@Service
public class BaseService {

	/**
	 *Get a connection client
	 * @return client
	 * @throws ChainException
	 */
	protected Client createClient() throws ChainException{
		return new Client(ApiSettings.TARGET_URL,ApiSettings.TOKEN);
	}

	/**
	 *Get common authentication 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;
	}
}

First, I'm using QueryBuilder to search for an account.

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

All major classes such as Account, Asset, Balance and MockHsm.Key have ** QueryBuilder () ** methods. You can search in a similar way.

Specify the search condition as a character string in ** setFilter ("condition") **. The conditions that can be specified here are described on the API Object page of the official documentation reference. https://chain.com/docs/core/reference/api-objects

In the next ** addFilterParameter (account) **, specify the character string to be embedded in ** \ $ 1, $ 2 ** specified by setFilter. Here, the account name character string (alias) passed as an argument is specified. After setting filter, execute with execute.

The query is described on the following page. https://chain.com/docs/core/build-applications/queries

The result will be returned by iterator such as Account.Items and Balance.Items.

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

I have obtained it at.

This is, for example

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

As soon as you turn it with while,

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

You can turn it with lambda like.

Finally, if there are no search results, a new account is created.

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

The MockHsm.Key used during this process will be the Key created in advance, but the same Key must be used to execute transactions related to this account. It should be created for each account, but since management is complicated, a common key (alias) is used here.

Asset (currency) generation

A transaction is being executed here. At the same time as the currency is generated, the currency is passed to the specified account. Input and output are always a set.

Define the currency in the ** Issue ** action and The ** ControlWithAccount ** action is passing the generated currency to the specified account.

@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));
		
		//Generate if the currency of assetName is not defined.
		if (!isExistAsset(assetName,client)){
		    new Asset.Builder()
		      .setAlias(assetName)
		      .addRootXpub(key.xpub)
		      .setQuorum(1)
		      .create(client);
		}
		//Currency definition transaction.
		//The currency of assetName is distributed to issueAccount by amount from Issue action to ControlWithAccount action.
		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 signing and execution
	    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;
	}
}

Predefined asset (currency) search

Written like any other query.

@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;
	}
}

Check balance

This is also written in the same way except that the Balance class is used. The following example uses two search parameters.

@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;
	}
}

money transfer

With the ** SpendFromAccount ** action, withdraw currency from the sender and The ** ControlWithAccount ** action is passing currency to the recipient. Be careful of Key etc. because the transaction is not executed only by alias.

@Service
public class TransferService extends BaseService {

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

	/**
	 *Remittance execution
	 * @param from Account Remittance source account
	 * @param to Account Remittance destination account
	 * @param assetName Currency name
	 * @param amount 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

Recommended Posts

How to use Chain API
How to use Map
How to use rbenv
How to use letter_opener_web
How to use with_option
How to use fields_for
How to use java.util.logging
How to use map
How to use collection_select
How to use Twitter4J
How to use active_hash! !!
How to use MapStruct
How to use hidden_field_tag
How to use TreeSet
[How to use label]
How to use identity
How to use hashes
How to use JUnit 5
How to use Dozer.mapper
How to use Gradle
How to use org.immutables
How to use java.util.stream.Collector
How to use VisualVM
How to use Map
How to use Java API with lambda expression
[Java] How to use Map
[Java] How to use Map
How to use Priority Queuing
[Rails] How to use enum
How to use java Optional
How to use JUnit (beginner)
How to use Ruby return
[Rails] How to use enum
How to use @Builder (Lombok)
[Swift] How to use UserDefaults
How to use java class
How to use Swift UIScrollView
How to use Big Decimal
[Java] How to use Optional ②
[Java] How to use removeAll ()
How to use String [] args
[Java] How to use string.format
How to use rails join
How to use Java Map
Ruby: How to use cookies
How to use dependent :: destroy
How to use Eclipse Debug_Shell
How to use Apache POI
[Rails] How to use validation
How to use Java variables
[Rails] How to use authenticate_user!
[Rails] How to use "kaminari"
How to use GC Viewer
[Java] How to use Optional ①
How to use Lombok now
[Creating] How to use JUnit
[Rails] How to use Scope
Summary of Java communication API (1) How to use Socket
Summary of Java communication API (3) How to use SocketChannel
Summary of Java communication API (2) How to use HttpUrlConnection
How to use the link_to method