Skip to main content

QRL API - Wallet

The QRL Wallet Daemon allows additional functionality to the QRL Node installation giving access to wallet functions and advanced control of accounts.

Source code https://github.com/theQRL/QRL/blob/master/src/qrl/daemon/walletd.py

info

This API is available with the base QRL Python package install, and when used with the QRL walletd-rest-proxy automatic wallet management is simple.

Getting Started

Running the wallet daemon is simple. The qrl_walletd daemon comes default with the node software.

Simply execute the function after successfully installing a node.

qrl_walletd

this will create the local ~/.qrl/qrl_walletd.pid and ~/.qrl/walletd.log files in the default QRL directory. Wallet files will be created here as well when using the qrl_walletd API.

Requirements

  • QRL Node software installed on the localhost.
  • Access to a synced node with the walletd running and access to port default wallet daemon port 19010

Grpc Bash Tools

Accessing the Grpc commands using a basic Linux command line can be accomplished using a 3rd party tool. For this guide we are using a tool called grpcurl

info

From the gRPCurl Docs:

grpcurl is a command-line tool that lets you interact with gRPC servers. It's basically curl for gRPC servers.

Install gRPCurl

info

Golang is required for this method to work.

Install the grpcurl tools following the installation directions

go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

This installs the command into the bin sub-folder of wherever your $GOPATH environment variable points. (If you have no GOPATH environment variable set, the default install location is $HOME/go/bin). If this directory is already in your $PATH, then you should be good to go.

Setup gRPCurl

There are a few things needed to use the tools.

The QRL does not have reflection enabled by default, in order to use the grpcurl tools the QRL proto files must be available.

Clone the QRL repo
git clone https://github.com/theQRL/qrl
Add Required Google Proto Files

There are two proto files that we need to gather from the main grpc repository for the system to function.

First create the required directory for the google api proto files.

Create api directory
mkdir ~/qrl/src/qrl/protos/google/api
Annotations proto File
wget -O ~/qrl/src/qrl/protos/google/api/annotations.proto https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto
HTTP proto File
wget -O ~/qrl/src/qrl/protos/google/api/http.proto https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto

Test gRPCurl

Validate the local setup is correctly working.

