SQL on Lisk blockchain via Lisk Docker and Adminer

Lisk Explorer is a great tool to view information stored on the Lisk blockchain, but in order to inspect the blockchain data in more detail we'll run SQL queries on Lisk blockchain database. By querying the database directly, more information can be extracted and the queries can be customized to select and analyze the data we want. E.g. it's easy to find out which delegate paid you the most, show all transactions for an account for the past n days, view lisk distribution and so on.

Lisk Docker will be used to automate the setup of the server, Lisk Core and PostgreSQL web client.

This tutorial assumes you have Docker installed and running.

First, download and unzip this archive.

(The archive contains docker-compose.yml, a copy of Lisk Docker mainnet image with additional postgres port exposed[1] and added Adminer image - PostgreSQL web client.)

Open Terminal, cd to directory where you unzipped archive and run:

make coldstart  

Get a coffee and wait until it completes. This can take a few minutes.

(It will download the latest Lisk blockchain snapshot, docker images, create docker containers, start Lisk Core and restore Lisk blockchain database from the snapshot.)

Once done, it will auto-start synchronizing Lisk Core with main chain.

(You can visit http://localhost:8000/api/blocks/getHeight to check the current block height.)

Now open http://localhost:8080 to access Adminer login page:

Use:

System: PostgreSQL  
Server: db  
Username: lisk  
Password: password  
Database: lisk_main  

Once logged in, click SQL command link and you're ready to execute SQL queries!

Here are some SQL queries you may find useful:

Which delegate paid me the most?

-- Which delegate paid me the most
WITH xs AS (  
  SELECT "senderId" AS address, username AS delegate
  FROM trs
  JOIN delegates ON delegates."transactionId" = trs."id"
)
SELECT  
  delegate, 
  SUM(trunc(amount::numeric/100000000, 8)) as amount
FROM trs  
JOIN xs ON xs.address = trs."senderId"  
WHERE "recipientId" = '14775206447342278732L' AND amount > 0  
GROUP BY delegate  
ORDER BY amount DESC;  
-- Which delegate paid me the most in the last n days, months...
WITH xs AS (  
  SELECT "senderId" AS address, username AS delegate
  FROM trs
  JOIN delegates ON delegates."transactionId" = trs."id"
), constants(LISK_EPOCH) AS (
  -- 1464109200 = Lisk Epoch (2016–05–24T17:00:00.000Z)
  VALUES (1464109200)
)
SELECT  
  delegate, 
  SUM(trunc(amount::numeric/100000000, 8)) as amount
FROM constants, trs  
JOIN xs ON xs.address = trs."senderId"  
JOIN blocks ON blocks.id = trs."blockId"  
WHERE to_timestamp(blocks."timestamp" + LISK_EPOCH)  
  BETWEEN (now() - '6 months'::interval) AND now() -- Use '1 day', '2 days', '1 week', '6 months'...
AND amount > 0  
AND "recipientId" = '14775206447342278732L'  
GROUP BY delegate  
ORDER BY amount DESC;  

Lisk Distribution

SELECT t.arange AS amount_range, COUNT(*) AS num_accounts  
FROM (  
  SELECT address, username AS delegate,
    CASE
      WHEN balance/100000000 <= 0                                             THEN '0'
      WHEN balance/100000000 > 0          AND balance/100000000 <= 1          THEN '0-1'
      WHEN balance/100000000 > 1          AND balance/100000000 <= 10         THEN '1-10'
      WHEN balance/100000000 > 10         AND balance/100000000 <= 100        THEN '10-100'
      WHEN balance/100000000 > 100        AND balance/100000000 <= 1000       THEN '100-1000'
      WHEN balance/100000000 > 1000       AND balance/100000000 <= 10000      THEN '1000-10000'
      WHEN balance/100000000 > 10000      AND balance/100000000 <= 100000     THEN '10000-100000'
      WHEN balance/100000000 > 100000     AND balance/100000000 <= 1000000    THEN '100000-1000000'
      WHEN balance/100000000 > 1000000    AND balance/100000000 <= 10000000   THEN '1000000-10000000'
      WHEN balance/100000000 > 10000000   AND balance/100000000 <= 100000000  THEN '10000000-100000000'
      WHEN balance/100000000 > 100000000                                      THEN '100000000+'
      ELSE 'unknown'
    END AS arange
  FROM mem_accounts
) AS t
GROUP BY t.arange  
ORDER BY t.arange DESC;  

How much lisk is forged by each delegate

WITH xs AS (  
  SELECT "senderId" AS address, username AS delegate, "senderPublicKey" AS publicKey
  FROM trs
  JOIN delegates ON delegates."transactionId" = trs."id"
)
SELECT  
  delegate,
  SUM(reward/100000000) as reward
FROM blocks  
JOIN xs ON xs.publicKey = blocks."generatorPublicKey"  
WHERE reward > 0  
GROUP BY delegate  
ORDER BY reward DESC;  

Total Lisk from forging (block rewards)

SELECT  
  SUM(reward/100000000) as reward
FROM blocks;  

Above returns 24,453,036. Current supply according to Lisk Explorer is 124,453,036. I guess supply is the sum of the block (forging) rewards and 100,000,000 (premind Lisk)

All outgoing transactions for an account

-- Outgoing transactions
WITH constants(LISK_EPOCH) AS (  
  -- 1464109200 = Lisk Epoch (2016–05–24T17:00:00.000Z)
  VALUES (1464109200)
)
SELECT to_timestamp(blocks."timestamp" + LISK_EPOCH) as datetime,  
  "senderId", trunc(amount::numeric/100000000, 8) as amount
FROM constants, trs  
JOIN blocks ON blocks.id = trs."blockId"  
WHERE "senderId" = '14775206447342278732L' AND amount > 0;  

All incoming transactions for an account

-- Incoming transactions
WITH constants(LISK_EPOCH) AS (  
  -- 1464109200 = Lisk Epoch (2016–05–24T17:00:00.000Z)
  VALUES (1464109200)
)
SELECT to_timestamp(blocks."timestamp" + LISK_EPOCH) as datetime,  
  "senderId",
  --"recipientId", 
  trunc(amount::numeric/100000000, 8) as amount
FROM constants, trs  
JOIN blocks ON blocks.id = trs."blockId"  
WHERE "recipientId" = '14775206447342278732L' AND amount > 0  
ORDER BY amount DESC;  
--ORDER BY datetime DESC;

To clean

docker-compose down  

To start again

docker-compose up  

[1] Not necessary for this tutorial, it's required if you want to use own PostgreSQL client eg. pgAdmin