Skip to content

Commit

Permalink
feat(cli): add dashboard mode of ckb-cli
Browse files Browse the repository at this point in the history
which provides convenience developping dapps locally.
  • Loading branch information
Keith-CY committed Jun 10, 2019
1 parent e59ac63 commit 9accdeb
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 2 deletions.
10 changes: 9 additions & 1 deletion packages/ckb-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ $ npm install -g @nervosnetwork/ckb-cli

## Usage

### REPL

```sh
$ @nervosnetwork/ckb-cli <url to the node>
$ ckb-cli i <url to the node>
ckb => connected to <url to the node>
ckb => await core.rpc.getTipBlockNumber()
'6905'
Expand Down Expand Up @@ -63,3 +65,9 @@ ckb => await core.rpc.getBlock('0x89de946313839a8a77749b6218d4d7ab3513910c5ed860
proposals: [] }
ckb =>
```
### Dashboard
```sh
$ ckb-cli d <url to the node>
```
3 changes: 3 additions & 0 deletions packages/ckb-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@
},
"dependencies": {
"@nervosnetwork/ckb-sdk-core": "0.13.0",
"blessed": "0.1.81",
"blessed-contrib": "4.8.16",
"commander": "2.20.0",
"inquirer": "6.2.1"
},
"devDependencies": {
"@types/blessed": "0.1.11",
"@types/crypto-js": "3.1.43",
"@types/inquirer": "6.0.0"
},
Expand Down
169 changes: 169 additions & 0 deletions packages/ckb-cli/src/dashboard/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import Core from '@nervosnetwork/ckb-sdk-core'
import * as contrib from 'blessed-contrib'
import { screen } from 'blessed'

const { grid: Grid, table } = contrib

const MAX_BLOCKS = 50
const MAX_TRANSACTIONS = 50
let listener: any

interface ChainInfo {
url: string
version: string
tipNumber: string
}
type Block = [string, string, number] // [number, hash, tx count]
type Transaction = [string, string, string] // [hash, inputs, outputs]
interface RenderedData {
chainInfo: ChainInfo
blocks: Block[]
transactions: Transaction[]
}

const data: RenderedData = {
chainInfo: {
url: '',
version: '',
tipNumber: '',
},
blocks: [],
transactions: [],
}

const dashboard = (core: Core) => {
const MainScreen = screen({
smartCSR: true,
cursor: {
artificial: true,
shape: 'underline',
blink: false,
color: 'white',
},
})

MainScreen.key(['escape', 'C-c'], () => process.exit(0))

const Layout = new Grid({
rows: 12,
cols: 12,
screen: MainScreen,
})

const chainInfo = Layout.set(0, 0, 2, 12, table, {
label: 'Chain Infomation',
fg: 'white',
columnSpacing: 10,
columnWidth: [30, 60, 30],
interactive: false,
data: {
headers: [],
data: [],
},
})

const updateChainInfo = (info: ChainInfo) => {
chainInfo.setData({
headers: Object.keys(info).map(() => ''),
data: Object.entries(info),
})
MainScreen.render()
}

const blocks = Layout.set(2, 0, 3, 12, table, {
label: 'Block List',
fg: 'white',
columnSpacing: 10,
columnWidth: [15, 66, 10],
interactive: false,
scrollable: true,
data: {
headers: [],
data: [],
},
})

const updateBlocks = (info: Block[]) => {
blocks.setData({
headers: ['block number', 'hash', 'tx count'],
data: info,
})
MainScreen.render()
}

const transactions = Layout.set(5, 0, 7, 12, table, {
label: 'Transaction List',
fg: 'white',
columnSpacing: 10,
columnWidth: [66, 30, 60],
interactive: false,
scrollable: true,
data: {
headers: [],
data: [],
},
})

const updateTransactions = (info: Transaction[]) => {
transactions.setData({
headers: ['hash', 'inputs', 'outputs'],
data: info,
})
MainScreen.render()
}

const fetchAndUpdateChainInfo = async () => {
const [nodeInfo, tipNumber] = await Promise.all([core.rpc.localNodeInfo(), core.rpc.getTipBlockNumber()])
data.chainInfo = {
url: core.node.url,
version: nodeInfo.version,
tipNumber,
}
updateChainInfo(data.chainInfo)
}

const fetchAndUpdateBlock = async () => {
const block = await core.rpc.getBlockByNumber(data.chainInfo.tipNumber)
data.blocks = [
[block.header.number, block.header.hash, block.transactions.length],
...data.blocks.slice(0, MAX_BLOCKS - 1),
]
const { transactions: txs } = block
if (txs.length) {
const newTxs: Transaction[] = txs.map(tx => [
tx.hash,
JSON.stringify(tx.inputs.map(input => input.previousOutput.cell)),
JSON.stringify(tx.outputs.map(output => `${output.lock.args[0]}-${output.capacity}`)),
])
data.transactions = [...newTxs, ...data.transactions].slice(0, MAX_TRANSACTIONS)
}
updateBlocks(data.blocks)
updateTransactions(data.transactions)
}

const stop = () => {
clearInterval(listener)
MainScreen.destroy()
}

const start = () => {
listener = setInterval(async () => {
try {
const currentTipNumber = data.chainInfo.tipNumber
await fetchAndUpdateChainInfo()
if (+data.chainInfo.tipNumber > +currentTipNumber) {
fetchAndUpdateBlock()
}
} catch (err) {
stop()
console.error(err.message)
}
}, 500)
}
return {
start,
stop,
}
}

export default dashboard
14 changes: 13 additions & 1 deletion packages/ckb-cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import repl from 'repl'
import Core from '@nervosnetwork/ckb-sdk-core'
import commander from 'commander'
import { prompt } from 'inquirer'
// import questions from './rpc' // { rpcMethods }
import dashboard from './dashboard'

const RPC_URL = 'http://localhost:8114'

Expand Down Expand Up @@ -70,8 +70,20 @@ commander
const core = new Core(remote)
replServer.context.core = core
replServer.context.rpc = {}
replServer.context.dashboard = () => dashboard(core)
Object.keys(core.rpc).forEach(key => {
replServer.context.rpc[key] = (core.rpc as any)[key]
})
})

commander
.command('dashboard [remote]')
.alias('d')
.description('dashboard')
.action(async (remote = RPC_URL) => {
console.info('boosting the dashboard')
const core = new Core(remote)
dashboard(core).start()
})

commander.parse(process.argv)

0 comments on commit 9accdeb

Please sign in to comment.