Architectural overview

Logo

The Lisk Framework is an application framework which integrates the Lisk Elements packages in order to form a functioning Lisk blockchain application. Its modular architecture allows developers to extend the off-chain and on-chain logic with user defined plugins and modules.

The Lisk Framework provides a consistent and intuitive interface between each module and plugin.

The diagram shown below provides a high-level overview of the architecture:

lisk-framework-architecture

The framework architecture is constructed of three different layer of abstractions. The Application object encapsulates all together to provide a user facing interface.

On-Chain Architecture

This layer of abstraction provide collection features to configure and run any business logic on the blockchain. The data associated with features developed with on-chain architecture can be saved to blockchain and business logic can be executed as well.

More information can be found in section On-chain architecture

Off-Chain Architecture

Some business flows or requirements implementation required to perform some computation which is based on the blockchain data but not required to change alter any blockchain state. It could be a simple use case as creating HTTP API for your blockchain or providing some aggregation service. The off-chain architecture provide features to cover all such use cases.

More information can be found in section Off-chain architecture

Communication Architecture

Some applications require to work externally still need to communicate with the blockchain. It could be a CLI or any app developed independently in any other technology stack. The communication architecture provides two industry standard communication formats, one is Inter Process Communication (IPC) and other is Web Sockets (WS). You can connect either to IPC or WS interface and can communicate with the blockchain.

More information can be found in section Communication architecture

Application

The Application class is an entry point to create a blockchain application. It can be instantiated in two ways:

//Initiates the Application including all default modules
const app = Application.defaultApplication(genesisBlock, config);
//Initiates the Application without default modules
const app = new Application(genesisBlock, config);

genesisBlock represents the Genesis block and config represents the application Configuration.

The recommended method to create an Application instance, is to use the defaultApplication since it comes with the default modules. If the class constructor is used, the modules need to be registered manually.

Registering modules and plugins

In order to register an additional module, the function below should be used.

app.registerModule(CustomModule);

Additionally, any plugin may be registered with the function below.

app.registerPlugin(CustomPlugin);

Configuration

The application config object is passed to the Application and must follow the configuration schema.