List Available Functions
~/go/bin/grpcurl -plaintext -import-path ~/qrl/src/qrl/protos/ -proto ~/qrl/src/qrl/protos/qrlwallet.proto describe qrl.WalletAPI
service WalletAPI {
rpc AddNewAddress ( .qrl.AddNewAddressReq ) returns ( .qrl.AddNewAddressResp );
rpc AddNewAddressWithSlaves ( .qrl.AddNewAddressWithSlavesReq ) returns ( .qrl.AddNewAddressResp );
rpc ChangePassphrase ( .qrl.ChangePassphraseReq ) returns ( .qrl.ChangePassphraseResp );
rpc EncryptWallet ( .qrl.EncryptWalletReq ) returns ( .qrl.EncryptWalletResp );
rpc GetAddressFromPK ( .qrl.AddressFromPKReq ) returns ( .qrl.AddressFromPKResp );
rpc GetBalance ( .qrl.BalanceReq ) returns ( .qrl.BalanceResp );
rpc GetBlock ( .qrl.BlockReq ) returns ( .qrl.BlockResp );
rpc GetBlockByNumber ( .qrl.BlockByNumberReq ) returns ( .qrl.BlockResp );
rpc GetHeight ( .qrl.HeightReq ) returns ( .qrl.HeightResp );
rpc GetNodeInfo ( .qrl.NodeInfoReq ) returns ( .qrl.NodeInfoResp );
rpc GetOTS ( .qrl.OTSReq ) returns ( .qrl.OTSResp );
rpc GetPaginatedTransactionsByAddress ( .qrl.PaginatedTransactionsByAddressReq ) returns ( .qrl.PaginatedTransactionsByAddressResp );
rpc GetRecoverySeeds ( .qrl.GetRecoverySeedsReq ) returns ( .qrl.GetRecoverySeedsResp );
rpc GetTotalBalance ( .qrl.TotalBalanceReq ) returns ( .qrl.TotalBalanceResp );
rpc GetTransaction ( .qrl.TransactionReq ) returns ( .qrl.TransactionResp );
rpc GetTransactionsByAddress ( .qrl.TransactionsByAddressReq ) returns ( .qrl.TransactionsByAddressResp );
rpc GetWalletInfo ( .qrl.GetWalletInfoReq ) returns ( .qrl.GetWalletInfoResp );
rpc IsValidAddress ( .qrl.ValidAddressReq ) returns ( .qrl.ValidAddressResp );
rpc ListAddresses ( .qrl.ListAddressesReq ) returns ( .qrl.ListAddressesResp );
rpc LockWallet ( .qrl.LockWalletReq ) returns ( .qrl.LockWalletResp );
rpc RelayMessageTxn ( .qrl.RelayMessageTxnReq ) returns ( .qrl.RelayTxnResp );
rpc RelayMessageTxnBySlave ( .qrl.RelayMessageTxnBySlaveReq ) returns ( .qrl.RelayTxnResp );
rpc RelaySlaveTxn ( .qrl.RelaySlaveTxnReq ) returns ( .qrl.RelayTxnResp );
rpc RelaySlaveTxnBySlave ( .qrl.RelaySlaveTxnBySlaveReq ) returns ( .qrl.RelayTxnResp );
rpc RelayTokenTxn ( .qrl.RelayTokenTxnReq ) returns ( .qrl.RelayTxnResp );
rpc RelayTokenTxnBySlave ( .qrl.RelayTokenTxnBySlaveReq ) returns ( .qrl.RelayTxnResp );
rpc RelayTransferTokenTxn ( .qrl.RelayTransferTokenTxnReq ) returns ( .qrl.RelayTxnResp );
rpc RelayTransferTokenTxnBySlave ( .qrl.RelayTransferTokenTxnBySlaveReq ) returns ( .qrl.RelayTxnResp );
rpc RelayTransferTxn ( .qrl.RelayTransferTxnReq ) returns ( .qrl.RelayTxnResp );
rpc RelayTransferTxnBySlave ( .qrl.RelayTransferTxnBySlaveReq ) returns ( .qrl.RelayTxnResp );
rpc RemoveAddress ( .qrl.RemoveAddressReq ) returns ( .qrl.RemoveAddressResp );
rpc UnlockWallet ( .qrl.UnlockWalletReq ) returns ( .qrl.UnlockWalletResp );
}
Describe Functions
~/go/bin/grpcurl -plaintext -import-path ~/qrl/src/qrl/protos/ -proto ~/qrl/src/qrl/protos/qrlwallet.proto describe qrl.RelayTransferTokenTxnBySlaveReq
qrl.RelayTransferTokenTxnBySlaveReq is a message:
message RelayTransferTokenTxnBySlaveReq {
repeated string addresses_to = 1;
string token_txhash = 2;
repeated uint64 amounts = 3;
uint64 fee = 4;
string master_address = 5;
}
Query Local Node
~/go/bin/grpcurl -plaintext -import-path ~/qrl/src/qrl/protos/ -proto ~/qrl/src/qrl/protos/qrlwallet.proto localhost:19010  qrl.WalletAPI.GetNodeInfo
{
"version": "3.0.1 python",
"numConnections": "4",
"numKnownPeers": "5",
"uptime": "4475717",
"blockHeight": "119014",
"blockLastHash": "bb0d5e7e59c3c5455881245849b0c1536f361793c4491d4b7931ef39c8040400",
"networkId": "Testnet 2022"
}

Add New Address
~/go/bin/grpcurl -plaintext -import-path ~/qrl/src/qrl/protos/ -proto ~/qrl/src/qrl/protos/qrlwallet.proto -d '{"height": "6", "hash_function": "sha2_256"}' localhost:19010 qrl.WalletAPI.AddNewAddress

Methods

