Bridge Champ Integration - part 3 - account management

In Part 2 we explained how we store private information and how we securely
initialize the cryptographic root node from the secret seed. In this article, we'll
discuss how we can derive the user account information from the root node key

To assign a unique blockchain account address for each Bridge Champ user, we
first assign each user a sequence number to serve as a unique identifier. Using this
unique identifier, we can derive the account address using one of the methods
explained below, and both should reach the same result. The inputs to the
algorithms are the root node and account unique index, and the output is the
blockchain account address. The two options are:
(1) root public key --> user public key --> user account
(2) root private key --> user private key --> user public key --> user account

Method (1) is used to derive user accounts based only on public information. This
is useful for a public-facing server checking account balances and submitting

Method (2) is used to derive user accounts based only on private information. We
use this method on a more secure server to sign transactions.
Here is a code snippet that demonstrates how we derive the same account
address using each of these methods

public long getAccountId(int pathIndex) {
    if (!isBooted()) {
        return 0;
    var masterPublicKey = new SerializedMasterPublicKey(rootNode.getSerializedMasterPublicKey());
    var childNodeFromMaster = KeyDerivation.deriveChildPublicKey(masterPublicKey, pathIndex);
    var childNodeFromPrivateKey = KeyDerivation.deriveChildPrivateKey(rootNode, pathIndex);
    if (!Arrays.equals(childNodeFromMaster.getPublicKey(), childNodeFromPrivateKey.getPublicKey())) {
        throw new IllegalStateException("public key derivation problem");
    return Account.getId(childNodeFromMaster.getPublicKey());

As you can see, we first verify that the cryptosystem is initialized i.e. booted and if
so we first obtain the child account public key from the master public key and then
from the master private key.
We compare the child's public keys to ensure they are the same and generate the
account address.

We now have the account address of the user, and we can store this address in the
database since this is public info that will never change. We can use it for example
to check the user account balance

public Balance getBalance(String account) {
    var response = GetBalancesCall.create().account(account).chain(CHAIN_ID).remote(remoteUrl).callNoError();
    var balanceJson = response.getJo("balances").getJo("" + CHAIN_ID);
    return new Balance(balanceJson.getLong("unconfirmedBalanceNQT"), balanceJson.getLong("balanceNQT"));

Now that we know how to generate user accounts, we will learn how to submit and
sign transactions in the next part.

To learn more about the overall solution architecture see part 4