Skip to main content

Connect the client to the blockchain

In this section, we'll establish the connection between our Unity application and the Chromia blockchain using the Postchain client. Our goal is to initialize the client and define methods to interact with the Tic Tac Toe game on the blockchain.

Installation and setup

Before diving into the integration process, we must set up our Unity project with the necessary dependencies. Specifically, we require the Postchain client and FT4 client to work with the Chromia blockchain.

  • For steps to install the Postchain client, see C# client.
  • For steps to install the FT4 client, see FT4 client.

Initializing the client

We'll start by initializing the client to connect to the Chromia blockchain. This involves specifying the blockchain RID or IID and setting up the client to interact with the network.

using System.Collections.Generic;
using System.Threading.Tasks;
using Postchain.Client;

public class BlockchainManager : MonoBehaviour
{
private ChromiaClient client;
private Buffer blockchainRID = Buffer.From("7d565d92fd15bd1cdac2dc276cbcbc5581349d05a9e94ba919e1155ef4daf8f9"); // Replace with your blockchain RID

async void Start()
{
await InitializeClient();
}

private async Task InitializeClient()
{
// Initialize Client
client = await ChromiaClient.Create("http://localhost:7740", blockchainRID);
Debug.Log("Client initialized");

// Additional initialization logic can be added here
}
}

Example: using BlockchainService for connection

Let's look at how to set up BlockchainService to connect your Unity application to the Chromia blockchain.

  1. Initialize the blockchain client: Begin by initializing the BlockchainService by calling BlockchainService.Initialize() and awaiting its completion. Ensure the constants HOST and CHROMIA_PORT are correctly set to point to your blockchain node.

  2. Register an account: The current implementation supports only the open registration strategy. The method OpenStrategyRegisterAccount() will handle this process. Upon successful registration, it will automatically set up an FT4Connection for further interactions.

  3. Interact with the blockchain: With the FT4Connection established, any subsequent blockchain operations are carried out using the CurrentSession within the FT4Connection. For example, creating a new game can be done with:

    var transactionReceipt = await _blockchainService.Ft4Connection.CurrentSession.Call(new Operation[] { new("create_game") });

BlockchainService code

public class BlockchainService
{
public Ft4Connection Ft4Connection => _ft4Connection;
public string UserAccountId => _ft4Connection != null ? _ft4Connection.CurrentSession.Account.Id : string.Empty;

// The HOST and PORT values should be adjusted based on the blockchain's configuration
private const string HOST = "localhost";
private const int CHROMIA_PORT = 7740;
private ChromiaClient _client;
private Ft4Connection _ft4Connection;

public async Task Initialize()
{
var nodeUrl = $"http://{HOST}:{CHROMIA_PORT}/";
var brid = await ChromiaClient.GetBlockchainRID(nodeUrl, 0);
_client = await ChromiaClient.Create(nodeUrl, brid);
}

public async UniTask<StoredAccountData> OpenStrategyRegisterAccount()
{
if (_client == null)
{
Debug.LogError("ChromiaClient has not been initialized.");
return null;
}

// Generate a random private key
var privateKeyBytes = new byte[32];
RandomNumberGenerator.Create().GetBytes(privateKeyBytes);
var privateKey = Buffer.From(privateKeyBytes);
var userSignature = SignatureProvider.Create(privateKey);

// Create the authorization descriptor with account and transfer flags
var userAuthDescriptor = new AuthDescriptor<SingleSignatureArgs>(
new SingleSignatureArgs
{
Flags = new AuthFlag[] { AuthFlag.Account, AuthFlag.Transfer },
Signer = userSignature.PubKey
}
);

// Register the account using the open registration strategy
var transaction = await Account.Register(_client, userSignature, userAuthDescriptor, RegistrationStrategies.Open);
if (transaction.Status == TransactionReceipt.ResponseStatus.Confirmed)
{
await InitializeFt4Connection(userSignature);
return new StoredAccountData
{
AccountId = _ft4Connection.CurrentSession.Account.Id,
PrivateKey = privateKey
};
}

Debug.LogError(transaction.RejectReason);
return null;
}

/// <summary>
/// Authenticates and initializes an <see cref="Ft4Connection"/> with an existing account.
/// </summary>
/// <param name="privateKey">The private key used to authenticate.</param>
/// <returns>True if the login was successful; otherwise, false.</returns>
public async UniTask<bool> LoginWithExistingAccount(Buffer privateKey)
{
if (_client == null)
{
Debug.LogError("ChromiaClient has not been initialized.");
return false;
}

var userSignature = SignatureProvider.Create(privateKey);
await InitializeFt4Connection(userSignature);
return _ft4Connection.CurrentSession.Account != null;
}

/// <summary>
/// Initializes the <see cref="Ft4Connection"/> with the provided signature provider.
/// </summary>
private async UniTask InitializeFt4Connection(SignatureProvider userSignature)
{
_ft4Connection = await Ft4Connection.Create(_client, userSignature);
}
}
  • Initialize(): Establishes a connection with the Chromia node using the provided HOST and CHROMIA_PORT.
  • OpenStrategyRegisterAccount(): Registers a new account with an open strategy and initializes the FT4Connection with the resulting account.
  • LoginWithExistingAccount(): Logs in using an existing private key and establishes an FT4Connection.
  • InitializeFt4Connection(): Sets up the Ft4Connection using the provided SignatureProvider.

Connecting to the blockchain

You can connect to the network using the Directory System Chain to automatically determine node URLs:

private async Task InitializeClientFromDirectory()
{
var client = await ChromiaClient.CreateFromDirectory("http://localhost:7750", blockchainRID);
Debug.Log("Client connected through directory");
}

Queries

To query data from the blockchain, define query parameters and call the query method. For the Tic Tac Toe game, you'll query the game state and player moves.

public async UniTask<GameData> GetGameById(int gameId)
{
return await client.Query<GameData>("get_game_by_id", ("id", gameId));
}

Transactions

To send transactions, create operations and use a signature provider to sign them. Transactions are invoked through the Ft4Connection's CurrentSession, which ensures they are authorized using the correct credentials under the hood.

public async UniTask<bool> MakeMove(int gameRowId, int slot)
{
var transactionReceipt =
await _blockchainService.Ft4Connection.CurrentSession.Call(new Operation[]
{ new("make_move", gameRowId, slot) });

if (transactionReceipt.Status is TransactionReceipt.ResponseStatus.Confirmed)
{
return true;
}

Debug.LogError(
$"Could not make move for slot {slot} in game {gameRowId}. Reason: {transactionReceipt.RejectReason}");
return false;
}

Error handling

Handle errors using try-catch blocks. The client throws exceptions like ChromiaException or TransportException if issues arise. Be prepared to manage errors related to transaction signing or client communication.

private async Task HandleTransactionAsync()
{
try
{
await MakeMove("gameId", 1, 1, 'X');
}
catch (ChromiaException ex)
{
Debug.LogError($"ChromiaException: {ex.Message}");
}
catch (TransportException ex)
{
Debug.LogError($"TransportException: {ex.Message}");
}
catch (Exception ex)
{
Debug.LogError($"Exception: {ex.Message}");
}
}