Method NameRequest TypeResponse Type
AddNewAddressAddNewAddressReqAddNewAddressResp
AddNewAddressWithSlavesAddNewAddressWithSlavesReqAddNewAddressResp
AddAddressFromSeedAddAddressFromSeedReqAddAddressFromSeedResp
ListAddressesListAddressesReqListAddressesResp
RemoveAddressRemoveAddressReqRemoveAddressResp
IsValidAddressValidAddressReqValidAddressResp
GetRecoverySeedsGetRecoverySeedsReqGetRecoverySeedsResp
GetWalletInfoGetWalletInfoReqGetWalletInfoResp
RelayTransferTxnRelayTransferTxnReqRelayTxnResp
RelayTransferTxnBySlaveRelayTransferTxnBySlaveReqRelayTxnResp
RelayMessageTxnRelayMessageTxnReqRelayTxnResp
RelayMessageTxnBySlaveRelayMessageTxnBySlaveReqRelayTxnResp
RelayTokenTxnRelayTokenTxnReqRelayTxnResp
RelayTokenTxnBySlaveRelayTokenTxnBySlaveReqRelayTxnResp
RelayTransferTokenTxnRelayTransferTokenTxnReqRelayTxnResp
RelayTransferTokenTxnBySlaveRelayTransferTokenTxnBySlaveReqRelayTxnResp
RelaySlaveTxnRelaySlaveTxnReqRelayTxnResp
RelaySlaveTxnBySlaveRelaySlaveTxnBySlaveReqRelayTxnResp
EncryptWalletEncryptWalletReqEncryptWalletResp
LockWalletLockWalletReqLockWalletResp
UnlockWalletUnlockWalletReqUnlockWalletResp
ChangePassphraseChangePassphraseReqChangePassphraseResp
GetTransactionsByAddressTransactionsByAddressReqTransactionsByAddressResp
GetTransactionTransactionReqTransactionResp
GetBalanceBalanceReqBalanceResp
GetTotalBalanceTotalBalanceReqTotalBalanceResp
GetOTSOTSReqOTSResp
GetHeightHeightReqHeightResp
GetBlockBlockReqBlockResp
GetBlockByNumberBlockByNumberReqBlockResp
GetAddressFromPKAddressFromPKReqAddressFromPKResp
GetNodeInfoNodeInfoReqNodeInfoResp

AddNewAddress

Adds new randomly generated address to the wallet located at ~/.qrl/walletd.json.

This function will create a single QRL address. For use when outgoing transactions will not exceed addresses one time signature tree height.

This address is limited to the initial OTS height given when generated and cannot be changes at a later time.

Using default settings this will generate a new address with:

  • OTS key height {"height": 10} or 1,0241,024 outgoing transactions
  • Using the {"hash_function": "shake_128"}

The newly generated address will be added to the ~/.qrl/walletd.json file. This file will be created if it does not already exist.

note

Ensure the tree height is large enough for your needs and transfer all funds out of the address before all OTS keys are used!

service WalletAPI {
rpc AddNewAddress(AddNewAddressReq) returns (AddNewAddressResp);
}


AddNewAddressWithSlaves

Adds a new address into the ~/.qrl/walletd.json wallet file and generates slaves for the address.

info

These slaves are not valid for use until they are broadcast onto the network in a RelaySlaveTxn transaction.

By default this function will generate a new address with:

  • Tree height 10 {"height": 10} or 1,0241,024 outgoing transactions
  • Three slave keys each with height 10 {"number_of_slaves":3}
  • Using the shake-128 hash function {"hash_function": "shake_128"}
  • The first five slave OTS keys will be preserved, beginning at {"index": 5}
    • Keys {0 - 4} are saved for backup or recovery for each slave generated.

This address and slave keys will be added to the ~/.qrl/walletd.json file, this file will be created if not existing.

service WalletAPI {
rpc AddNewAddressWithSlaves(AddNewAddressWithSlavesReq) returns (AddNewAddressResp);
}


AddAddressFromSeed

danger

Not implemented at this time

Usage details

service WalletAPI
{
//rpc AddAddressFromSeed(AddAddressFromSeedReq) returns (AddAddressFromSeedResp);
}


ListAddresses

List all addresses found in wallet file.

Returns an array of public addresses found in the walletd.json wallet file.

note

This function will return results from the walletd.json file located in the default location when the walletd-rest-proxy was started. Any manual changes to this file will require the proxy to be restarted to pickup the changes.

service WalletAPI {
rpc ListAddresses(ListAddressesReq) returns(ListAddressesResp);
}


RemoveAddress

Permanently remove a given address, including all private and slave keys, from the walletd.json file.

This action is irreversible and potentially destructive!

Backup the address keys prior file to removing using the GetRecoverySeeds function.

Removes given address from the the walletd.json wallet file. Requires a QRL address that exists in the ~/.qrl/walletd.json file.

note