Configuration schema
let config = {
  // Required. Default: ~/.lisk
  // rootPath defines the root path for all data to be stored
  rootPath: string,
  // Required. Default: alpha-sdk-app
  // label defines the process name and folder within the root path
  label: string,
  // Required. Default: 0.0.0
  // version must follow semver format
  version: string,
  // Required. Default: 2.0
  // network version defines a P2P network version
  networkVersion: string,
  // Required.
  // rpc defines communication behavior
  rpc: {
    // Default: false
    // enabled creates IPC socket if true
    enable: boolean,
    // Default: ipc
    // enabled communication through 'ipc' or 'ws'
    mode:'ipc',
    // Default: 8080
    // In case of `mode` is set to `ws`, this port used
    port: 8080
  },
  // Required.
  // forging holds delegate information for forging
  forging: {
    // Optional. Default: 2
    // waitThreshold defines the extra waiting time for the last block before forging
    waitThreshold: number,
    // Optional.
    // delegates holds the delegate information for forging
    delegates: {
      // Required.
      // address defines the address of the delegate
      address: string,
      // Required.
      // encryptedPassphrase defines the encrypted passphrase
      encryptedPassphrase: string,
      // Required.
      // hashOnion holds the seed reveal to put in block header
      hashOnion: {
        // Required.
        // count holds the total number of hash onions
        count: number,
        // Required.
        // distance holds a distance between each hash onion
        distance: number,
        // Required.
        // hashes holds the seed reveal for every distance
        hashes: string[]
      }
    }[],
    // Optional. Default: false
    // force defines whether to use a default password and enable forging by default
    force?: boolean,
    // Optional.
    // defaultPassword defines a password to use to decrypt the encrypted Passphrase
    defaultPassword?: string
  },
  // Required.
  // network holds the network information of the node
  network: {
    // Required. Default: 5000
    // port defines an open port for P2P incoming connections
    port: number,
    // Required.
    // seedPeers defines an entry point of the network
    seedPeers: { ip: string, port: number }[],
    // Optional.
    // blacklistedIPs defines IP address which the node will reject the connection for both outbound and inbound connections
    blacklistedIPs?: string[],
    // Optional.
    // fixedPeers defines peers which will always try to connect for outbound connections
    fixedPeers?: { ip: string, port: number }[],
    // Optional.
    // whitelistedPeers defines peers that are always allowed to connect to the node on inbound connections
    whitelistedPeers?: { ip: string, port: number }[],
    // Optional. Default: 86400000 (24h)
    // peerBanTime defines the length of banning in milliseconds
    peerBanTime?: number,
    // Optional.
    // connectTimeout defines a timeout for a connection
    connectTimeout?: number,
    // Optional.
    // actTimeout defines a timeout for response from a peer
    ackTimeout?: number,
    // Optional. Default: 20
    // maxOutboundConnections defines a maximum number of outbound connection allowed
    maxOutboundConnections?: number,
    // Optional. Default: 100
    // maxInboundConnections defines a maximum number of inbound connection allowed
    maxInboundConnections?: number,
    // Optional. Default: 16
    // sendPeerLimit defines a maximum peer to send information when “send” is called
    sendPeerLimit?: number,
    // Optional. Default: 200
    // maxPeerDiscoveryResponseLength defines a maximum length for the peer information response of peer discovery
    maxPeerDiscoveryResponseLength?: number,
    // Optional. Default: 3048576
    // wsMaxPayload defines maximum size of the payload allowed per communication
    wsMaxPayload?: number,
    // Optional. Default: true
    // advertiseAddress defines whether to announce the IP/Port other peers
    advertiseAddress?: boolean
  },
  // Optional.
  // logger holds information for the logging
  logger: {
    // Optional. Default: lisk.log
    // logFileName defines a name for the log file
    logFileName: string,
    // Optional. Default: info
    // fileLogLevel defines the log level output for the file logging
    fileLogLevel: string,
    // Optional. Default: none
    // consoleLogLevel defines the log level output for the console logging
    consoleLogLevel: string
  },
  // Required.
  // genesisConfig holds the blockchain protocol configuration
  // it is also passed to the module constructor
  genesisConfig: {
    // Since all genesis config options will be passed to all modules, the developer can add an extra config specific for a module
    myCustomProperty: unknown,
    // Optional. Default: 68
    // bftThreshold defines a threshold for pre-vote and pre-commit
    bftThreshold: number,
    // Optional. Default: Lisk
    // communityIdentifier defines a community identifier used to create the network identifier
    communityIdentifier: string,
    // Optional. Default: 10
    // blockTime defines the frequency of blocks to be created
    blockTime: number,
    // Optional. Default: 15360 (15kb)
    // maxPayloadLength defines a maximum payload size allowed in a block in bytes
    maxPayloadLength: number,
    // Optional.
    // rewards defines a block reward schedule
    rewards: {
      // Optional. Default: [‘500000000’, ‘400000000’, ‘300000000’, ‘200000000’, ‘100000000’]
      // milestones defines the block reward for every distance
      milestones: string[],
      // Optional. Default: 2160
      // offset defines at which height the block reward is given
      offset: number,
      // Optional. Default: 3000000
      // distance defines the duration of the each milestone
      distance: number
    },
    // Optional. Default: 1000
    // minFeePerByte defines a minimum fee per byte for a transaction
    minFeePerByte: number,
    // Optional.
    // baseFees defines a additional base fee to be included in the calculation of the minimum fee for a transaction
    baseFees: {
      moduleID: number,
      assetID: number,
      baseFee: string,
    }[]
  },
  // Optional.
  // transactionPool defines custom properties of the transaction pool
  transactionPool: {
    // Optional. Default: 4096
    // maxTransactions defines a maximum number of transactions in the pool
    maxTransactions?: number,
    // Optional. Default: 64
    // maxTransactionsPerAccount defines a maximum number of transactions in the pool per sender account
    maxTransactionsPerAccount?: number,
    // Optional. Default: 10800000
    // transactionExpiryTime defines timeout of the transaction in the pool in milliseconds
    transactionExpiryTime?: number,
    // Optional. Default: 0
    // minEntranceFeePriority defines a minimum fee priority required to be added to the transaction pool
    minEntranceFeePriority?: string,
    // Optional. Default: 10
    // minReplacementFeeDifference defines a minimum fee difference to replace a transaction with the same nonce
    minReplacementFeeDifference?: string,
  },
  // Optional.
  // plugins holds a config which is passed to a particular plugin.
  // pluginAlias is a variable name that is dependant on the installed plugin
  plugins: {
    httpApi: {
      port: number, //default: 4000,
      whiteList: string[], //default: ['127.0.0.1'],
      cors: {
        origin: string, //default: '*',
        methods?: string[], //default: ['GET', 'POST', 'PUT'],
      },
      limits: {
        max: number, //default: 0,
        delayMs: number, //default: 0,
        delayAfter: number, //default: 0,
        windowMs: number, //default: 60000,
        headersTimeout: number, //default: 5000,
        serverSetTimeout: number, //default: 20000,
      },
    },
  }
}

Genesis block

A genesis block must be given to the application, and all networks should have a different genesis block.

The genesis block describes the very first block in the blockchain. It defines the initial state of the blockchain on start of the network.

The genesis block is not forged by a delegate, such as all of the other blocks which come after the genesis block. Instead it is defined by the developer, when creating the Application instance of the blockchain application.

The Lisk SDK exposes an object genesisBlockDevnet that holds all of the required important information to spin up a local development network.

Genesis block schema
const genesisBlock = {
  header: {
    generatorPublicKey: "",
    // height can be either 0 or regenesis height
    height: number,
    // empty buffer or merkle root of the previous blocks from previous network
    previousBlockID: Buffer,
    reward: 0n,
    signature: "",
    // timestamp of the blockchain in unix timestamp in second
    timestamp: number,
    // transactionRoot is alway empty hash
    transactionRoot: Buffer.from('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 'hex'),
    version: 0,
    asset: {
      // number of initial round to use the initDelegates
      initRounds: number,
      // address of initial delegates
      initDelegates: Buffer[],
      // encoded accounts for the initial state
      accounts: Buffer[],
    },
  },
  payload: [],
}
A valid genesis block can be created using @liskhq/lisk-genesis.