Skip to content

Commit

Permalink
feat: Optimize light client external node detection (#3165)
Browse files Browse the repository at this point in the history
  • Loading branch information
yanguoyu authored May 30, 2024
1 parent 429e880 commit 5d4afb1
Show file tree
Hide file tree
Showing 14 changed files with 147 additions and 69 deletions.
76 changes: 47 additions & 29 deletions packages/neuron-ui/src/containers/Main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const MainContent = () => {
[network, networks]
)

const isLightClientNetwork = network?.type === 2

useSyncChainData({
chainURL: network?.remote ?? '',
dispatch,
Expand Down Expand Up @@ -98,6 +100,48 @@ const MainContent = () => {
}
}, [])

const dialogProps = (function getDialogProps() {
if (isLightClientNetwork) {
return {
onConfirm: onCloseSwitchNetwork,
children: t('main.external-node-detected-dialog.external-node-is-light'),
}
}
if (sameUrlNetworks.length) {
return {
onConfirm: onSwitchNetwork,
children: (
<>
<span className={styles.chooseNetworkTip}>
{t('main.external-node-detected-dialog.body-tips-with-network')}
</span>
<div className={styles.networks}>
<RadioGroup
onChange={onChangeSelected}
options={sameUrlNetworks.map(v => ({
value: v.id,
label: `${v.name} (${v.remote})`,
}))}
inputIdPrefix="main-switch"
/>
</div>
<div className={styles.addNetwork}>
<Button type="text" onClick={onOpenEditorDialog}>
<AddSimple />
{t('main.external-node-detected-dialog.add-network')}
</Button>
</div>
</>
),
}
}
return {
onConfirm: onOpenEditorDialog,
confirmText: t('main.external-node-detected-dialog.add-network'),
children: t('main.external-node-detected-dialog.body-tips-without-network'),
}
})()

return (
<div onContextMenu={onContextMenu}>
<Outlet />
Expand All @@ -124,39 +168,13 @@ const MainContent = () => {
<Dialog
show={isSwitchNetworkShow}
onCancel={onCloseSwitchNetwork}
onConfirm={sameUrlNetworks.length ? onSwitchNetwork : onOpenEditorDialog}
confirmText={sameUrlNetworks.length ? undefined : t('main.external-node-detected-dialog.add-network')}
onConfirm={dialogProps.onConfirm}
confirmText={dialogProps.confirmText}
cancelText={t('main.external-node-detected-dialog.ignore-external-node')}
title={t('main.external-node-detected-dialog.title')}
className={styles.networkDialog}
>
{sameUrlNetworks.length ? (
<span className={styles.chooseNetworkTip}>
{t('main.external-node-detected-dialog.body-tips-with-network')}
</span>
) : (
t('main.external-node-detected-dialog.body-tips-without-network')
)}
{sameUrlNetworks.length ? (
<>
<div className={styles.networks}>
<RadioGroup
onChange={onChangeSelected}
options={sameUrlNetworks.map(v => ({
value: v.id,
label: `${v.name} (${v.remote})`,
}))}
inputIdPrefix="main-switch"
/>
</div>
<div className={styles.addNetwork}>
<Button type="text" onClick={onOpenEditorDialog}>
<AddSimple />
{t('main.external-node-detected-dialog.add-network')}
</Button>
</div>
</>
) : null}
{dialogProps.children}
</Dialog>
{showEditorDialog ? (
<NetworkEditorDialog
Expand Down
3 changes: 2 additions & 1 deletion packages/neuron-ui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,8 @@
"body-tips-without-network": "\"Internal Node\" is reserved for the built-in CKB node, please add a new network option for the external node.",
"body-tips-with-network": "\"Internal Node\" is reserved for the built-in CKB node, please select from the following network options or add a new one for the external node.",
"add-network": "Add Network",
"ignore-external-node": "Ignore external node"
"ignore-external-node": "Ignore external node",
"external-node-is-light": "Neuron does not support external light client nodes due to different security assumptions for light clients. Would you like to connect to the \"Light Client\" ?"
},
"no-disk-space-dialog": {
"tip": "Due to insufficient disk space, synchronization has been stopped. <br /> Please allocate more disk space or migrate the data to another disk.",
Expand Down
3 changes: 2 additions & 1 deletion packages/neuron-ui/src/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -1192,7 +1192,8 @@
"body-tips-without-network": "\"Internal Node\" está reservado para el nodo CKB incorporado, agregue una nueva opción de red para el nodo externo.",
"body-tips-with-network": "\"Internal Node\" está reservado para el nodo CKB incorporado, seleccione una de las siguientes opciones de red o agregue una nueva para el nodo externo.",
"add-network": "Agregar red",
"ignore-external-node": "Ignorar nodo externo"
"ignore-external-node": "Ignorar nodo externo",
"external-node-is-light": "Debido a las diferentes suposiciones de seguridad del cliente ligero, Neuron no admite nodos de cliente ligero externos. ¿Desea conectarse a un \"Light Client\" ?"
},
"no-disk-space-dialog": {
"tip": "La sincronización se ha detenido debido a falta de espacio en disco. <br /> Asigne más espacio en disco o migre los datos a otro disco.",
Expand Down
3 changes: 2 additions & 1 deletion packages/neuron-ui/src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1202,7 +1202,8 @@
"body-tips-without-network": "\"Noeud interne\" est réservé au noeud CKB intégré, veuillez ajouter une nouvelle option réseau pour le noeud externe.",
"body-tips-with-network": "\"Noeud interne\" est réservé au noeud CKB intégré, veuillez sélectionner parmi les options réseau suivantes ou en ajouter une nouvelle pour le noeud externe.",
"add-network": "Ajouter un réseau",
"ignore-external-node": "Ignorer le noeud externe"
"ignore-external-node": "Ignorer le noeud externe",
"external-node-is-light": "En raison de différentes hypothèses de sécurité du client léger, Neuron ne prend pas en charge les nœuds clients légers externes. Voulez-vous vous connecter à un \"Light Client\" ?"
},
"no-disk-space-dialog": {
"tip": "En raison d'un espace disque insuffisant, la synchronisation a été interrompue. <br /> Veuillez allouer plus d'espace disque ou migrer les données vers un autre disque.",
Expand Down
3 changes: 2 additions & 1 deletion packages/neuron-ui/src/locales/zh-tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -1205,7 +1205,8 @@
"body-tips-without-network": "\"Internal Node\" 僅用於連接內置節點,請添加新的網絡選項以連接外部節點。",
"body-tips-with-network": "\"Internal Node\" 僅用於連接內置節點,請選擇以下網絡選項或添加新的網絡選項以連接外部節點。",
"add-network": "添加網絡",
"ignore-external-node": "忽略外部節點"
"ignore-external-node": "忽略外部節點",
"external-node-is-light": "由於輕客戶端的安全假設不同,Neuron 不支持外部輕客戶端節點。您想連接到 \"Light Client\" 嗎?"
},
"no-disk-space-dialog": {
"tip": "由於磁盤空間不足,同步已停止。 <br /> 請分配更多磁盤空間或將數據遷移到其他磁盤。",
Expand Down
3 changes: 2 additions & 1 deletion packages/neuron-ui/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -1204,7 +1204,8 @@
"body-tips-without-network": "\"Internal Node\" 仅用于连接内置节点,请添加新的网络选项以连接外部节点。",
"body-tips-with-network": "\"Internal Node\" 仅用于连接内置节点,请选择以下网络选项或添加新的网络选项以连接外部节点。",
"add-network": "添加网络",
"ignore-external-node": "忽略外部节点"
"ignore-external-node": "忽略外部节点",
"external-node-is-light": "由于轻客户端的安全假设不同,Neuron 不支持外部轻客户端节点。您想连接到 \"Light Client\" 吗?"
},
"no-disk-space-dialog": {
"tip": "由于磁盘空间不足,同步已停止。 <br /> 请分配更多磁盘空间或将数据迁移到其他磁盘。",
Expand Down
3 changes: 1 addition & 2 deletions packages/neuron-wallet/src/block-sync-renderer/sync/queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { ShouldInChildProcess } from '../../exceptions'
import { AppendScript, BlockTips, Synchronizer } from './synchronizer'
import LightSynchronizer from './light-synchronizer'
import { generateRPC } from '../../utils/ckb-rpc'
import { BUNDLED_LIGHT_CKB_URL } from '../../utils/const'
import { NetworkType } from '../../models/network'
import WalletService from '../../services/wallets'

Expand Down Expand Up @@ -65,7 +64,7 @@ export default class Queue {
start = async () => {
logger.info('Queue:\tstart')
try {
if (this.#url === BUNDLED_LIGHT_CKB_URL) {
if (this.#nodeType === NetworkType.Light) {
this.#indexerConnector = new LightSynchronizer(this.#addresses, this.#url)
} else {
this.#indexerConnector = new FullSynchronizer(this.#addresses, this.#url, this.#nodeType)
Expand Down
8 changes: 6 additions & 2 deletions packages/neuron-wallet/src/services/ckb-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,12 @@ export const startCkbNode = async () => {
listenPort = await getUsablePort(rpcPort >= listenPort ? rpcPort + 1 : listenPort)

updateToml(path.join(SettingsService.getInstance().getNodeDataPath(), 'ckb.toml'), {
rpc: `listen_address = "127.0.0.1:${rpcPort}"`,
network: `listen_addresses = ["/ip4/0.0.0.0/tcp/${listenPort}"]`,
rpc: {
listen_address: `"127.0.0.1:${rpcPort}"`,
},
network: {
listen_addresses: `["/ip4/0.0.0.0/tcp/${listenPort}"]`,
},
})
const options = ['run', '-C', SettingsService.getInstance().getNodeDataPath(), '--indexer']
const stdio: (StdioNull | StdioPipe)[] = ['ignore', 'pipe', 'pipe']
Expand Down
16 changes: 13 additions & 3 deletions packages/neuron-wallet/src/services/light-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export class CKBLightRunner extends NodeRunner {
protected binaryName: string = 'ckb-light-client'
protected logStream: Map<string, fs.WriteStream> = new Map()
protected _port: number = 9000
protected listenPort: number = 8118

static getInstance(): CKBLightRunner {
if (!CKBLightRunner.instance) {
Expand Down Expand Up @@ -117,13 +118,22 @@ export class CKBLightRunner extends NodeRunner {

async updateConfig() {
const usablePort = await getUsablePort(this._port)
const listenPort = await getUsablePort(usablePort >= this._port ? this.listenPort + 1 : this.listenPort)
this._port = usablePort
this.listenPort = listenPort
const storePath = path.join(SettingsService.getInstance().getNodeDataPath(), './store')
const networkPath = path.join(SettingsService.getInstance().getNodeDataPath(), './network')
updateToml(this.configFile, {
store: `path = "${this.platform() === 'win' ? storePath.replace(/\\/g, '\\\\') : storePath}"`,
network: `path = "${this.platform() === 'win' ? networkPath.replace(/\\/g, '\\\\') : networkPath}"`,
rpc: `listen_address = "127.0.0.1:${usablePort}"`,
store: {
path: `"${this.platform() === 'win' ? storePath.replace(/\\/g, '\\\\') : storePath}"`,
},
network: {
path: `"${this.platform() === 'win' ? networkPath.replace(/\\/g, '\\\\') : networkPath}"`,
listen_addresses: `["/ip4/0.0.0.0/tcp/${listenPort}"]`,
},
rpc: {
listen_address: `"127.0.0.1:${usablePort}"`,
},
})
}

Expand Down
18 changes: 14 additions & 4 deletions packages/neuron-wallet/src/utils/toml.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import fs from 'fs'
import path from 'path'

export function updateToml(filePath: string, updateValue: Record<string, string>, newFilePath?: string) {
export function updateToml(
filePath: string,
updateValue: Record<string, Record<string, string>>,
newFilePath?: string
) {
const values = fs.readFileSync(filePath).toString().split('\n')
let field: string | undefined = undefined
const newValues = values.map(v => {
Expand All @@ -14,9 +18,15 @@ export function updateToml(filePath: string, updateValue: Record<string, string>
return v
}
if (field && updateValue[field]) {
const newLine = updateValue[field]
field = undefined
return newLine
const equalIndex = v.indexOf('=')
if (equalIndex !== -1) {
const stringBeforeEqual = v.slice(0, equalIndex)
const key = stringBeforeEqual.trim()
if (updateValue[field][key]) {
const newLine = `${stringBeforeEqual}= ${updateValue[field][key]}`
return newLine
}
}
}
return v
})
Expand Down
16 changes: 12 additions & 4 deletions packages/neuron-wallet/tests/services/ckb-runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,12 @@ describe('ckb runner', () => {
expect(getNodeUrl()).toBe('http://127.0.0.1:8114')
expect(getUsablePortMock).toHaveBeenLastCalledWith(8115)
expect(updateTomlMock).toBeCalledWith(path.join('/chains/mainnet', 'ckb.toml'), {
rpc: `listen_address = "127.0.0.1:8114"`,
network: `listen_addresses = ["/ip4/0.0.0.0/tcp/8115"]`,
rpc: {
listen_address: `"127.0.0.1:8114"`,
},
network: {
listen_addresses: `["/ip4/0.0.0.0/tcp/8115"]`,
},
})
const promise = stopCkbNode()
stubbedCkb.emit('close')
Expand All @@ -270,8 +274,12 @@ describe('ckb runner', () => {
expect(getNodeUrl()).toBe('http://127.0.0.1:8115')
expect(getUsablePortMock).toHaveBeenLastCalledWith(8116)
expect(updateTomlMock).toBeCalledWith(path.join('/chains/mainnet', 'ckb.toml'), {
rpc: `listen_address = "127.0.0.1:8115"`,
network: `listen_addresses = ["/ip4/0.0.0.0/tcp/8116"]`,
rpc: {
listen_address: `"127.0.0.1:8115"`,
},
network: {
listen_addresses: `["/ip4/0.0.0.0/tcp/8116"]`,
},
})
let promise = stopCkbNode()
stubbedCkb.emit('close')
Expand Down
30 changes: 22 additions & 8 deletions packages/neuron-wallet/tests/services/light-runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,32 +337,46 @@ describe('test light runner', () => {

describe('test update config', () => {
it('port is used', async () => {
getUsablePortMock.mockResolvedValueOnce(9001)
getUsablePortMock.mockResolvedValueOnce(9001).mockResolvedValueOnce(8119)
lightDataPathMock.mockReturnValue('lightDataPath')
resolveMock.mockImplementation((...v: string[]) => v.join(''))
joinMock.mockImplementation((...v: string[]) => v.join(''))
await CKBLightRunner.getInstance().updateConfig()
expect(CKBLightRunner.getInstance().port).toEqual(9001)
expect(updateTomlMock).toHaveBeenCalledWith('lightDataPath./ckb_light.toml', {
store: `path = "lightDataPath./store"`,
network: `path = "lightDataPath./network"`,
rpc: `listen_address = "127.0.0.1:9001"`,
store: {
path: `"lightDataPath./store"`,
},
network: {
listen_addresses: '["/ip4/0.0.0.0/tcp/8119"]',
path: `"lightDataPath./network"`,
},
rpc: {
listen_address: `"127.0.0.1:9001"`,
},
})
//reset port
getUsablePortMock.mockResolvedValueOnce(9000)
await CKBLightRunner.getInstance().updateConfig()
})
it('port is not used', async () => {
getUsablePortMock.mockResolvedValueOnce(9000)
getUsablePortMock.mockResolvedValueOnce(9000).mockResolvedValueOnce(8118)
lightDataPathMock.mockReturnValue('lightDataPath')
resolveMock.mockImplementation((...v: string[]) => v.join(''))
joinMock.mockImplementation((...v: string[]) => v.join(''))
await CKBLightRunner.getInstance().updateConfig()
expect(CKBLightRunner.getInstance().port).toEqual(9000)
expect(updateTomlMock).toHaveBeenCalledWith('lightDataPath./ckb_light.toml', {
store: `path = "lightDataPath./store"`,
network: `path = "lightDataPath./network"`,
rpc: `listen_address = "127.0.0.1:9000"`,
store: {
path: `"lightDataPath./store"`,
},
network: {
listen_addresses: '["/ip4/0.0.0.0/tcp/8118"]',
path: `"lightDataPath./network"`,
},
rpc: {
listen_address: `"127.0.0.1:9000"`,
},
})
})
})
Expand Down
1 change: 1 addition & 0 deletions packages/neuron-wallet/tests/utils/toml/test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ log_to_stdout = true

[network]
network = 127
listen_addresses = ["/ip4/0.0.0.0/tcp/8115"]
### Specify the public and routable network addresses
# public_addresses = []

Expand Down
Loading

2 comments on commit 5d4afb1

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Packaging for test is done in 9300460542

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Packaging for test is done in 9300460407

Please sign in to comment.