The address to be removed must be given to the function {"address": ""}. This action is permanent.

service WalletAPI {
rpc RemoveAddress(RemoveAddressReq) returns (RemoveAddressResp);
}


IsValidAddress

Check if a QRL address is valid. Returns {"valid": "True"} if the QRL Address is valid.

This function requires a QRL address to lookup. Validates the public address given meets all requirements and is valid to be used as recipient of a transaction.

note

IsValidAddress expects a Q hex address format, example: Q0103006fa2d29c4acb9bc192581694a616d394f7ef2f35dd5ab5a4dddd865740a3f3293e54c560

service WalletAPI {
rpc IsValidAddress(ValidAddressReq) returns (ValidAddressResp);
}


GetRecoverySeeds

Print out the recovery seeds, or secret keys for the given QRL address if it exists in the local wallet file.

note

The address must exist in the wallet.

Returns the backup or recovery seeds for the address given.

service WalletAPI {
rpc GetRecoverySeeds(GetRecoverySeedsReq) returns (GetRecoverySeedsResp);
}


GetWalletInfo

Print info on the wallet.

Returns JSON array of information on the wallet located at /home/$USER/.qrl/walletd.json

  • Wallet Version
  • Address count
  • Encryption
service WalletAPI {
rpc GetWalletInfo(GetWalletInfoReq) returns (GetWalletInfoResp);
}


RelayTransferTxn

Send or Transfer funds from a QRL address in the wallet to another QRL address. This function will use the root OTS keys for the address.

Use this function to send QRL from an address contained in the /home/$USER/.qrl/walletd.json file to another QRL address, up to 100 recipients are allowed per transaction.

You must provide the ots_index for the transaction, keeping track of keys that have already been used to avoid any attempted key reuse.

Another method to gather the next available OTS key uses the GetOTS function which will return the next expected OTS key from the address. This method expects the keys to be used incrementally from OTS key 0.

info

For a more flexible address allowing additional levels of OTS slave keys, use an address created with the AddNewAddressWithSlaves and the RelayTransferTxnBySlave function.

service WalletAPI {
rpc RelayTransferTxn(RelayTransferTxnReq) returns (RelayTxnResp);
}


RelayTransferTxnBySlave

Relay a transfer transaction using a slave address contained in the local wallet.

This function will use a previously created address with slaves. This function will send funds from the master QRL address using the slave address to send.

note

OTS Key use for slave addresses is automatically tracked using this system.

service WalletAPI {
rpc RelayTransferTxnBySlave(RelayTransferTxnBySlaveReq) returns (RelayTxnResp);
}


RelayMessageTxn

Send up to 80 bytes of message data onto the chain.

Send message data onto the chain. Allows up to 80 Bytes of data to be sent onto the QRL blockchain.

Message Encoding

For more information on the QRL Message Encoding please see the Message documentation

service WalletAPI {
rpc RelayMessageTxn(RelayMessageTxnReq) returns (RelayTxnResp);
}


RelayMessageTxnBySlave

Send up to 80 bytes of message data onto the chain using a slave address.

Send message data onto the chain. Allows up to 80 Bytes of data to be sent onto the QRL blockchain using a slave address.

Message Encoding

For more information on the QRL Message Encoding please see the Message documentation

service WalletAPI {
rpc RelayMessageTxnBySlave(RelayMessageTxnBySlaveReq) returns (RelayTxnResp);
}


RelayTokenTxn

Create colored token on the QRL Blockchain.

QRL Tokens can be created on the QRL blockchain using this method.

These tokens can be initially owned by up to 100 addresses and have a variety of values that may be defined.

info

Tokens can be transferred between addresses however the initial details cannot be changed after the initial creation.

service WalletAPI
{
rpc RelayTokenTxn(RelayTokenTxnReq) returns (RelayTxnResp);
}


RelayTokenTxnBySlave

Create colored token on the QRL Blockchain.

QRL Tokens can be created on the QRL blockchain using this method will use the slave addresses associated to the local wallet address. this address must be created using the AddNewAddressWithSlaves method

These tokens can be initially owned by up to 100 addresses and have a variety of values that may be defined.

info

Tokens can be transferred between addresses however the initial details cannot be changed after the initial creation.

service WalletAPI {
rpc RelayTokenTxnBySlave(RelayTokenTxnBySlaveReq) returns (RelayTxnResp);
}


