Tutorial

Defining an Asset: Transaction Submission

The next step after creating a transaction is submitting it to the ledger. Findora's JavaScript API provides a simple network library to facilitate interactions with Findora's REST API. This tutorial will demonstrate how Alice, a user of Findora Ledger, can create and submit a transaction that defines a new asset.

Before continuing, it is worth mentioning that the Findora testnet consists of three services: 1) the core ledger API, 2) a transaction submission API and 3) a query API that serves important cached information that clients need to interact with the ledger. Findora's network library sends requests to the appropriate service. The user need only provide the location (port) of each one.

const network = new Network.Network(PROTOCOL, HOST, QUERY_PORT, SUBMISSION_PORT, LEDGER_PORT);

Before submitting a transaction, Alice needs to create a key:

console.log('Creating key store and generating an issuance key for Alice...');
const PASSWORD = 'findorarocks123';
const keyStore = new KeyStore.KeyStore(PASSWORD);
const masterKey = keyStore.genMasterKey(PASSWORD);
const aliceKeyPair = keyStore.genKeyPair(masterKey, 'Alice');

Now Alice can construct a transaction containing a DefineAsset operation.

console.log('Creating definition transaction...');
const tokenCode = Ledger.random_asset_type();
const memo = 'test memo';
const blockCount = BigInt((await network.getStateCommitment())[1]);
const definitionTransaction = Ledger.TransactionBuilder
  .new(blockCount).add_operation_create_asset(aliceKeyPair, memo, tokenCode, Ledger.AssetRules.new()).transaction();

Alice will now attempt to submit her transaction.

console.log('Submitting transaction...');

The Findora testnet submits transactions immediately, so the request below will fail if the transaction is rejected. As discussed before, a DefineAsset operation may fail if its token code is already in use. If succcesful, the response of the submitted transaction request will return a handle that can be used the query the status of the transaction.

const handle = await network.submitTransaction(definitionTransaction);
console.log(`Transaction submitted successfully! The transaction handle is ${handle}.`);

Using the transaction handle, Alice can fetch the status of the transaction from the query server.

console.log('Getting transaction status...');
await Utils.sleep(4);
const transactionStatus = await network.getTxnStatus(handle);

The transaction status object of a successfully submitted transaction contains a single field, 'Committed', containing a tuple with the transaction sequence identifier (TxnSID) and a list of output sequence identifiers (TxoSIDs). The TxnSID is the position of the transaction in the ledger. TxoSIDs are covered in a later example.

const txnSID = transactionStatus.Committed[0];
console.log(`TxnSID is: ${txnSID}`);

An important property of a Findora Ledger is the ability to authenticate transactions. Users can authenticate transactions against a small tag called the state commitment. The state commitment is a commitment to the current state of the ledger. The state commitment response is a tuple containing the state commitment and the state commitment version.

console.log('Fetching state commitment...');
const stateCommitment = (await network.getStateCommitment())[0];

The network request below will not only return the transaction stored at the index provided (in this case, the transaction we just submitted), but a cryptographic proof that the transaction being returned exists at that location in the ledger.

const txn = await network.getTxn(txnSID);

Alice can now verify that ledger has returned a valid proof that transaction she just submitted exists at the position stored by the variable txnSID.

console.log('Authenticating the transaction that Alice just submitted...');
const isValid = Ledger
  .verify_authenticated_txn(JSON.stringify(stateCommitment), JSON.stringify(txn));
assert(isValid);
console.log('Transaction authenticated!');

Alice can also ask the query server for the list assets she has created. The query server stores useful information tied to ledger public keys. The query server expects a base64-encoded public key. The Findora JavaScript API provides a helper that converts a public key to its base64-encoded form.

console.log('Fetching assets created by Alice...');
const base64PubKey = Ledger.public_key_to_base64(aliceKeyPair.get_pk());
await Utils.sleep(2);
const aliceAssets = await network.getCreatedAssets(base64PubKey);
const base64AssetType = Ledger.asset_type_from_jsvalue(aliceAssets[0].val);

assert(base64AssetType == tokenCode);