Chapter 3: CLI

Extending the application

To further enhance the LNS blockchain application, create two new LNS specific commands which can be executed directly from the command line:

  • lns:resolve Command to resolve a provided domain name to an account address.

  • lns:lookup: Command to perform a reverse lookup for a provided account address. Returns the default domain name of an account.

The application CLI already contains numerous general commands by default. They are directly created when the application is bootstrapped using Lisk Commander with lisk init.

An overview of all existing CLI commands can be seen by navigating to the root folder of the blockchain application, and running the following command:

./bin/run

This will return the command reference for the application CLI:

Lisk-SDK Application

VERSION
  lns/0.1.0 darwin-x64 node-v18.16.0

USAGE
  $ lisk-name-service [COMMAND]

TOPICS
  account        Commands relating to lisk-name-service accounts.
  block          Commands relating to lisk-name-service blocks.
  blockchain     Commands relating to lisk-name-service blockchain data.
  config         Commands relating to lisk-name-service node configuration.
  forger-info    Commands relating to lisk-name-service forger-info data.
  forging        Commands relating to lisk-name-service forging.
  genesis-block  Creates genesis block file.
  node           Commands relating to lisk-name-service node.
  passphrase     Commands relating to lisk-name-service passphrases.
  transaction    Commands relating to lisk-name-service transactions.

COMMANDS
  autocomplete  display autocomplete installation instructions.
  console       Lisk interactive REPL (Read-eval-print loop), session to run commands.
  hash-onion    Create hash onions to be used by the forger.
  help          display help for lisk-name-service.
  start         Start Blockchain Node.

We already used the CLI in this tutorial to start the LNS application. Now, the plan is to create a new topic lns, and to define the two new commands lns:resolve and lns:lookup to be part of it.

Navigate to lisk-name-service/lns/commands/ and create a new folder lns, which will contain the files for the new commands.

lisk-name-service/lns/commands/
mkdir lns
cd lns

The application CLI commands are based on OCLIF.

Check out their documentation to get a deeper understanding on how the CLI commands are constructed.

lns:resolve

Create a new file resolve.ts and import the BaseIPCClientCommand from Lisk Commander.

Create a new class LNSResolveCommand, which extends the class BaseIPCClientCommand of Lisk Commander.

/lisk-name-service/lns/src/commands/lns/resolve.ts
import { BaseIPCClientCommand } from 'lisk-commander';

export class LNSResolveCommand extends BaseIPCClientCommand {

}

The BaseIPCClientCommand already contains two optional default flags for the command: pretty and data-path. It also contains an API client to the node which will be used to invoke actions in the LNS application via the CLI.

The only method required for a new command is the .run() function.

In this particular use case, one argument is needed, the name to resolve.

You may also add some examples about the usage of the command, which will be added to the auto-generated command reference.

/lisk-name-service/lns/src/commands/lns/resolve.ts
import { BaseIPCClientCommand } from 'lisk-commander';

export class LNSResolveCommand extends BaseIPCClientCommand {
    // Available command arguments
	static args = [
		{
			name: 'name',
			required: true,
			description: 'Name to resolve.',
		},
	];

	// Command usage examples
	static examples = ['lns:resolve jhon.lsk', 'lns:resolve jhon.lsk --pretty'];

	// Executed every time the respective command is executed in the CLI.
	public async run(): Promise<void> {
	    // Parses the provided command arguments
		const { args } = this.parse(LNSResolveCommand);
		// Get the name argument from the arguments object
		const { name } = args as { name: string };

		// Invoke the action 'lns:resolveName' on the node via the API client.
		const result = await this._client?.invoke('lns:resolveName', { name });

		// If the node returns a result, print it to the console as JSON.
		if (result) {
			return this.printJSON(result);
		}

		// Display this message, if the name couldn't be resolved successfully.
		return this.log(`Can not resolve name "${name}"`);
	}
}

This is all the code required to add the new command to the LNS blockchain application.

As can be seen, there is not much logic required to be implemented here. The action lns:resolveName can be reused to get the address, based on the address that was provided as the command argument.

lns:lookup