RelayTransferTokenTxn

Transfer tokens held in the local QRL wallet address to another QRL address.

This method will transfer tokens held in a QRL Address in the local wallet. Using this method you can transfer tokens between addresses.

note

To find all addresses held by an address use the QRL Block Explorer to look up the address and list all tokens held.

Additionally you can use another API to lookup the address details and tokens held by address. For instance the GetTokensByAddresss Method will return a list of tokens held by that address.

Requires token transaction hash

This method requires the token transaction hash from the token creation. This ensures the correct token is used for the transaction

service WalletAPI
{
rpc RelayTransferTokenTxn(RelayTransferTokenTxnReq) returns (RelayTxnResp);
}


RelayTransferTokenTxnBySlave

Transfer tokens held in the local QRL wallet address to another QRL address using a slave address.

This method will transfer tokens held in a QRL Address in the local wallet with a slave address. Using this method you can transfer tokens between addresses on the QRL blockchain.

This requires that the address that holds the tokens, was also created using the AddNewAddressWithSlaves method.

note

To find all addresses held by an address use the QRL Block Explorer to look up the address and list all tokens held.

Additionally you can use another API to lookup the address details and tokens held by address. For instance the GetTokensByAddresss Method will return a list of tokens held by that address.

Requires token transaction hash

This method requires the token transaction hash from the token creation. This ensures the correct token is used for the transaction

service WalletAPI {
rpc RelayTransferTokenTxnBySlave(RelayTransferTokenTxnBySlaveReq) returns (RelayTxnResp);
}


RelaySlaveTxn

Relays a generated hypertree XMSS slave address onto the QRL blockchain.

This function allows the address given to submit a slave address to the chain, authorizing the slave XMSS OTS keys to send transactions as the master address.

service WalletAPI {
rpc RelaySlaveTxn(RelaySlaveTxnReq) returns (RelayTxnResp);
}


RelaySlaveTxnBySlave

Relays a generated hypertree XMSS slave address onto the QRL blockchain using an already generated slave address.

This function allows the slave address given to submit a slave address to the chain, authorizing the slave XMSS OTS keys to send transactions as the master address.

service WalletAPI {
rpc RelaySlaveTxnBySlave(RelaySlaveTxnBySlaveReq) returns (RelayTxnResp);
}


EncryptWallet

Encrypt the local wallet file using AES encryption and a passphrase.

Using this method to encrypt the wallet will give additional security to the file at rest.

danger

Once encrypted the passphrase is required to access the wallet and all addresses contained within. If this passphrase is lost there is nothing that can be done to recover it!

While this will encrypt the walletd.json file the wallet Daemon uses, the file is accessible through the WalletD while running after you have unlocked the address.

service WalletAPI {
rpc EncryptWallet(EncryptWalletReq) returns (EncryptWalletResp);
}


LockWallet

Lock an encrypted wallet that has previously been unlocked.

Usage details

service WalletAPI {
rpc LockWallet(LockWalletReq) returns (LockWalletResp);
}


UnlockWallet

Short description

Usage details

service WalletAPI {
rpc UnlockWallet(UnlockWalletReq) returns (UnlockWalletResp);
}


ChangePassphrase

Changes the encryption Passphrase previously established for a wallet.

Usage details

service WalletAPI {
rpc ChangePassphrase(ChangePassphraseReq) returns (ChangePassphraseResp);
}


GetTransactionsByAddress

Get all transactions by QRL Address

Returns all transactions found by a given address.

service WalletAPI {
rpc GetTransactionsByAddress(TransactionsByAddressReq) returns (TransactionsByAddressResp);
}


GetTransaction

Lookup a transaction on the QRL Blockchain using the transaction Hash

Usage details

service WalletAPI {
rpc GetTransaction(TransactionReq) returns (TransactionResp);
}


GetBalance

Retrieve balance information for the address given.

Returns the address balance in shorshor if any available funds exist in this address.

info

Additional validation for an address can be found using the QRL Block explorer

service WalletAPI {
rpc GetBalance(BalanceReq) returns (BalanceResp);
}


GetTotalBalance

Return the balance for all QRL addresses contained in the main wallet.

This method returns the sum of all balances found in the wallet.

service WalletAPI {
rpc GetTotalBalance(TotalBalanceReq) returns (TotalBalanceResp);
}


