diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index d1d21db08..126b54494 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -103,6 +103,11 @@ jobs: env: CI: false + # Inject LOG_ENCRYPTION_PUBLIC_KEY to encrypt sensitive log + - name: Inject environment variables + run: | + echo "LOG_ENCRYPTION_PUBLIC_KEY=${{ secrets.LOG_ENCRYPTION_PUBLIC_KEY }}" >> packages/neuron-wallet/.env + - name: Package for MacOS if: matrix.os == 'macos-latest' run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 14a324aac..0f4894734 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,45 @@ +# 0.117.0 (2024-08-12) + +### CKB Node & Light Client + +- [CKB@v0.117.0](https://github.com/nervosnetwork/ckb/releases/tag/v0.117.0) was released on Jul. 29th, 2024. This version of CKB node is now bundled and preconfigured in Neuron. +- [CKB Light Client@v0.3.7](https://github.com/nervosnetwork/ckb-light-client/releases/tag/v0.3.7) was released on Apr. 13th, 2024. This version of CKB Light Client is now bundled and preconfigured in Neuron + +### Assumed valid target + +Block before `0xca44ae8f7bc12ba8eab3224cbe3156c913e2284693e36dc1d01e4d30f362f3c2`(at height `13,705,152`) will be skipped in validation.(https://github.com/nervosnetwork/neuron/pull/3227) + +--- + +[![Neuron@v0.117.0](https://github.com/user-attachments/assets/7d2eba67-e33e-4fca-a714-7ba1709d8bd3)](https://youtu.be/zf78Y094m60) + +YouTube: https://youtu.be/zf78Y094m60 + +--- + +## New features + +- #3206: Support XUDT asset management.(@yanguoyu) +- #3207: Support connecting to an external light client.(@devchenyan) +- #3167: Support cells consolidation.(@devchenyan) +- #3199: Validate pending transactions periodically.(@devchenyan) +- #3200: Optimize the process of generating a wallet.(@devchenyan) +- #3176: Support setting start block numbers of multisig addresses.(@yanguoyu) +- #3160: Optimize synchronization in light client mode for multiple wallets.(@yanguoyu) +- #3169: Be compatible with multisig transaction JSON file exported from CKB CLI.(@devchenyan) +- #3197: Support resetting pin code for window lock.(@yanguoyu) +- #3194: Add a tip for multisig addresses.(@yanguoyu) + +## Bug fixes + +- #3195: Fix the synchronization status check.(@yanguoyu) + +## New Contributors + +- @tcpdumppy made their first contribution in https://github.com/nervosnetwork/neuron/pull/3182 + +**Full Changelog**: https://github.com/nervosnetwork/neuron/compare/v0.116.2...v0.117.0 + # 0.116.2 (2024-05-29) ### CKB Node & Light Client @@ -57,18 +99,18 @@ YouTube: https://youtu.be/QXv8by2C8zU ## New features -- 3134: Support 'replace-by-fee' nervos dao transactions and sudt transactions.(@devchenyan) -- 3144: Reduce size of light client log in debug information and reveal start-block-number in log.(@yanguoyu) -- 3064: Support locking window by pin code.(@yanguoyu) -- 3131: Add detailed result for nervos dao transaction.(@devchenyan) +- #3134: Support 'replace-by-fee' nervos dao transactions and sudt transactions.(@devchenyan) +- #3144: Reduce size of light client log in debug information and reveal start-block-number in log.(@yanguoyu) +- #3064: Support locking window by pin code.(@yanguoyu) +- #3131: Add detailed result for nervos dao transaction.(@devchenyan) ## Bug fixes -- 3121: Locate the first transaction on Explorer directly when users want to set the start-block-number for light client.(@yanguoyu) -- 3101: Show migration instruction properly.(@devchenyan) -- 3062: Migrate legacy ACP to active ACP account(@yanguoyu) -- 3141: Fix some issues about light client synchronizaiton.(@yanguoyu) -- 3120: Remove all sync data when start-block-number is set less than before.(@yanguoyu) +- #3121: Locate the first transaction on Explorer directly when users want to set the start-block-number for light client.(@yanguoyu) +- #3101: Show migration instruction properly.(@devchenyan) +- #3062: Migrate legacy ACP to active ACP account(@yanguoyu) +- #3141: Fix some issues about light client synchronizaiton.(@yanguoyu) +- #3120: Remove all sync data when start-block-number is set less than before.(@yanguoyu) **Full Changelog**: https://github.com/nervosnetwork/neuron/compare/v0.114.3...v0.116.0 diff --git a/compatible.json b/compatible.json index d0e70db79..125c5a0fc 100644 --- a/compatible.json +++ b/compatible.json @@ -121,6 +121,23 @@ "0.3", "0.2" ] + }, + "0.117": { + "full": [ + "0.117", + "0.116", + "0.115", + "0.114", + "0.113", + "0.112", + "0.111", + "0.110", + "0.109" + ], + "light": [ + "0.3", + "0.2" + ] } } } diff --git a/lerna.json b/lerna.json index 42a9f9ef2..6e0105574 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.116.2", + "version": "0.117.0", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } diff --git a/package.json b/package.json index e3cbe767f..460937a77 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "neuron", "productName": "Neuron", "description": "CKB Neuron Wallet", - "version": "0.116.2", + "version": "0.117.0", "private": true, "author": { "name": "Nervos Core Dev", diff --git a/packages/neuron-ui/package.json b/packages/neuron-ui/package.json index 633abb7d5..9f0ef450d 100644 --- a/packages/neuron-ui/package.json +++ b/packages/neuron-ui/package.json @@ -1,6 +1,6 @@ { "name": "neuron-ui", - "version": "0.116.2", + "version": "0.117.0", "private": true, "author": { "name": "Nervos Core Dev", diff --git a/packages/neuron-wallet/.env b/packages/neuron-wallet/.env index b5240ac1d..7d71ecbdd 100644 --- a/packages/neuron-wallet/.env +++ b/packages/neuron-wallet/.env @@ -117,6 +117,6 @@ DAO_CODE_HASH=0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e MULTISIG_CODE_HASH=0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8 # CKB NODE OPTIONS -CKB_NODE_ASSUME_VALID_TARGET='0x6dd077b407d019a0bce0cbad8c34e69a524ae4b2599b9feda2c7491f3559d32c' -CKB_NODE_ASSUME_VALID_TARGET_BLOCK_NUMBER=13007704 -CKB_NODE_DATA_SIZE=56 +CKB_NODE_ASSUME_VALID_TARGET='0xca44ae8f7bc12ba8eab3224cbe3156c913e2284693e36dc1d01e4d30f362f3c2' +CKB_NODE_ASSUME_VALID_TARGET_BLOCK_NUMBER=13705152 +CKB_NODE_DATA_SIZE=58 diff --git a/packages/neuron-wallet/package.json b/packages/neuron-wallet/package.json index 52d4387f3..dcd3c40b6 100644 --- a/packages/neuron-wallet/package.json +++ b/packages/neuron-wallet/package.json @@ -3,7 +3,7 @@ "productName": "Neuron", "description": "CKB Neuron Wallet", "homepage": "https://www.nervos.org/", - "version": "0.116.2", + "version": "0.117.0", "private": true, "author": { "name": "Nervos Core Dev", @@ -92,7 +92,7 @@ "electron-builder": "24.9.1", "electron-devtools-installer": "3.2.0", "jest-when": "3.6.0", - "neuron-ui": "0.116.2", + "neuron-ui": "0.117.0", "typescript": "5.3.3" } } diff --git a/packages/neuron-wallet/src/controllers/export-debug.ts b/packages/neuron-wallet/src/controllers/export-debug.ts index c393984bd..0504fe8ce 100644 --- a/packages/neuron-wallet/src/controllers/export-debug.ts +++ b/packages/neuron-wallet/src/controllers/export-debug.ts @@ -14,6 +14,7 @@ import { generateRPC } from '../utils/ckb-rpc' import { CKBLightRunner } from '../services/light-runner' import { LIGHT_CLIENT_MAINNET, LIGHT_CLIENT_TESTNET } from '../utils/const' import WalletsService from '../services/wallets' +import LogEncryption from '../services/log-encryption' export default class ExportDebugController { #I18N_PATH = 'export-debug-info' @@ -158,7 +159,12 @@ export default class ExportDebugController { csv += row } const csvFileName = 'hd_public_key_info.csv' - this.archive.append(csv, { name: csvFileName }) + const encryption = LogEncryption.getInstance() + if (encryption.isEnabled) { + this.archive.append(encryption.encrypt(csv), { name: `encrypted_${csvFileName}` }) + } else { + this.archive.append(csv, { name: csvFileName }) + } } catch (error) { logger.error(`Export Debug:\t export public key info error: ${error}`) } diff --git a/packages/neuron-wallet/src/services/log-encryption.ts b/packages/neuron-wallet/src/services/log-encryption.ts index 2ae29e9e1..31c024d19 100644 --- a/packages/neuron-wallet/src/services/log-encryption.ts +++ b/packages/neuron-wallet/src/services/log-encryption.ts @@ -17,12 +17,16 @@ export default class LogEncryption { */ private readonly adminPublicKey: string + public get isEnabled(): boolean { + return !!this.adminPublicKey + } + /** * * @param adminPublicKey a PEM-formatted RSA public key */ constructor(adminPublicKey: string) { - this.adminPublicKey = adminPublicKey + this.adminPublicKey = adminPublicKey.replace(/\\n/g, '\n') } /** diff --git a/packages/neuron-wallet/tests/controllers/export-debug.test.ts b/packages/neuron-wallet/tests/controllers/export-debug.test.ts index 9e9f4b797..f346649c3 100644 --- a/packages/neuron-wallet/tests/controllers/export-debug.test.ts +++ b/packages/neuron-wallet/tests/controllers/export-debug.test.ts @@ -100,6 +100,19 @@ jest.mock('../../src/services/wallets', () => { } }) +const encryptionMock = { + isEnabled: false, + encrypt: jest.fn(v => v), +} + +jest.mock('../../src/services/log-encryption.ts', () => { + return { + getInstance() { + return encryptionMock + }, + } +}) + import { dialog } from 'electron' import logger from '../../src/utils/logger' import ExportDebugController from '../../src/controllers/export-debug' @@ -148,7 +161,7 @@ describe('Test ExportDebugController', () => { }) it('should call required methods', () => { - expect.assertions(9) + expect.assertions(10) expect(showSaveDialogMock).toHaveBeenCalled() expect(addBundledCKBLogMock).toHaveBeenCalled() @@ -159,6 +172,7 @@ describe('Test ExportDebugController', () => { expect(showMessageBoxMock).toHaveBeenCalled() expect(showErrorBoxMock).not.toHaveBeenCalled() expect(logger.error).not.toHaveBeenCalled() + expect(encryptionMock.encrypt).not.toHaveBeenCalled() const csv = ['index,addressType,addressIndex,publicKeyInBlake160\n', '0,0,0,hash1\n', '1,1,1,hash2\n'].join('') expect(archiveAppendMock).toHaveBeenCalledWith(csv, expect.objectContaining({ name: 'hd_public_key_info.csv' })) @@ -200,4 +214,17 @@ describe('Test ExportDebugController', () => { expect(showErrorBoxMock).toHaveBeenCalled() }) }) + + describe('when encryption is enabled', () => { + beforeEach(() => { + encryptionMock.isEnabled = true + showSaveDialogMock.mockResolvedValue({ canceled: false, filePath: 'mock_path' }) + return exportDebugController.export() + }) + + it('encrypt should be called', () => { + expect.assertions(1) + expect(encryptionMock.encrypt).toHaveBeenCalled() + }) + }) }) diff --git a/scripts/admin/decrypt-log/run.ts b/scripts/admin/decrypt-log/run.ts index 632420929..ccf266bcc 100644 --- a/scripts/admin/decrypt-log/run.ts +++ b/scripts/admin/decrypt-log/run.ts @@ -1,4 +1,64 @@ -import { LogDecryption } from "../../../packages/neuron-wallet/src/services/log-encryption"; +import { createDecipheriv, privateDecrypt } from "node:crypto"; + +export const DEFAULT_ALGORITHM = "aes-256-cbc"; + +export class LogDecryption { + private readonly adminPrivateKey: string; + + constructor(adminPrivateKey: string) { + this.adminPrivateKey = adminPrivateKey; + } + + decrypt(encryptedMessage: string): string { + const { iv, key, content } = parseMessage(encryptedMessage); + + const decipher = createDecipheriv( + DEFAULT_ALGORITHM, + privateDecrypt(this.adminPrivateKey, Buffer.from(key, "base64")), + Buffer.from(iv, "base64") + ); + + return Buffer.concat([decipher.update(content, "base64"), decipher.final()]).toString("utf-8"); + } +} + +/** + * Parse a message into a JSON + * + * Input: + * ``` + * [key1:value2] [key2:value2] remain content + * ``` + * Output: + * ```json + * { + * "key1": "value1", + * "key2": "value2", + * "content": "remain content" + * } + * ``` + * @param message + */ +function parseMessage(message: string) { + const result: Record = {}; + const regex = /\[([^\]:]+):([^\]]+)]/g; + let match; + let lastIndex = 0; + + while ((match = regex.exec(message)) !== null) { + const [, key, value] = match; + result[key.trim()] = value.trim(); + lastIndex = regex.lastIndex; + } + + // Extract remaining content after the last bracket + const remainingContent = message.slice(lastIndex).trim(); + if (remainingContent) { + result.content = remainingContent; + } + + return result; +} const ADMIN_PRIVATE_KEY = process.env .ADMIN_PRIVATE_KEY!.split(/\r?\n/)