Create a new file lookup.ts and paste the code snippet below. This is all the code required to add the lns:lookup command to the LNS blockchain application.

The implementation is analog to the previous command, nevertheless, now use the corresponding action lns:lookup to get the domain name for the provided account address.

/lisk-name-service/lns/src/commands/lns/lookup.ts
import { BaseIPCClientCommand } from 'lisk-commander';

export class LNSLookupCommand extends BaseIPCClientCommand {
	static args = [
		{
			name: 'address',
			required: true,
			description: 'Address to lookup',
		},
	];

	static examples = ['lns:lookup <hex-address>', 'lns:lookup afe179fa12a988c1244444479c --pretty'];

	public async run(): Promise<void> {
		const { args } = this.parse(LNSLookupCommand);
		const { address } = args as { address: string };

		if (address !== Buffer.from(address, 'hex').toString('hex')) {
			this.error('Invalid address format');
		}

		const result = await this._client?.invoke('lns:lookupAddress', { address });

		if (result) {
			return this.printJSON(result);
		}

		return this.log(`Can not find account with address "${address}"`);
	}
}

Trying out the new CLI commands

Display the CLI reference once again. The new topic lns should now appear under TOPICS:

./bin/run
Lisk-SDK Application

VERSION
  lns/0.1.0 darwin-x64 node-v18.16.0

USAGE
  $ lisk-name-service [COMMAND]

TOPICS
  account        Commands relating to lisk-name-service accounts.
  block          Commands relating to lisk-name-service blocks.
  blockchain     Commands relating to lisk-name-service blockchain data.
  config         Commands relating to lisk-name-service node configuration.
  forger-info    Commands relating to lisk-name-service forger-info data.
  forging        Commands relating to lisk-name-service forging.
  genesis-block  Creates genesis block file.
  lns
  node           Commands relating to lisk-name-service node.
  passphrase     Commands relating to lisk-name-service passphrases.
  transaction    Commands relating to lisk-name-service transactions.

COMMANDS
  autocomplete  display autocomplete installation instructions.
  console       Lisk interactive REPL session to run commands.
  hash-onion    Create hash onions to be used by the forger.
  help          display help for lisk-name-service.
  start         Start Blockchain Node.

If the LNS application is not already running, start it again:

$ ./bin/run start

Now resolve the domain name my-name.lsk, which was registered previously.

./bin/run lns:resolve my-name.lsk

This will return the corresponding LNS object:

{
  "ownerAddress":"39cdb96af23eaf431ef3fb8e5da58d9950c3bc96",
  "name":"my-name.lsk",
  "ttl":4000,
  "expiry":1694173170,
  "createdAt":1631101170,
  "updatedAt":1631105400,
  "records":[{
    "type":2,
    "label":"my-twitter",
    "value":"@followMe"
  }]
}

When the resolve command works as expected, copy the ownerAdress from the returned LNS object and provide it as an argument for the lns:lookup command:

$ ./bin/run lns:lookup 39cdb96af23eaf431ef3fb8e5da58d9950c3bc96

This will return the corresponding LNS object:

{
  "ownerAddress":"39cdb96af23eaf431ef3fb8e5da58d9950c3bc96",
  "name":"awesome.lsk",
  "ttl":3600,
  "expiry":1662734230,
  "createdAt":1631198230,
  "updatedAt":1631198230,
  "records":[]
}

Each account can register multiple domain names to their account. The address lookup returns a different object for the provided address, because the account has set awesome.lsk to be the default domain name for this account.

How to display the topic reference:

./bin/run lns
USAGE
  $ lisk-name-service lns:COMMAND

COMMANDS
  lns:lookup
  lns:resolve

How to display the command reference:

$ ./bin/run lns:resolve --help
USAGE
  $ lisk-name-service lns:resolve NAME

ARGUMENTS
  NAME  Name to resolve.

OPTIONS
  -d, --data-path=data-path  Directory path to specify where node data is stored. Environment variable "LISK_DATA_PATH" can also be
                             used.

  --pretty                   Prints JSON in pretty format rather than condensed.

EXAMPLES
  lns:resolve jhon.lisk
  lns:resolve jhon.lisk --pretty