GetOTS

Return

Usage details

service WalletAPI {
rpc GetOTS(OTSReq) returns (OTSResp);
}


GetHeight

Returns the block height of the walletd connected node

This method will return the latest block height known the the node that is connected to the walletd.

service WalletAPI {
rpc GetHeight(HeightReq) returns (HeightResp);
}


GetBlock

Return block data for given block header hash

Returns PlainBlock details for a given block header hash

service WalletAPI {
rpc GetBlock(BlockReq) returns (BlockResp);
}


GetBlockByNumber

Get QRL block data by block number from the QRL Blockchain.

Using a previously mined block number to lookup block data on-chain.

service WalletAPI {
rpc GetBlockByNumber(BlockByNumberReq) returns (BlockResp);
}


GetAddressFromPK

Returns typical QRL Address Q00010... from PK address 01060089256a7ea3...

Provide the PK of a QRL address and return the typical address prepended with a Q.

service WalletAPI {
rpc GetAddressFromPK(AddressFromPKReq) returns (AddressFromPKResp);
}


GetNodeInfo

Returns the node information for the connected QRL node.

Usage details

service WalletAPI {
rpc GetNodeInfo(NodeInfoReq) returns (NodeInfoResp);
}


Messages

These golang messages support the methods above.

Message NameDetails
PlainTransactionThis is the main transaction method and includes Transfer, CoinBase, LatticePublicKey, Message, Token, TransferToken, and Slave transaction types
PlainAddressAmountReturns address and amount for balance lookup
MiniTransactionReturns transaction_hash, outgoing or incoming and the amount of the transaction
OTSBitfieldByPageReturns ots_bitfield and page_number by page for an address
PlainBlockHeaderReturns hash_header, block_number, timestamp_seconds, hash_header_prev, reward_block, reward_fee, merkle_root, mining_nonce, extra_nonce from block given
PlainBlockreturns PlainBlockHeader, PlainTransaction, PlainGenesisBalance
PlainGenesisBalancereturns the address, balance from Genesis

PlainTransaction

message PlainTransaction {
string master_addr = 1;
uint64 fee = 2;
string public_key = 3;
string signature = 4;
uint64 nonce = 5;
string transaction_hash = 6;
string signer_addr = 7;

oneof transactionType {
Transfer transfer = 8;
CoinBase coinbase = 9;
LatticePublicKey latticePK = 10;
Message message = 11;
Token token = 12;
TransferToken transfer_token = 13;
Slave slave = 14;
}

//////////
message Transfer {
repeated string addrs_to = 1;
repeated uint64 amounts = 2;
}

message CoinBase {
string addr_to = 1;
uint64 amount = 2;
}

message LatticePublicKey {
string kyber_pk = 1;
string dilithium_pk = 2;
}

message Message {
string message_hash = 1;
}

message Token {
string symbol = 1;
string name = 2;
string owner = 3;
uint64 decimals = 4;
repeated PlainAddressAmount initial_balances = 5;
}

message TransferToken {
string token_txhash = 1;
repeated string addrs_to = 2;
repeated uint64 amounts = 3;
}

message Slave {
repeated string slave_pks = 1;
repeated uint32 access_types = 2;
}
}

PlainAddressAmount

message PlainAddressAmount {
string address = 1;
uint64 amount = 2;
}

MiniTransaction

message MiniTransaction {
string transaction_hash = 1;
bool out = 2;
uint64 amount = 3;
}

OTSBitfieldByPage

message OTSBitfieldByPage {
repeated bytes ots_bitfield = 1;
uint64 page_number = 2;
}

PlainBlockHeader

message PlainBlockHeader {
// Header
string hash_header = 1;

uint64 block_number = 2;
uint64 timestamp_seconds = 3;
string hash_header_prev = 4;
uint64 reward_block = 5;
uint64 reward_fee = 6;
string merkle_root = 7;

uint32 mining_nonce = 8;
uint64 extra_nonce = 9;
}

PlainBlock

message PlainBlock {
PlainBlockHeader header = 1;
repeated PlainTransaction transactions = 2;

repeated PlainGenesisBalance genesis_balance = 3;
}

PlainGenesisBalance

message PlainGenesisBalance {
string address = 1;
uint64 balance = 2;
}