diff --git a/CHANGELOG.md b/CHANGELOG.md
index 944a2d5f33..69eb28eeb7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,38 @@
+## [0.25.2](https://github.com/nervosnetwork/neuron/compare/v0.25.1...v0.25.2) (2019-11-29)
+
+
+### Bug Fixes
+
+* also clean lock/dao info in renderer process ([a0b2470](https://github.com/nervosnetwork/neuron/commit/a0b2470))
+* Fix the problem that balance not right if switch network from default network ([0f763a5](https://github.com/nervosnetwork/neuron/commit/0f763a5))
+* remove bufferTime for address created event ([9b0a077](https://github.com/nervosnetwork/neuron/commit/9b0a077))
+* the missing txs ([ed557b6](https://github.com/nervosnetwork/neuron/commit/ed557b6))
+* **neuron-ui:** remove www from docs.nervos.org ([3fc8154](https://github.com/nervosnetwork/neuron/commit/3fc8154))
+* balance not update after sent tx ([65e51dd](https://github.com/nervosnetwork/neuron/commit/65e51dd))
+* **neuron-ui:** show 0 if withdraw rpc returns errors ([b714376](https://github.com/nervosnetwork/neuron/commit/b714376))
+* clean lock utils info and dao utils info when switch network ([60ec486](https://github.com/nervosnetwork/neuron/commit/60ec486))
+* initialize NetworksService in renderer process ([73f1bf0](https://github.com/nervosnetwork/neuron/commit/73f1bf0))
+* network switch event broadcast twice ([f1b0f72](https://github.com/nervosnetwork/neuron/commit/f1b0f72))
+* pending in windows when network off ([67dcb79](https://github.com/nervosnetwork/neuron/commit/67dcb79))
+* sign witnesses test ([5000edd](https://github.com/nervosnetwork/neuron/commit/5000edd))
+
+
+### Features
+
+* **neuron-ui:** update the url to nervos dao rfc ([6b68ab6](https://github.com/nervosnetwork/neuron/commit/6b68ab6))
+* Add API for downloading and installing updates ([b8d24ca](https://github.com/nervosnetwork/neuron/commit/b8d24ca))
+* Add app updater subject and state ([423109d](https://github.com/nervosnetwork/neuron/commit/423109d))
+* Adding check update to settings view ([98fe06c](https://github.com/nervosnetwork/neuron/commit/98fe06c))
+* Connect updater events to UI ([b267321](https://github.com/nervosnetwork/neuron/commit/b267321))
+* Delete unused updater translations ([bcafce8](https://github.com/nervosnetwork/neuron/commit/bcafce8))
+* Different stage status of checking updates ([cd82ca4](https://github.com/nervosnetwork/neuron/commit/cd82ca4))
+* Polish updater i18n translations and UI ([af45f0c](https://github.com/nervosnetwork/neuron/commit/af45f0c))
+* Show release notes when there's update available ([8cf9581](https://github.com/nervosnetwork/neuron/commit/8cf9581))
+* Trigger check updates menu item enabling/disabling ([cd8e5d5](https://github.com/nervosnetwork/neuron/commit/cd8e5d5))
+* **neuron-ui:** add copy address and copy tx hash context menus on the tx detail view. ([7d86454](https://github.com/nervosnetwork/neuron/commit/7d86454))
+
+
+
## [0.25.1](https://github.com/nervosnetwork/neuron/compare/v0.25.0...v0.25.1) (2019-11-18)
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 6f62d0933a..e720ce4279 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -16,8 +16,12 @@ pr:
include:
- '*'
-jobs:
- - job: macOS
+stages:
+- stage: unit_tests
+ displayName: Unit Tests
+ jobs:
+ - job: mac
+ displayName: macOS
pool:
vmImage: 'macos-10.14'
strategy:
@@ -37,9 +41,10 @@ jobs:
- script: CI=true yarn test
name: Test
- - job: Linux
+ - job: linux
+ displayName: Linux
pool:
- vmImage: 'ubuntu-16.04'
+ vmImage: 'ubuntu-18.04'
strategy:
matrix:
node_12_x:
@@ -60,7 +65,8 @@ jobs:
CI=true yarn test
name: Test
- - job: Windows
+ - job: win
+ displayName: Windows
pool:
vmImage: 'windows-2019'
strategy:
@@ -84,7 +90,12 @@ jobs:
yarn test
name: Test
+- stage: e2e_tests
+ displayName: Integration Tests
+ dependsOn: []
+ jobs:
- job: Integration
+ displayName: Integration Tests
pool:
vmImage: 'macos-10.14'
steps:
@@ -99,8 +110,12 @@ jobs:
- script: yarn test:e2e
name: Test
- - job: Release
- condition: eq(variables['build.sourceBranch'], 'refs/heads/master')
+- stage: release
+ displayName: Release Binaries
+ condition: eq(variables['build.sourceBranch'], 'refs/heads/master')
+ jobs:
+ - job: release_mac
+ displayName: Release macOS
pool:
vmImage: 'macos-10.14'
steps:
@@ -117,19 +132,57 @@ jobs:
displayName: 'Download macOS Signing Certificate'
inputs:
secureFile: Neuron_mac.p12
+ - script: yarn release mac
+ name: Release
+ displayName: 'Sign and Release'
+ env:
+ CSC_LINK: $(macSiginingCertificate.secureFilePath)
+ CSC_KEY_PASSWORD: $(macSiginingCertificatePassword)
+ APPLE_ID: $(appleId)
+ APPLE_ID_PASSWORD: $(appleIdPassword)
+ GH_TOKEN: $(ghToken)
+
+ - job: release_linux
+ displayName: Release Linux
+ pool:
+ vmImage: 'ubuntu-18.04'
+ steps:
+ - task: NodeTool@0
+ inputs:
+ versionSpec: 12.x
+ displayName: 'Install Node.js'
+ - script: |
+ yarn global add lerna
+ yarn bootstrap
+ name: Bootstrap
+ - script: yarn release linux
+ name: Release
+ displayName: 'Sign and Release'
+ env:
+ GH_TOKEN: $(ghToken)
+
+ - job: release_win
+ displayName: Release Windows
+ pool:
+ vmImage: 'macos-10.14'
+ steps:
+ - task: NodeTool@0
+ inputs:
+ versionSpec: 12.x
+ displayName: 'Install Node.js'
+ - script: |
+ yarn global add lerna
+ yarn bootstrap
+ name: Bootstrap
- task: DownloadSecureFile@1
name: winSiginingCertificate
displayName: 'Download Windows Signing Certificate'
inputs:
secureFile: Neuron_win.p12
- - script: yarn release
+ - script: yarn release win
name: Release
displayName: 'Sign and Release'
env:
- CSC_LINK: $(macSiginingCertificate.secureFilePath)
- CSC_KEY_PASSWORD: $(macSiginingCertificatePassword)
WIN_CSC_LINK: $(winSiginingCertificate.secureFilePath)
WIN_CSC_KEY_PASSWORD: $(winSiginingCertificatePassword)
- APPLE_ID: $(appleId)
- APPLE_ID_PASSWORD: $(appleIdPassword)
- GH_TOKEN: $(ghToken)
+ GH_TOKEN: $(ghToken)
\ No newline at end of file
diff --git a/lerna.json b/lerna.json
index 91991a3759..7079de735e 100644
--- a/lerna.json
+++ b/lerna.json
@@ -2,7 +2,7 @@
"packages": [
"packages/*"
],
- "version": "0.25.1",
+ "version": "0.25.2",
"npmClient": "yarn",
"useWorkspaces": true
}
diff --git a/package.json b/package.json
index 1b7b788329..c199379971 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "neuron",
"productName": "Neuron",
"description": "CKB Neuron Wallet",
- "version": "0.25.1",
+ "version": "0.25.2",
"private": true,
"author": {
"name": "Nervos Core Dev",
diff --git a/packages/neuron-ui/package.json b/packages/neuron-ui/package.json
index 074933c3a6..c70162ab30 100644
--- a/packages/neuron-ui/package.json
+++ b/packages/neuron-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "neuron-ui",
- "version": "0.25.1",
+ "version": "0.25.2",
"private": true,
"author": {
"name": "Nervos Core Dev",
@@ -43,7 +43,7 @@
"last 2 chrome versions"
],
"dependencies": {
- "@nervosnetwork/ckb-sdk-core": "0.25.0-alpha.0",
+ "@nervosnetwork/ckb-sdk-core": "0.25.0",
"@uifabric/experiments": "7.16.1",
"@uifabric/styling": "7.6.2",
"canvg": "2.0.0",
@@ -54,7 +54,7 @@
"react": "16.9.0",
"react-dom": "16.9.0",
"react-i18next": "11.0.1",
- "react-router-dom": "5.0.1",
+ "react-router-dom": "5.1.2",
"react-scripts": "3.2.0",
"styled-components": "5.0.0-beta.0"
},
@@ -72,7 +72,7 @@
"@types/node": "12.7.4",
"@types/react": "16.9.2",
"@types/react-dom": "16.9.0",
- "@types/react-router-dom": "4.3.5",
+ "@types/react-router-dom": "5.1.3",
"@types/storybook-react-router": "1.0.0",
"@types/storybook__addon-knobs": "5.0.3",
"@types/storybook__addon-storyshots": "5.1.1",
diff --git a/packages/neuron-ui/src/components/GeneralSetting/index.tsx b/packages/neuron-ui/src/components/GeneralSetting/index.tsx
index f535311820..98c710ecff 100644
--- a/packages/neuron-ui/src/components/GeneralSetting/index.tsx
+++ b/packages/neuron-ui/src/components/GeneralSetting/index.tsx
@@ -1,43 +1,159 @@
-import React, { useCallback, useState } from 'react'
+import React, { useContext, useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Stack, PrimaryButton, Spinner, Text } from 'office-ui-fabric-react'
+import { Stack, PrimaryButton, Spinner, Text, ProgressIndicator } from 'office-ui-fabric-react'
+import { NeuronWalletContext } from 'states/stateProvider'
import { StateWithDispatch } from 'states/stateProvider/reducer'
import { addPopup } from 'states/stateProvider/actionCreators'
-import { clearCellCache } from 'services/remote'
+import { checkForUpdates, downloadUpdate, installUpdate, clearCellCache } from 'services/remote'
+import { releaseNotesStyle } from './style.module.scss'
+
+const UpdateDownloadStatus = ({
+ progress = 0,
+ newVersion = '',
+ releaseNotes = '',
+}: React.PropsWithoutRef<{ progress: number; newVersion: string; releaseNotes: string }>) => {
+ const [t] = useTranslation()
+ const available = newVersion !== '' && progress < 0
+ const downloaded = progress >= 1
+
+ if (available) {
+ const download = () => {
+ downloadUpdate()
+ }
+
+ const releaseNotesHtml = () => {
+ return { __html: releaseNotes }
+ }
+
+ /* eslint-disable react/no-danger */
+
+ return (
+
+
+ {t('updates.updates-found-do-you-want-to-update', { version: newVersion })}
+
+ {t('updates.release-notes')}
+
+
+
+ {t('updates.download-update')}
+
+
+
+ )
+ }
+
+ if (downloaded) {
+ const quitAndInstall = () => {
+ installUpdate()
+ }
+
+ return (
+
+
+ {t('updates.updates-downloaded-about-to-quit-and-install')}
+
+
+
+ {t('updates.quit-and-install')}
+
+
+
+ )
+ }
+
+ return (
+
+ )
+}
const GeneralSetting = ({ dispatch }: React.PropsWithoutRef) => {
const [t] = useTranslation()
- const [clearing, setClearing] = useState(false)
+ const { updater } = useContext(NeuronWalletContext)
+ const [clearingCache, setClearingCache] = useState(false)
+
+ const checkUpdates = useCallback(() => {
+ checkForUpdates()
+ }, [])
const clearCache = useCallback(() => {
- setClearing(true)
+ setClearingCache(true)
setTimeout(() => {
clearCellCache().finally(() => {
addPopup('clear-cache-successfully')(dispatch)
- setClearing(false)
+ setClearingCache(false)
})
}, 100)
}, [dispatch])
return (
-
-
-
- {clearing ? : t('settings.general.clear-cache')}
-
+
+
+
+ {updater.version !== '' || updater.downloadProgress >= 0 ? (
+
+ ) : (
+
+ {updater.checking ? : t('updates.check-updates')}
+
+ )}
+
+
+
+
+
+ {t('settings.general.clear-cache-description')}
+
+
+
+ {clearingCache ? : t('settings.general.clear-cache')}
+
+
-
- {t('settings.general.clear-cache-description')}
-
)
}
diff --git a/packages/neuron-ui/src/components/GeneralSetting/style.module.scss b/packages/neuron-ui/src/components/GeneralSetting/style.module.scss
new file mode 100644
index 0000000000..5a1025cfb1
--- /dev/null
+++ b/packages/neuron-ui/src/components/GeneralSetting/style.module.scss
@@ -0,0 +1,21 @@
+.releaseNotesStyle {
+ overflow: scroll;
+ height: 200px;
+ margin-bottom: 20px;
+ padding: 10px 15px 15px 15px;
+ border: solid 1px #ccc;
+
+ ul {
+ list-style-type: disc;
+ padding-left: 30px;
+
+ li {
+ margin: 5px 0;
+ }
+ }
+
+ a {
+ text-decoration: none;
+ pointer-events: none;
+ }
+}
\ No newline at end of file
diff --git a/packages/neuron-ui/src/components/NervosDAO/WithdrawDialog.tsx b/packages/neuron-ui/src/components/NervosDAO/WithdrawDialog.tsx
index 02eac05dc7..238cd784c6 100644
--- a/packages/neuron-ui/src/components/NervosDAO/WithdrawDialog.tsx
+++ b/packages/neuron-ui/src/components/NervosDAO/WithdrawDialog.tsx
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'
import { Dialog, DialogFooter, DefaultButton, PrimaryButton, DialogType, Text } from 'office-ui-fabric-react'
import { useTranslation } from 'react-i18next'
import { shannonToCKBFormatter, localNumberFormatter } from 'utils/formatters'
-import { ckbCore } from 'services/chain'
+import { calculateDaoMaximumWithdraw, getBlock } from 'services/chain'
import calculateTargetEpochNumber from 'utils/calculateClaimEpochNumber'
import { epochParser } from 'utils/parsers'
@@ -26,8 +26,7 @@ const WithdrawDialog = ({
if (!record) {
return
}
- ckbCore.rpc
- .getBlock(record.blockHash)
+ getBlock(record.blockHash)
.then(b => {
setDepositEpoch(b.header.epoch)
})
@@ -40,14 +39,13 @@ const WithdrawDialog = ({
return
}
- ;(ckbCore.rpc as any)
- .calculateDaoMaximumWithdraw(
- {
- txHash: record.outPoint.txHash,
- index: `0x${BigInt(record.outPoint.index).toString(16)}`,
- },
- tipBlockHash
- )
+ calculateDaoMaximumWithdraw(
+ {
+ txHash: record.outPoint.txHash,
+ index: `0x${BigInt(record.outPoint.index).toString(16)}`,
+ },
+ tipBlockHash
+ )
.then((res: string) => {
setWithdrawValue(res)
})
diff --git a/packages/neuron-ui/src/components/NervosDAO/index.tsx b/packages/neuron-ui/src/components/NervosDAO/index.tsx
index 6f9ab71f59..43ad2b0ac1 100644
--- a/packages/neuron-ui/src/components/NervosDAO/index.tsx
+++ b/packages/neuron-ui/src/components/NervosDAO/index.tsx
@@ -15,7 +15,7 @@ import { MIN_DEPOSIT_AMOUNT, MEDIUM_FEE_RATE, SHANNON_CKB_RATIO, MAX_DECIMAL_DIG
import { verifyAmount } from 'utils/validators'
import { generateDepositTx, generateWithdrawTx, generateClaimTx } from 'services/remote'
-import { ckbCore, getHeaderByNumber } from 'services/chain'
+import { getHeaderByNumber, calculateDaoMaximumWithdraw } from 'services/chain'
import { epochParser } from 'utils/parsers'
import DAORecord from 'components/CustomRows/DAORecordRow'
@@ -246,13 +246,13 @@ const NervosDAO = ({
const formattedDepositOutPoint = depositOutPoint
? {
txHash: depositOutPoint.txHash,
- index: BigInt(depositOutPoint.index),
+ index: `0x${BigInt(depositOutPoint.index).toString(16)}`,
}
: {
txHash: outPoint.txHash,
- index: BigInt(outPoint.index),
+ index: `0x${BigInt(outPoint.index).toString(16)}`,
}
- return (ckbCore.rpc as any).calculateDaoMaximumWithdraw(formattedDepositOutPoint, withdrawBlockHash) as string
+ return calculateDaoMaximumWithdraw(formattedDepositOutPoint, withdrawBlockHash).catch(() => null)
})
)
.then(res => {
diff --git a/packages/neuron-ui/src/components/Transaction/index.tsx b/packages/neuron-ui/src/components/Transaction/index.tsx
index 1c9136507f..e73e0b0536 100644
--- a/packages/neuron-ui/src/components/Transaction/index.tsx
+++ b/packages/neuron-ui/src/components/Transaction/index.tsx
@@ -2,7 +2,14 @@ import React, { useEffect, useState, useMemo, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { Stack, DetailsList, Text, CheckboxVisibility, IColumn, Icon } from 'office-ui-fabric-react'
import { currentWallet as currentWalletCache } from 'services/localCache'
-import { getTransaction, showErrorMessage, getAllNetworks, getCurrentNetworkID, openExternal } from 'services/remote'
+import {
+ getTransaction,
+ showErrorMessage,
+ getAllNetworks,
+ getCurrentNetworkID,
+ openExternal,
+ openContextMenu,
+} from 'services/remote'
import { ckbCore } from 'services/chain'
import { transactionState } from 'states/initStates/chain'
@@ -111,7 +118,7 @@ const Transaction = () => {
name: t('transaction.index'),
minWidth: 60,
maxWidth: 60,
- onRender: (item?: any | State.DetailedOutput) => {
+ onRender: (item?: State.DetailedOutput) => {
if (item) {
return item.outPoint.index
}
@@ -216,7 +223,7 @@ const Transaction = () => {
return
}
getTransaction({ hash, walletID: currentWallet.id })
- .then((res: any) => {
+ .then(res => {
if (res.status) {
setTransaction(res.result)
} else {
@@ -270,6 +277,75 @@ const Transaction = () => {
[t, transaction]
)
+ const onBasicInfoContextMenu = useCallback(
+ (property: { label: string; value: string }, index?: number) => {
+ if (index === 0 && property && property.value) {
+ const menuTemplate = [
+ {
+ label: t('common.copy-tx-hash'),
+ click: () => {
+ window.clipboard.writeText(property.value)
+ },
+ },
+ ]
+ openContextMenu(menuTemplate)
+ }
+ },
+ [t]
+ )
+
+ const onInputContextMenu = useCallback(
+ (input?: State.DetailedInput) => {
+ if (input && input.lock && input.lock.args) {
+ try {
+ const address = ckbCore.utils.bech32Address(input.lock.args, {
+ prefix: addressPrefix,
+ type: ckbCore.utils.AddressType.HashIdx,
+ codeHashOrCodeHashIndex: '0x00',
+ })
+ const menuTemplate = [
+ {
+ label: t('common.copy-address'),
+ click: () => {
+ window.clipboard.writeText(address)
+ },
+ },
+ ]
+ openContextMenu(menuTemplate)
+ } catch (err) {
+ console.error(err)
+ }
+ }
+ },
+ [addressPrefix, t]
+ )
+
+ const onOutputContextMenu = useCallback(
+ (output?: State.DetailedOutput) => {
+ if (output && output.lock && output.lock.args) {
+ try {
+ const address = ckbCore.utils.bech32Address(output.lock.args, {
+ prefix: addressPrefix,
+ type: ckbCore.utils.AddressType.HashIdx,
+ codeHashOrCodeHashIndex: '0x00',
+ })
+ const menuTemplate = [
+ {
+ label: t('common.copy-address'),
+ click: () => {
+ window.clipboard.writeText(address)
+ },
+ },
+ ]
+ openContextMenu(menuTemplate)
+ } catch (err) {
+ console.error(err)
+ }
+ }
+ },
+ [addressPrefix, t]
+ )
+
if (error.code) {
return (
@@ -290,6 +366,7 @@ const Transaction = () => {
checkboxVisibility={CheckboxVisibility.hidden}
compact
isHeaderVisible={false}
+ onItemContextMenu={onBasicInfoContextMenu}
/>
@@ -303,6 +380,7 @@ const Transaction = () => {
items={transaction.inputs}
columns={inputColumns}
checkboxVisibility={CheckboxVisibility.hidden}
+ onItemContextMenu={onInputContextMenu}
compact
isHeaderVisible
/>
@@ -317,6 +395,7 @@ const Transaction = () => {
items={transaction.outputs}
columns={outputColumns}
checkboxVisibility={CheckboxVisibility.hidden}
+ onItemContextMenu={onOutputContextMenu}
compact
isHeaderVisible
/>
diff --git a/packages/neuron-ui/src/containers/Main/hooks.ts b/packages/neuron-ui/src/containers/Main/hooks.ts
index 1bd1a525ee..ada6f494e0 100644
--- a/packages/neuron-ui/src/containers/Main/hooks.ts
+++ b/packages/neuron-ui/src/containers/Main/hooks.ts
@@ -17,6 +17,7 @@ import {
CurrentNetworkID as CurrentNetworkIDSubject,
ConnectionStatus as ConnectionStatusSubject,
SyncedBlockNumber as SyncedBlockNumberSubject,
+ AppUpdater as AppUpdaterSubject,
Command as CommandSubject,
} from 'services/subjects'
import { ckbCore, getBlockchainInfo, getTipHeader } from 'services/chain'
@@ -183,6 +184,13 @@ export const useSubscription = ({
})
})
+ const appUpdaterSubscription = AppUpdaterSubject.subscribe(appUpdaterInfo => {
+ dispatch({
+ type: NeuronWalletActions.UpdateAppUpdaterStatus,
+ payload: appUpdaterInfo,
+ })
+ })
+
const commandSubscription = CommandSubject.subscribe(({ winID, type, payload }: Subject.CommandMetaInfo) => {
if (winID && getWinID() === winID) {
switch (type) {
@@ -223,6 +231,7 @@ export const useSubscription = ({
currentNetworkIDSubscription.unsubscribe()
connectionStatusSubscription.unsubscribe()
syncedBlockNumberSubscription.unsubscribe()
+ appUpdaterSubscription.unsubscribe()
commandSubscription.unsubscribe()
}
}, [walletID, pageNo, pageSize, keywords, isAllowedToFetchList, history, dispatch])
diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json
index 7521caf19c..b2dfe7c53d 100644
--- a/packages/neuron-ui/src/locales/en.json
+++ b/packages/neuron-ui/src/locales/en.json
@@ -178,7 +178,7 @@
"network": "Network"
},
"general": {
- "clear-cache": "Clear cache",
+ "clear-cache": "Clear Cache",
"clear-cache-description": "Clear cache if you encounter data sync or balance display problems. Neuron will rescan block data.",
"show": "Show",
"hide": "Hide"
@@ -241,7 +241,9 @@
"toggle": {
"on": "On",
"off": "Off"
- }
+ },
+ "copy-tx-hash": "Copy Transaction Hash",
+ "copy-address": "Copy Address"
},
"notification-panel": {
"title": "Notifications"
@@ -341,6 +343,16 @@
"withdraw-alert": "Hint: these are only {{epochs}} epochs (~{{hours}} hours) until the end of your current lock period. If you wish to withdraw in current lock period, please send withdraw request in time. There are {{nextLeftEpochs}} epochs(~{{days}} days) until the end of your next lock period.",
"insufficient-period-alert-title": "Referenced Header is Invalid",
"insufficient-period-alert-message": "Only mature header can be referenced in transactions(Matureness requires 4 epochs)"
+ },
+ "updates": {
+ "check-updates": "Check for Updates",
+ "downloading-update": "Downloading update...",
+ "update-not-available": "There are currently no updates available.",
+ "release-notes": "Release Notes:",
+ "updates-found-do-you-want-to-update": "An update ({{version}}) is available, do you want to download and install now?",
+ "download-update": "Install Update",
+ "updates-downloaded-about-to-quit-and-install": "Update downloaded. Ready to install and relaunch.",
+ "quit-and-install": "Install and relaunch"
}
}
}
diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json
index 4809dc1eae..ad859f0de5 100644
--- a/packages/neuron-ui/src/locales/zh.json
+++ b/packages/neuron-ui/src/locales/zh.json
@@ -241,7 +241,9 @@
"toggle": {
"on": "开",
"off": "关"
- }
+ },
+ "copy-tx-hash": "复制交易 Hash",
+ "copy-address": "复制地址"
},
"notification-panel": {
"title": "通知中心"
@@ -341,6 +343,16 @@
"withdraw-alert": "提示:本补贴申请距离 Nervos DAO 规则允许的最近一个锁定周期仅剩下 {{epochs}} 个 epoch (约 {{hours}} 小时)。 如果您希望在本锁定周期取出,请及时提交取出申请,以确保取出申请能在本锁定周期结束之前上链。下一个锁定周期的结束时间预计为 {{nextLeftEpochs}} 个 epochs (约 {{days}} 天)。",
"insufficient-period-alert-title": "Header 引用无效",
"insufficient-period-alert-message": "交易只能引用已成熟的 Header(成熟期为 4 个 epochs)"
+ },
+ "updates": {
+ "check-updates": "检查更新",
+ "downloading-update": "正在下载更新...",
+ "update-not-available": "已经在使用最新版本",
+ "release-notes": "Release Notes:",
+ "updates-found-do-you-want-to-update": "新版本 {{version}} 可供下载。要下载和安装吗?",
+ "download-update": "下载安装",
+ "updates-downloaded-about-to-quit-and-install": "下载完成。请安装新版本。",
+ "quit-and-install": "安装并重启应用"
}
}
}
diff --git a/packages/neuron-ui/src/services/chain.ts b/packages/neuron-ui/src/services/chain.ts
index 8b2f08a3d0..e98285478a 100644
--- a/packages/neuron-ui/src/services/chain.ts
+++ b/packages/neuron-ui/src/services/chain.ts
@@ -1,11 +1,13 @@
import CKBCore from '@nervosnetwork/ckb-sdk-core'
export const ckbCore = new CKBCore('')
-export const { getBlockchainInfo, getTipHeader, getHeaderByNumber } = ckbCore.rpc
+export const { getBlock, getBlockchainInfo, getTipHeader, getHeaderByNumber, calculateDaoMaximumWithdraw } = ckbCore.rpc
export default {
ckbCore,
+ getBlock,
getBlockchainInfo,
getTipHeader,
getHeaderByNumber,
+ calculateDaoMaximumWithdraw,
}
diff --git a/packages/neuron-ui/src/services/remote/index.ts b/packages/neuron-ui/src/services/remote/index.ts
index 4d0f24c5ae..5e717365d7 100644
--- a/packages/neuron-ui/src/services/remote/index.ts
+++ b/packages/neuron-ui/src/services/remote/index.ts
@@ -2,6 +2,7 @@ export * from './app'
export * from './wallets'
export * from './networks'
export * from './transactions'
+export * from './updater'
const REMOTE_MODULE_NOT_FOUND =
'The remote module is not found, please make sure the UI is running inside the Electron App'
@@ -81,6 +82,16 @@ export const openExternal = (url: string) => {
}
}
+export const openContextMenu = (template: { label: string; click: Function }[]): void => {
+ if (!window.remote) {
+ window.alert(REMOTE_MODULE_NOT_FOUND)
+ } else {
+ const { Menu } = window.remote.require('electron')
+ const menu = Menu.buildFromTemplate(template)
+ menu.popup()
+ }
+}
+
export default {
getLocale,
validateMnemonic,
@@ -90,4 +101,5 @@ export default {
showOpenDialog,
getWinID,
openExternal,
+ openContextMenu,
}
diff --git a/packages/neuron-ui/src/services/remote/updater.ts b/packages/neuron-ui/src/services/remote/updater.ts
new file mode 100644
index 0000000000..ddf8207362
--- /dev/null
+++ b/packages/neuron-ui/src/services/remote/updater.ts
@@ -0,0 +1,11 @@
+import { apiMethodWrapper } from './apiMethodWrapper'
+
+export const checkForUpdates = apiMethodWrapper(api => () => api.checkForUpdates())
+export const downloadUpdate = apiMethodWrapper(api => () => api.downloadUpdate())
+export const installUpdate = apiMethodWrapper(api => () => api.quitAndInstall())
+
+export default {
+ checkForUpdates,
+ downloadUpdate,
+ installUpdate,
+}
diff --git a/packages/neuron-ui/src/services/subjects.ts b/packages/neuron-ui/src/services/subjects.ts
index dc9566c05e..3152c613b9 100644
--- a/packages/neuron-ui/src/services/subjects.ts
+++ b/packages/neuron-ui/src/services/subjects.ts
@@ -21,6 +21,7 @@ const SubjectConstructor = (
| 'connection-status-updated'
| 'synced-block-number-updated'
| 'command'
+ | 'app-updater-updated'
) => {
return window.ipcRenderer
? {
@@ -45,6 +46,7 @@ export const NetworkList = SubjectConstructor('network-list
export const CurrentNetworkID = SubjectConstructor('current-network-id-updated')
export const ConnectionStatus = SubjectConstructor('connection-status-updated')
export const SyncedBlockNumber = SubjectConstructor('synced-block-number-updated')
+export const AppUpdater = SubjectConstructor('app-updater-updated')
export const Command = SubjectConstructor('command')
export default {
@@ -56,5 +58,6 @@ export default {
CurrentNetworkID,
ConnectionStatus,
SyncedBlockNumber,
+ AppUpdater,
Command,
}
diff --git a/packages/neuron-ui/src/states/initStates/index.ts b/packages/neuron-ui/src/states/initStates/index.ts
index 9cac8f7eee..f393cf795e 100644
--- a/packages/neuron-ui/src/states/initStates/index.ts
+++ b/packages/neuron-ui/src/states/initStates/index.ts
@@ -3,12 +3,14 @@ import chain from './chain'
import wallet from './wallet'
import settings from './settings'
import nervosDAO from './nervosDAO'
+import updater from './updater'
export * from './app'
export * from './chain'
export * from './wallet'
export * from './settings'
export * from './nervosDAO'
+export * from './updater'
const initStates = {
app,
@@ -16,6 +18,7 @@ const initStates = {
wallet,
settings,
nervosDAO,
+ updater,
}
export default initStates
diff --git a/packages/neuron-ui/src/states/initStates/updater.ts b/packages/neuron-ui/src/states/initStates/updater.ts
new file mode 100644
index 0000000000..487b8769c6
--- /dev/null
+++ b/packages/neuron-ui/src/states/initStates/updater.ts
@@ -0,0 +1,8 @@
+const appUpdaterState: State.AppUpdater = {
+ checking: false,
+ downloadProgress: -1,
+ version: '',
+ releaseNotes: '',
+}
+
+export default appUpdaterState
diff --git a/packages/neuron-ui/src/states/stateProvider/reducer.ts b/packages/neuron-ui/src/states/stateProvider/reducer.ts
index 54ed5a328b..6a71bd17bd 100644
--- a/packages/neuron-ui/src/states/stateProvider/reducer.ts
+++ b/packages/neuron-ui/src/states/stateProvider/reducer.ts
@@ -20,6 +20,8 @@ export enum NeuronWalletActions {
UpdateSyncedBlockNumber = 'updateSyncedBlockNumber',
// dao
UpdateNervosDaoData = 'updateNervosDaoData',
+ // updater
+ UpdateAppUpdaterStatus = 'updateAppUpdaterStatus',
}
export enum AppActions {
UpdateTransactionID = 'updateTransactionID',
@@ -97,6 +99,12 @@ export const reducer = (
networks,
wallets,
},
+ updater: {
+ checking: false,
+ downloadProgress: -1,
+ version: '',
+ releaseNotes: '',
+ },
}
}
case NeuronWalletActions.UpdateCodeHash: {
@@ -224,6 +232,12 @@ export const reducer = (
},
}
}
+ case NeuronWalletActions.UpdateAppUpdaterStatus: {
+ return {
+ ...state,
+ updater: payload,
+ }
+ }
case NeuronWalletActions.UpdateNervosDaoData: {
return {
...state,
diff --git a/packages/neuron-ui/src/types/App/index.d.ts b/packages/neuron-ui/src/types/App/index.d.ts
index a8abf40fd5..227daa96b7 100644
--- a/packages/neuron-ui/src/types/App/index.d.ts
+++ b/packages/neuron-ui/src/types/App/index.d.ts
@@ -184,12 +184,20 @@ declare namespace State {
records: NervosDAORecord[]
}
+ interface AppUpdater {
+ checking: boolean
+ downloadProgress: number
+ version: string
+ releaseNotes: string
+ }
+
interface AppWithNeuronWallet {
app: App
chain: Chain
settings: Settings
wallet: Wallet
nervosDAO: NervosDAO
+ updater: AppUpdater
}
}
diff --git a/packages/neuron-ui/src/types/Subject/index.d.ts b/packages/neuron-ui/src/types/Subject/index.d.ts
index b614627c1d..c141cf4b3f 100644
--- a/packages/neuron-ui/src/types/Subject/index.d.ts
+++ b/packages/neuron-ui/src/types/Subject/index.d.ts
@@ -29,4 +29,10 @@ declare namespace Subject {
}
type ConnectionStatus = boolean
type BlockNumber = string
+ interface AppUpdater {
+ checking: boolean
+ downloadProgress: number
+ version: string
+ releaseNotes: string
+ }
}
diff --git a/packages/neuron-ui/src/utils/const.ts b/packages/neuron-ui/src/utils/const.ts
index 8c41f126af..3c94a590f6 100644
--- a/packages/neuron-ui/src/utils/const.ts
+++ b/packages/neuron-ui/src/utils/const.ts
@@ -21,7 +21,7 @@ export const WITHDRAW_EPOCHS = 180
export const RUN_NODE_GUIDE_URL = 'https://docs.nervos.org/references/neuron-wallet-guide.html#1-run-a-ckb-mainnet-node'
export const NERVOS_DAO_RFC_URL =
- 'https://github.com/nervosnetwork/rfcs/tree/master/rfcs/0000-dao-deposit-withdraw/0000-dao-deposit-withdraw.md'
+ 'https://www.github.com/nervosnetwork/rfcs/blob/master/rfcs/0023-dao-deposit-withdraw/0023-dao-deposit-withdraw.md'
export enum ConnectionStatus {
Online = 'online',
diff --git a/packages/neuron-wallet/package.json b/packages/neuron-wallet/package.json
index a518cea225..8d9caf8255 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.25.1",
+ "version": "0.25.2",
"private": true,
"author": {
"name": "Nervos Core Dev",
@@ -34,8 +34,8 @@
]
},
"dependencies": {
- "@nervosnetwork/ckb-sdk-core": "0.25.0-alpha.0",
- "@nervosnetwork/ckb-sdk-utils": "0.25.0-alpha.0",
+ "@nervosnetwork/ckb-sdk-core": "0.25.0",
+ "@nervosnetwork/ckb-sdk-utils": "0.25.0",
"bn.js": "4.11.8",
"chalk": "2.4.2",
"electron-log": "3.0.7",
@@ -51,7 +51,7 @@
"uuid": "3.3.3"
},
"devDependencies": {
- "@nervosnetwork/ckb-types": "0.25.0-alpha.0",
+ "@nervosnetwork/ckb-types": "0.25.0",
"@types/electron-devtools-installer": "2.2.0",
"@types/elliptic": "6.4.9",
"@types/sqlite3": "3.1.5",
@@ -64,7 +64,7 @@
"electron-devtools-installer": "2.2.4",
"electron-notarize": "0.1.1",
"lint-staged": "9.2.5",
- "neuron-ui": "0.25.1",
+ "neuron-ui": "0.25.2",
"rimraf": "3.0.0",
"spectron": "8.0.0",
"ts-transformer-imports": "0.4.3",
diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts
index 7316bfbfd4..59470a40c6 100644
--- a/packages/neuron-wallet/src/controllers/api.ts
+++ b/packages/neuron-wallet/src/controllers/api.ts
@@ -4,7 +4,7 @@ import env from 'env'
import i18n from 'utils/i18n'
import { popContextMenu } from './app/menu'
import { showWindow } from './app/show-window'
-import { TransactionsController, WalletsController, SyncController, NetworksController } from 'controllers'
+import { TransactionsController, WalletsController, SyncController, NetworksController, UpdateController } from 'controllers'
import { NetworkType, NetworkID, Network } from 'types/network'
import NetworksService from 'services/networks'
import WalletsService from 'services/wallets'
@@ -158,21 +158,6 @@ export default class ApiController {
return WalletsController.getAllAddresses(id)
}
- @MapApiResponse
- public static async sendCapacity(params: {
- id: string
- walletID: string
- items: {
- address: string
- capacity: string
- }[]
- password: string
- fee: string
- description?: string
- }) {
- return WalletsController.sendCapacity(params)
- }
-
@MapApiResponse
public static async sendTx(params: {
walletID: string
@@ -227,11 +212,6 @@ export default class ApiController {
return WalletsController.withdrawFromDao(params)
}
- @MapApiResponse
- public static async computeCycles(params: { walletID: string; capacities: string }) {
- return WalletsController.computeCycles(params)
- }
-
@MapApiResponse
public static async updateAddressDescription(params: {
walletID: string
@@ -291,6 +271,7 @@ export default class ApiController {
}
// Dao
+
@MapApiResponse
public static async getDaoCells(
params: Controller.Params.GetDaoCellsParams
@@ -298,7 +279,23 @@ export default class ApiController {
return DaoController.getDaoCells(params)
}
- // settings
+ // Settings
+
+ @MapApiResponse
+ public static async checkForUpdates() {
+ return new UpdateController().checkUpdates()
+ }
+
+ @MapApiResponse
+ public static async downloadUpdate() {
+ return new UpdateController(false).downloadUpdate()
+ }
+
+ @MapApiResponse
+ public static async quitAndInstall() {
+ return new UpdateController(false).quitAndInstall()
+ }
+
@MapApiResponse
public static async clearCellCache() {
await SyncController.stopSyncing()
diff --git a/packages/neuron-wallet/src/controllers/app/menu.ts b/packages/neuron-wallet/src/controllers/app/menu.ts
index f82ef2d9d0..b0e8a97e99 100644
--- a/packages/neuron-wallet/src/controllers/app/menu.ts
+++ b/packages/neuron-wallet/src/controllers/app/menu.ts
@@ -1,4 +1,4 @@
-import { app, shell, BrowserWindow, dialog, MenuItemConstructorOptions, clipboard, Menu, MenuItem, MessageBoxOptions, MessageBoxReturnValue } from 'electron'
+import { app, shell, BrowserWindow, dialog, MenuItemConstructorOptions, clipboard, Menu, MessageBoxOptions, MessageBoxReturnValue } from 'electron'
import { bech32Address, AddressPrefix, AddressType } from '@nervosnetwork/ckb-sdk-utils'
import i18n from 'utils/i18n'
import env from 'env'
@@ -77,9 +77,12 @@ const updateApplicationMenu = (mainWindow: BrowserWindow | null) => {
click: () => { showAbout() },
},
{
- enabled: isMainWindow,
label: i18n.t('application-menu.neuron.check-updates'),
- click: (menuItem: MenuItem) => { new UpdateController().checkUpdates(menuItem) }
+ enabled: isMainWindow && !UpdateController.isChecking,
+ click: () => {
+ new UpdateController().checkUpdates()
+ navTo(URL.Preference)
+ }
},
separator,
{
@@ -225,7 +228,11 @@ const updateApplicationMenu = (mainWindow: BrowserWindow | null) => {
})
helpSubmenu.push({
label: i18n.t('application-menu.neuron.check-updates'),
- click: (menuItem: MenuItem) => { new UpdateController().checkUpdates(menuItem) }
+ enabled: isMainWindow && !UpdateController.isChecking,
+ click: () => {
+ new UpdateController().checkUpdates()
+ navTo(URL.Preference)
+ }
})
helpSubmenu.push({
id: 'about',
diff --git a/packages/neuron-wallet/src/controllers/app/subscribe.ts b/packages/neuron-wallet/src/controllers/app/subscribe.ts
index f0f9aa8bfd..0e7b3dd131 100644
--- a/packages/neuron-wallet/src/controllers/app/subscribe.ts
+++ b/packages/neuron-wallet/src/controllers/app/subscribe.ts
@@ -7,6 +7,7 @@ import { DebouncedCurrentNetworkIDSubject, DebouncedNetworkListSubject } from 'm
import { SampledSyncedBlockNumberSubject, DebouncedConnectionStatusSubject } from 'models/subjects/node'
import { WalletListSubject, CurrentWalletSubject } from 'models/subjects/wallets'
import dataUpdateSubject from 'models/subjects/data-update'
+import AppUpdaterSubject from 'models/subjects/app-updater'
interface AppResponder {
sendMessage: (channel: string, arg: any) => void
@@ -52,4 +53,9 @@ export const subscribe = (dispatcher: AppResponder) => {
dataUpdateSubject.next({ dataType: 'current-wallet', actionType: 'update' })
}
})
+
+ AppUpdaterSubject.subscribe(params => {
+ dispatcher.updateMenu()
+ dispatcher.sendMessage('app-updater-updated', params)
+ })
}
diff --git a/packages/neuron-wallet/src/controllers/update.ts b/packages/neuron-wallet/src/controllers/update.ts
index 139027982e..acc274942a 100644
--- a/packages/neuron-wallet/src/controllers/update.ts
+++ b/packages/neuron-wallet/src/controllers/update.ts
@@ -1,76 +1,82 @@
import { dialog } from 'electron'
import { autoUpdater, UpdateInfo } from 'electron-updater'
import i18n from 'utils/i18n'
+import AppUpdaterSubject from 'models/subjects/app-updater'
export default class UpdateController {
- sender: { enabled: boolean } | null
+ static isChecking = false // One instance is already running and checking
- constructor() {
+ constructor(check: boolean = true) {
autoUpdater.autoDownload = false
- this.sender = null
- this.bindEvents()
+ if (check && !UpdateController.isChecking) {
+ this.bindEvents()
+ }
}
- public checkUpdates(sender: { enabled: boolean }) {
- this.sender = sender
- this.sender.enabled = false
-
+ public checkUpdates() {
+ UpdateController.isChecking = true
autoUpdater.checkForUpdates()
+
+ AppUpdaterSubject.next({
+ checking: true,
+ downloadProgress: -1,
+ version: '',
+ releaseNotes: ''
+ })
+ }
+
+ public quitAndInstall() {
+ autoUpdater.quitAndInstall()
+ }
+
+ public downloadUpdate() {
+ this.notify(0)
+ autoUpdater.downloadUpdate()
}
- bindEvents() {
+ private bindEvents() {
autoUpdater.removeAllListeners()
autoUpdater.on('error', error => {
+ UpdateController.isChecking = false
+ this.notify()
+
dialog.showErrorBox('Error', error == null ? 'unknown' : (error.stack || error).toString())
- this.enableSender()
})
autoUpdater.on('update-available', (info: UpdateInfo) => {
- const { version } = info
- dialog
- .showMessageBox({
- type: 'info',
- title: version,
- message: i18n.t('updater.updates-found-do-you-want-to-update', { version }),
- buttons: [i18n.t('updater.update-now'), i18n.t('common.no')],
- })
- .then(returnValue => {
- if (returnValue.response === 0) {
- autoUpdater.downloadUpdate()
- } else {
- this.enableSender()
- }
- })
+ UpdateController.isChecking = false
+ this.notify(-1, info.version, info.releaseNotes as string)
})
autoUpdater.on('update-not-available', () => {
+ UpdateController.isChecking = false
+ this.notify()
+
dialog.showMessageBox({
type: 'info',
message: i18n.t('updater.update-not-available'),
buttons: [i18n.t('common.ok')],
})
- this.enableSender()
+ })
+
+ autoUpdater.on('download-progress', progress => {
+ this.notify(progress.percent / 100)
})
autoUpdater.on('update-downloaded', () => {
- dialog
- .showMessageBox({
- type: 'info',
- message: i18n.t('updater.updates-downloaded-about-to-quit-and-install'),
- buttons: [i18n.t('common.ok')],
- })
- .then(() => {
- setImmediate(() => autoUpdater.quitAndInstall())
- })
+ UpdateController.isChecking = false
+ this.notify(1)
})
}
- enableSender() {
- if (this.sender) {
- this.sender.enabled = true
- }
- this.sender = null
+ private notify(downloadProgress: number = -1, version = '', releaseNotes = '') {
+ AppUpdaterSubject.next({
+ checking: UpdateController.isChecking,
+ downloadProgress,
+ version,
+ releaseNotes
+ })
}
}
diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts
index 51c6e8d555..d6c632e3da 100644
--- a/packages/neuron-wallet/src/controllers/wallets.ts
+++ b/packages/neuron-wallet/src/controllers/wallets.ts
@@ -321,56 +321,6 @@ export default class WalletsController {
}
}
- public static async sendCapacity(params: {
- id: string
- walletID: string
- items: {
- address: string
- capacity: string
- }[]
- password: string
- fee: string
- description?: string
- }) {
- if (!params) {
- throw new IsRequired('Parameters')
- }
- // set default feeRate
- let feeRate = '0'
- if (!params.fee || params.fee === '0') {
- feeRate = '1000'
- }
-
- const isMainnet = NetworksService.getInstance().isMainnet()
- params.items.forEach(item => {
- if (isMainnet && !item.address.startsWith('ckb')) {
- throw new MainnetAddressRequired(item.address)
- }
-
- if (!isMainnet && !item.address.startsWith('ckt')) {
- throw new TestnetAddressRequired(item.address)
- }
-
- if (!this.verifyAddress(item.address)) {
- throw new InvalidAddress(item.address)
- }
- })
-
- const walletsService = WalletsService.getInstance()
- const hash = await walletsService.sendCapacity(
- params.walletID,
- params.items,
- params.password,
- params.fee,
- feeRate,
- params.description
- )
- return {
- status: ResponseCode.Success,
- result: hash,
- }
- }
-
public static async sendTx(params: {
walletID: string
tx: TransactionWithoutHash
@@ -380,6 +330,7 @@ export default class WalletsController {
if (!params) {
throw new IsRequired('Parameters')
}
+
const walletsService = WalletsService.getInstance()
const hash = await walletsService.sendTx(
params.walletID,
@@ -405,6 +356,9 @@ export default class WalletsController {
if (!params) {
throw new IsRequired('Parameters')
}
+ const addresses: string[] = params.items.map(i => i.address)
+ WalletsController.checkAddresses(addresses)
+
const walletsService = WalletsService.getInstance()
const tx = await walletsService.generateTx(
params.walletID,
@@ -486,18 +440,6 @@ export default class WalletsController {
}
}
- public static async computeCycles(params: { walletID: string; capacities: string }) {
- if (!params) {
- throw new IsRequired('Parameters')
- }
- const walletsService = WalletsService.getInstance()
- const cycles = await walletsService.computeCycles(params.walletID, params.capacities)
- return {
- status: ResponseCode.Success,
- result: cycles,
- }
- }
-
public static async updateAddressDescription({
walletID,
address,
@@ -522,6 +464,23 @@ export default class WalletsController {
}
}
+ private static checkAddresses = (addresses: string[]) => {
+ const isMainnet = NetworksService.getInstance().isMainnet()
+ addresses.forEach(address => {
+ if (isMainnet && !address.startsWith('ckb')) {
+ throw new MainnetAddressRequired(address)
+ }
+
+ if (!isMainnet && !address.startsWith('ckt')) {
+ throw new TestnetAddressRequired(address)
+ }
+
+ if (!WalletsController.verifyAddress(address)) {
+ throw new InvalidAddress(address)
+ }
+ })
+ }
+
private static verifyAddress = (address: string): boolean => {
if (typeof address !== 'string' || address.length !== 46) {
return false
diff --git a/packages/neuron-wallet/src/database/address/address-dao.ts b/packages/neuron-wallet/src/database/address/address-dao.ts
index a748b18907..02fe31f29c 100644
--- a/packages/neuron-wallet/src/database/address/address-dao.ts
+++ b/packages/neuron-wallet/src/database/address/address-dao.ts
@@ -54,38 +54,35 @@ export default class AddressDao {
// pendingBalance means balance of OutputStatus.Pending cells (sent from me, but not committed)
// so the final balance is (liveBalance + sentBalance - pendingBalance)
// balance is the balance of the cells those who don't hold data or type script
- public static updateTxCountAndBalance = async (
- address: string,
+ public static updateTxCountAndBalances = async (
+ addresses: string[],
url: string = NodeService.getInstance().core.rpc.node.url
): Promise => {
const all = AddressStore.getAll()
const toUpdate = all.filter(value => {
- return value.address === address
+ return addresses.includes(value.address)
})
const others = all.filter(value => {
- return value.address !== address
+ return !addresses.includes(value.address)
})
- const txCount: number = await TransactionsService.getCountByAddressAndStatus(address, [
- TransactionStatus.Pending,
- TransactionStatus.Success,
- ], url)
const lockUtils = new LockUtils(await LockUtils.systemScript(url))
- const result = await Promise.all(
- toUpdate.map(async entity => {
- const item = entity
- item.txCount = txCount
- const lockHashes: string[] = lockUtils.addressToAllLockHashes(item.address)
- item.liveBalance = await CellsService.getBalance(lockHashes, OutputStatus.Live)
- item.sentBalance = await CellsService.getBalance(lockHashes, OutputStatus.Sent)
- item.pendingBalance = await CellsService.getBalance(lockHashes, OutputStatus.Pending)
- item.balance = (BigInt(item.liveBalance) + BigInt(item.sentBalance)).toString()
- return item
- })
- )
+ for (const addr of toUpdate) {
+ const txCount: number = await TransactionsService.getCountByAddressAndStatus(addr.address, [
+ TransactionStatus.Pending,
+ TransactionStatus.Success,
+ ], url)
+ addr.txCount = txCount
+
+ const lockHashes: string[] = lockUtils.addressToAllLockHashes(addr.address)
+ addr.liveBalance = await CellsService.getBalance(lockHashes, OutputStatus.Live)
+ addr.sentBalance = await CellsService.getBalance(lockHashes, OutputStatus.Sent)
+ addr.pendingBalance = await CellsService.getBalance(lockHashes, OutputStatus.Pending)
+ addr.balance = (BigInt(addr.liveBalance) + BigInt(addr.sentBalance)).toString()
+ }
AddressStore.updateAll(toUpdate.concat(others))
- return result
+ return toUpdate
}
public static nextUnusedAddress(walletId: string, version: AddressVersion): Address | undefined {
diff --git a/packages/neuron-wallet/src/database/chain/cleaner.ts b/packages/neuron-wallet/src/database/chain/cleaner.ts
index e168a1bc07..5d134bf9f2 100644
--- a/packages/neuron-wallet/src/database/chain/cleaner.ts
+++ b/packages/neuron-wallet/src/database/chain/cleaner.ts
@@ -11,4 +11,4 @@ export default class ChainCleaner {
await getConnection().getRepository(entity).clear()
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/neuron-wallet/src/listeners/address.ts b/packages/neuron-wallet/src/listeners/address.ts
index 845105fd7f..a69592d6f9 100644
--- a/packages/neuron-wallet/src/listeners/address.ts
+++ b/packages/neuron-wallet/src/listeners/address.ts
@@ -1,6 +1,4 @@
import { remote } from 'electron'
-import { ReplaySubject } from 'rxjs'
-import { bufferTime } from 'rxjs/operators'
import AddressesUsedSubject, { AddressesWithURL } from 'models/subjects/addresses-used-subject'
import AddressService from 'services/addresses'
import { Address } from 'database/address/address-dao'
@@ -12,23 +10,13 @@ const addressesUsedSubject = isRenderer
? remote.require('./models/subjects/addresses-used-subject').default.getSubject()
: AddressesUsedSubject.getSubject()
-// pipe not working directly
-const bridge = new ReplaySubject(1000)
-addressesUsedSubject.subscribe((params: AddressesWithURL) => {
- bridge.next(params)
-})
-
// update txCount when addresses used
export const register = () => {
- bridge.pipe(bufferTime(1000)).subscribe(async (addressesList: AddressesWithURL[]) => {
- if (addressesList.length === 0) {
- return
- }
- const addresses = addressesList.map(list => list.addresses).reduce((acc, val) => acc.concat(val), [])
- const url: string = addressesList[addressesList.length - 1].url
- const uniqueAddresses = [...new Set(addresses)]
- const addrs = await AddressService.updateTxCountAndBalances(uniqueAddresses, url)
- const walletIds: string[] = addrs.map(addr => (addr as Address).walletId).filter((value, idx, a) => a.indexOf(value) === idx)
+ addressesUsedSubject.subscribe(async (address: AddressesWithURL) => {
+ const addrs = await AddressService.updateTxCountAndBalances(address.addresses, address.url)
+ const walletIds: string[] = addrs
+ .map(addr => (addr as Address).walletId)
+ .filter((value, idx, a) => a.indexOf(value) === idx)
for (const id of walletIds) {
const wallet = WalletService.getInstance().get(id)
const accountExtendedPublicKey: AccountExtendedPublicKey = wallet.accountExtendedPublicKey()
diff --git a/packages/neuron-wallet/src/locales/en.ts b/packages/neuron-wallet/src/locales/en.ts
index a437ec3d14..c38c5a8d5d 100644
--- a/packages/neuron-wallet/src/locales/en.ts
+++ b/packages/neuron-wallet/src/locales/en.ts
@@ -143,9 +143,6 @@ export default {
},
updater: {
'update-not-available': 'There are currently no updates available.',
- 'updates-found-do-you-want-to-update': 'An update ({{version}}) is available, do you want to update now?',
- 'update-now': 'Update now',
- 'updates-downloaded-about-to-quit-and-install': 'Update downloaded. Neuron will quit and install the update...',
},
common: {
yes: 'Yes',
diff --git a/packages/neuron-wallet/src/locales/zh.ts b/packages/neuron-wallet/src/locales/zh.ts
index c77849b7c2..7dc69d8408 100644
--- a/packages/neuron-wallet/src/locales/zh.ts
+++ b/packages/neuron-wallet/src/locales/zh.ts
@@ -142,9 +142,6 @@ export default {
},
updater: {
'update-not-available': '没有可供升级的新版本。',
- 'updates-found-do-you-want-to-update': '有可供升级的新版本({{version}})。现在进行升级吗?',
- 'update-now': '马上升级',
- 'updates-downloaded-about-to-quit-and-install': '下载完成。Neuron 将退出并安装新版本...',
},
common: {
yes: '是',
diff --git a/packages/neuron-wallet/src/models/dao-utils.ts b/packages/neuron-wallet/src/models/dao-utils.ts
index 04d76060c5..07bb9bf5d5 100644
--- a/packages/neuron-wallet/src/models/dao-utils.ts
+++ b/packages/neuron-wallet/src/models/dao-utils.ts
@@ -45,6 +45,10 @@ export default class DaoUtils {
return DaoUtils.daoScriptInfo
}
+ static cleanInfo(): void {
+ DaoUtils.daoScriptInfo = undefined
+ }
+
static setDaoScript(info: SystemScript) {
DaoUtils.daoScriptInfo = info
DaoUtils.previousURL = NodeService.getInstance().core.rpc.node.url
diff --git a/packages/neuron-wallet/src/models/lock-utils.ts b/packages/neuron-wallet/src/models/lock-utils.ts
index 2ef84f8e51..1e01f42d57 100644
--- a/packages/neuron-wallet/src/models/lock-utils.ts
+++ b/packages/neuron-wallet/src/models/lock-utils.ts
@@ -17,17 +17,6 @@ export interface SystemScript {
hashType: ScriptHashType
}
-const subscribed = (target: any, propertyName: string) => {
- let value: any
- Object.defineProperty(target, propertyName, {
- get: () => value,
- set: (info: { codeHash: string }) => {
- SystemScriptSubject.next({ codeHash: info.codeHash })
- value = info
- },
- })
-}
-
export default class LockUtils {
systemScript: SystemScript
@@ -35,10 +24,9 @@ export default class LockUtils {
this.systemScript = systemScript
}
- @subscribed
- static systemScriptInfo: SystemScript | undefined
+ private static systemScriptInfo: SystemScript | undefined
- static previousURL: string | undefined
+ private static previousURL: string | undefined
static async loadSystemScript(nodeURL: string): Promise {
const core = new Core(nodeURL)
@@ -75,6 +63,10 @@ export default class LockUtils {
return LockUtils.systemScriptInfo
}
+ static cleanInfo(): void {
+ LockUtils.systemScriptInfo = undefined
+ }
+
static setSystemScript(info: SystemScript) {
LockUtils.systemScriptInfo = info
LockUtils.previousURL = NodeService.getInstance().core.rpc.node.url
diff --git a/packages/neuron-wallet/src/models/subjects/address-created-subject.ts b/packages/neuron-wallet/src/models/subjects/address-created-subject.ts
index 63ff7968cd..1601899534 100644
--- a/packages/neuron-wallet/src/models/subjects/address-created-subject.ts
+++ b/packages/neuron-wallet/src/models/subjects/address-created-subject.ts
@@ -7,4 +7,8 @@ export default class AddressCreatedSubject {
static getSubject() {
return this.subject
}
+
+ static setSubject(subject: ReplaySubject) {
+ this.subject = subject
+ }
}
diff --git a/packages/neuron-wallet/src/models/subjects/app-updater.ts b/packages/neuron-wallet/src/models/subjects/app-updater.ts
new file mode 100644
index 0000000000..c3bd1c55af
--- /dev/null
+++ b/packages/neuron-wallet/src/models/subjects/app-updater.ts
@@ -0,0 +1,10 @@
+import { Subject } from 'rxjs'
+
+const AppUpdaterSubject = new Subject<{
+ checking: boolean
+ downloadProgress: number, // -1: not started, 1: finished, 0~1: downloading
+ version: string, // "": no update, "v.x.y.z": version v.x.y.z available
+ releaseNotes: string
+}>()
+
+export default AppUpdaterSubject
diff --git a/packages/neuron-wallet/src/models/subjects/network-switch-subject.ts b/packages/neuron-wallet/src/models/subjects/network-switch-subject.ts
new file mode 100644
index 0000000000..d1a4e89ef6
--- /dev/null
+++ b/packages/neuron-wallet/src/models/subjects/network-switch-subject.ts
@@ -0,0 +1,10 @@
+import { BehaviorSubject } from 'rxjs'
+import { NetworkWithID } from 'types/network'
+
+export default class NetworkSwitchSubject {
+ static subject = new BehaviorSubject(undefined)
+
+ static getSubject() {
+ return this.subject
+ }
+}
diff --git a/packages/neuron-wallet/src/models/subjects/wallet-created-subject.ts b/packages/neuron-wallet/src/models/subjects/wallet-created-subject.ts
index f7afa4f591..653a951c38 100644
--- a/packages/neuron-wallet/src/models/subjects/wallet-created-subject.ts
+++ b/packages/neuron-wallet/src/models/subjects/wallet-created-subject.ts
@@ -1,13 +1,13 @@
-import { ReplaySubject } from 'rxjs'
+import { Subject } from 'rxjs'
export class WalletCreatedSubject {
- static subject = new ReplaySubject(1)
+ static subject = new Subject()
static getSubject() {
return this.subject
}
- static setSubject(subject: ReplaySubject) {
+ static setSubject(subject: Subject) {
this.subject = subject
}
}
diff --git a/packages/neuron-wallet/src/services/addresses.ts b/packages/neuron-wallet/src/services/addresses.ts
index 9d876969d9..e5daa731c7 100644
--- a/packages/neuron-wallet/src/services/addresses.ts
+++ b/packages/neuron-wallet/src/services/addresses.ts
@@ -87,13 +87,11 @@ export default class AddressService {
)
}
- public static updateTxCountAndBalances = async (addresses: string[], url: string = NodeService.getInstance().core.rpc.node.url) => {
- let result: Address[] = []
- for (const address of addresses) {
- const updatedAddress = await AddressDao.updateTxCountAndBalance(address, url)
- result = result.concat(updatedAddress)
- }
- return result
+ public static updateTxCountAndBalances = async (
+ addresses: string[],
+ url: string = NodeService.getInstance().core.rpc.node.url
+ ): Promise => {
+ return AddressDao.updateTxCountAndBalances(addresses, url)
}
// Generate both receiving and change addresses.
diff --git a/packages/neuron-wallet/src/services/networks.ts b/packages/neuron-wallet/src/services/networks.ts
index 0f499da69a..2a89f3973f 100644
--- a/packages/neuron-wallet/src/services/networks.ts
+++ b/packages/neuron-wallet/src/services/networks.ts
@@ -1,7 +1,6 @@
import Core from '@nervosnetwork/ckb-sdk-core'
import { v4 as uuid } from 'uuid'
-import { BehaviorSubject } from 'rxjs'
-import { LackOfDefaultNetwork, DefaultNetworkUnremovable } from 'exceptions/network'
+import { DefaultNetworkUnremovable, LackOfDefaultNetwork } from 'exceptions/network'
import Store from 'models/store'
@@ -9,8 +8,9 @@ import { Validate, Required } from 'decorators'
import { UsedName, NetworkNotFound, InvalidFormat } from 'exceptions'
import { NetworkListSubject, CurrentNetworkIDSubject } from 'models/subjects/networks'
import { MAINNET_GENESIS_HASH, EMPTY_GENESIS_HASH, NetworkID, NetworkName, NetworkRemote, NetworksKey, NetworkType, Network, NetworkWithID } from 'types/network'
+import NetworkSwitchSubject from 'models/subjects/network-switch-subject'
-export const networkSwitchSubject = new BehaviorSubject(undefined)
+const isMainProcess = process && process.type === 'browser'
const presetNetworks: { selected: string, networks: NetworkWithID[] } = {
selected: 'mainnet',
@@ -40,7 +40,9 @@ export default class NetworksService extends Store {
super('networks', 'index.json', JSON.stringify(presetNetworks))
const currentNetworkList = this.getAll()
- NetworkListSubject.next({ currentNetworkList })
+ if (isMainProcess) {
+ NetworkListSubject.next({ currentNetworkList })
+ }
const currentNetwork = this.getCurrent()
if (currentNetwork) {
@@ -48,12 +50,16 @@ export default class NetworksService extends Store {
this.update(currentNetwork.id, {}) // Update to trigger chain/genesis hash refresh
}
- CurrentNetworkIDSubject.next({ currentNetworkID: currentNetwork.id })
- networkSwitchSubject.next(currentNetwork)
+ if (isMainProcess) {
+ CurrentNetworkIDSubject.next({ currentNetworkID: currentNetwork.id })
+ NetworkSwitchSubject.getSubject().next(currentNetwork)
+ }
}
this.on(NetworksKey.List, async (_, currentNetworkList: NetworkWithID[] = []) => {
- NetworkListSubject.next({ currentNetworkList })
+ if (isMainProcess) {
+ NetworkListSubject.next({ currentNetworkList })
+ }
const currentID = this.getCurrentID()
if (currentNetworkList.find(network => network.id === currentID)) {
@@ -72,8 +78,10 @@ export default class NetworksService extends Store {
if (!currentNetwork) {
throw new NetworkNotFound(currentNetworkID)
}
- CurrentNetworkIDSubject.next({ currentNetworkID })
- networkSwitchSubject.next(currentNetwork)
+ if (isMainProcess) {
+ CurrentNetworkIDSubject.next({ currentNetworkID })
+ NetworkSwitchSubject.getSubject().next(currentNetwork)
+ }
})
}
@@ -157,9 +165,9 @@ export default class NetworksService extends Store {
this.updateAll(list)
- if (this.getCurrentID() === id) {
+ if (this.getCurrentID() === id && isMainProcess) {
CurrentNetworkIDSubject.next({ currentNetworkID: id })
- networkSwitchSubject.next(network)
+ NetworkSwitchSubject.getSubject().next(network)
}
}
diff --git a/packages/neuron-wallet/src/services/sync/block-listener.ts b/packages/neuron-wallet/src/services/sync/block-listener.ts
index 5aed6e8d2b..b592ff7088 100644
--- a/packages/neuron-wallet/src/services/sync/block-listener.ts
+++ b/packages/neuron-wallet/src/services/sync/block-listener.ts
@@ -31,11 +31,12 @@ export default class BlockListener {
}
public setLockHashes = (lockHashes: string[]) => {
- this.lockHashes = lockHashes
+ const hashes = [...new Set(lockHashes)]
+ this.lockHashes = hashes
if (!this.queue) {
return
}
- this.queue.setLockHashes(lockHashes)
+ this.queue.setLockHashes(hashes)
}
public appendLockHashes = (lockHashes: string[]) => {
diff --git a/packages/neuron-wallet/src/services/wallets.ts b/packages/neuron-wallet/src/services/wallets.ts
index e8bf140b93..4127b62fb3 100644
--- a/packages/neuron-wallet/src/services/wallets.ts
+++ b/packages/neuron-wallet/src/services/wallets.ts
@@ -34,7 +34,6 @@ const fileService = FileService.getInstance()
const MODULE_NAME = 'wallets'
const DEBOUNCE_TIME = 200
-const SECP_CYCLES = BigInt('1440000')
export interface Wallet {
id: string
@@ -329,22 +328,6 @@ export default class WalletService {
this.listStore.clear()
}
- public sendCapacity = async (
- walletID: string = '',
- items: {
- address: string
- capacity: string
- }[] = [],
- password: string = '',
- fee: string = '0',
- feeRate: string = '0',
- description: string = ''
- ): Promise => {
- const tx = await this.generateTx(walletID, items, fee, feeRate)
-
- return this.sendTx(walletID, tx, password, description)
- }
-
public sendTx = async (walletID: string = '', tx: TransactionWithoutHash, password: string = '', description: string = '') => {
const wallet = this.get(walletID)
if (!wallet) {
@@ -425,7 +408,8 @@ export default class WalletService {
// update addresses txCount and balance
const blake160s = TransactionsService.blake160sOfTx(tx)
- const usedAddresses = blake160s.map(blake160 => LockUtils.blake160ToAddress(blake160))
+ const prefix = NetworksService.getInstance().isMainnet() ? AddressPrefix.Mainnet : AddressPrefix.Testnet
+ const usedAddresses = blake160s.map(blake160 => LockUtils.blake160ToAddress(blake160, prefix))
AddressesUsedSubject.getSubject().next({
addresses: usedAddresses,
url: core.rpc.node.url,
@@ -767,24 +751,6 @@ export default class WalletService {
return (BigInt(0x20) << BigInt(56)) + (length << BigInt(40)) + (index << BigInt(24)) + number
}
- public computeCycles = async (walletID: string = '', capacities: string): Promise => {
- const wallet = this.get(walletID)
- if (!wallet) {
- throw new WalletNotFound(walletID)
- }
-
- const addressInfos = this.getAddressInfos(walletID)
-
- const addresses: string[] = addressInfos.map(info => info.address)
-
- const lockHashes: string[] = new LockUtils(await LockUtils.systemScript()).addressesToAllLockHashes(addresses)
-
- const { inputs } = await CellsService.gatherInputs(capacities, lockHashes, '0')
- const cycles = SECP_CYCLES * BigInt(inputs.length)
-
- return cycles.toString()
- }
-
// path is a BIP44 full path such as "m/44'/309'/0'/0/0"
public getAddressInfos = (walletID: string): AddressInterface[] => {
const wallet = this.get(walletID)
@@ -799,26 +765,6 @@ export default class WalletService {
return AddressService.nextUnusedChangeAddress(walletId)!.address
}
- public signWitness = (
- lock: string | undefined,
- privateKey: string,
- txHash: string,
- inputType: string | undefined = undefined,
- outputType: string | undefined = undefined
- ): string => {
- const witnessArg: CKBComponents.WitnessArgs = {
- lock,
- inputType,
- outputType,
- }
- const result = core.signWitnesses(privateKey)({
- transactionHash: txHash,
- witnesses: [witnessArg]
- })
-
- return result[0] as string
- }
-
// Derive all child private keys for specified BIP44 paths.
public getPrivateKeys = (wallet: Wallet, paths: string[], password: string): PathAndPrivateKey[] => {
const masterPrivateKey = wallet.loadKeystore().extendedPrivateKey(password)
diff --git a/packages/neuron-wallet/src/startup/sync-block-task/create.ts b/packages/neuron-wallet/src/startup/sync-block-task/create.ts
index 19b8d04fe9..a23072413d 100644
--- a/packages/neuron-wallet/src/startup/sync-block-task/create.ts
+++ b/packages/neuron-wallet/src/startup/sync-block-task/create.ts
@@ -1,7 +1,6 @@
import { BrowserWindow } from 'electron'
import { ReplaySubject } from 'rxjs'
import path from 'path'
-import { networkSwitchSubject } from 'services/networks'
import { NetworkWithID } from 'types/network'
import env from 'env'
import AddressService from 'services/addresses'
@@ -11,7 +10,12 @@ import DataUpdateSubject from 'models/subjects/data-update'
import logger from 'utils/logger'
import NodeService from 'services/node'
import NetworksService from 'services/networks'
-import { distinctUntilChanged } from 'rxjs/operators'
+import { distinctUntilChanged, pairwise, startWith } from 'rxjs/operators'
+import LockUtils from 'models/lock-utils'
+import DaoUtils from 'models/dao-utils'
+import NetworkSwitchSubject from 'models/subjects/network-switch-subject'
+import { SyncedBlockNumberSubject } from 'models/subjects/node'
+import BlockNumber from 'services/sync/block-number'
export { genesisBlockHash }
@@ -29,8 +33,13 @@ export interface DatabaseInitParams {
// network switch or network connect
const networkChange = async (network: NetworkWithID) => {
await InitDatabase.getInstance().stopAndWait()
+ // clean LockUtils info and DaoUtils info
+ LockUtils.cleanInfo()
+ DaoUtils.cleanInfo()
const info = await InitDatabase.getInstance().init(network)
+ const blockNumber = await (new BlockNumber()).getCurrent()
+ SyncedBlockNumberSubject.next(blockNumber.toString())
DataUpdateSubject.next({
dataType: 'transaction',
actionType: 'update',
@@ -50,11 +59,17 @@ const networkChange = async (network: NetworkWithID) => {
export const databaseInitSubject = new ReplaySubject(1)
-networkSwitchSubject.subscribe(async (network: NetworkWithID | undefined) => {
- if (network) {
- await networkChange(network)
- }
-})
+NetworkSwitchSubject
+ .getSubject()
+ .pipe(
+ startWith(undefined),
+ pairwise()
+ )
+ .subscribe(async ([previousNetwork, network]: (NetworkWithID | undefined)[]) => {
+ if ((!previousNetwork && network) || (previousNetwork && network && network.id !== previousNetwork.id)) {
+ await networkChange(network)
+ }
+ })
NodeService
.getInstance()
@@ -72,8 +87,6 @@ NodeService
const loadURL = `file://${path.join(__dirname, 'index.html')}`
-export { networkSwitchSubject }
-
let syncBlockBackgroundWindow: BrowserWindow | null
// create a background task to sync transactions
diff --git a/packages/neuron-wallet/src/startup/sync-block-task/indexer.ts b/packages/neuron-wallet/src/startup/sync-block-task/indexer.ts
index cef634c18e..d8da7ba553 100644
--- a/packages/neuron-wallet/src/startup/sync-block-task/indexer.ts
+++ b/packages/neuron-wallet/src/startup/sync-block-task/indexer.ts
@@ -5,6 +5,7 @@ import IndexerQueue, { LockHashInfo } from 'services/indexer/queue'
import { Address } from 'database/address/address-dao'
import initConnection from 'database/chain/ormconfig'
+import DaoUtils from 'models/dao-utils'
const { nodeService, addressCreatedSubject, walletCreatedSubject } = remote.require('./startup/sync-block-task/params')
@@ -24,6 +25,10 @@ export const switchNetwork = async (nodeURL: string, genesisBlockHash: string, _
await indexerQueue.stopAndWait()
}
+ // clean LockUtils info and DaoUtils info
+ LockUtils.cleanInfo()
+ DaoUtils.cleanInfo()
+
// disconnect old connection and connect to new database
await initConnection(genesisBlockHash)
// load lockHashes
diff --git a/packages/neuron-wallet/src/startup/sync-block-task/params.ts b/packages/neuron-wallet/src/startup/sync-block-task/params.ts
index 7339c00e94..0c31a686ea 100644
--- a/packages/neuron-wallet/src/startup/sync-block-task/params.ts
+++ b/packages/neuron-wallet/src/startup/sync-block-task/params.ts
@@ -3,8 +3,7 @@ import { AddressesUsedSubject } from 'models/subjects/addresses-used-subject'
import AddressDbChangedSubject from 'models/subjects/address-db-changed-subject'
import WalletCreatedSubject from 'models/subjects/wallet-created-subject'
import AddressCreatedSubject from 'models/subjects/address-created-subject'
-
-export { networkSwitchSubject } from 'services/networks'
+import NetworkSwitchSubject from 'models/subjects/network-switch-subject'
export { genesisBlockHash } from './create'
export { databaseInitSubject } from './create'
@@ -14,3 +13,4 @@ export const addressesUsedSubject = AddressesUsedSubject.getSubject()
export const addressDbChangedSubject = AddressDbChangedSubject.getSubject()
export const walletCreatedSubject = WalletCreatedSubject.getSubject()
export const addressCreatedSubject = AddressCreatedSubject.getSubject()
+export const networkSwitchSubject = NetworkSwitchSubject.getSubject()
diff --git a/packages/neuron-wallet/src/startup/sync-block-task/sync.ts b/packages/neuron-wallet/src/startup/sync-block-task/sync.ts
index e0a05985a1..799332d83a 100644
--- a/packages/neuron-wallet/src/startup/sync-block-task/sync.ts
+++ b/packages/neuron-wallet/src/startup/sync-block-task/sync.ts
@@ -4,6 +4,7 @@ import LockUtils from 'models/lock-utils'
import BlockListener from 'services/sync/block-listener'
import { Address } from 'database/address/address-dao'
import initConnection from 'database/chain/ormconfig'
+import DaoUtils from 'models/dao-utils'
const { nodeService, addressCreatedSubject, walletCreatedSubject } = remote.require('./startup/sync-block-task/params')
@@ -31,6 +32,10 @@ export const switchNetwork = async (url: string, genesisBlockHash: string, _chai
await blockListener.stopAndWait()
}
+ // clean LockUtils info and DaoUtils info
+ LockUtils.cleanInfo()
+ DaoUtils.cleanInfo()
+
// disconnect old connection and connect to new database
await initConnection(genesisBlockHash)
// load lockHashes
@@ -79,4 +84,4 @@ export const switchNetwork = async (url: string, genesisBlockHash: string, _chai
})
blockListener.start()
-}
\ No newline at end of file
+}
diff --git a/packages/neuron-wallet/src/startup/sync-block-task/task.ts b/packages/neuron-wallet/src/startup/sync-block-task/task.ts
index 31c0226e0f..0638917d53 100644
--- a/packages/neuron-wallet/src/startup/sync-block-task/task.ts
+++ b/packages/neuron-wallet/src/startup/sync-block-task/task.ts
@@ -8,11 +8,14 @@ import Utils from 'services/sync/utils'
import { switchNetwork as syncSwitchNetwork } from './sync'
import { switchNetwork as indexerSwitchNetwork } from './indexer'
import { DatabaseInitParams } from './create'
+import AddressCreatedSubject from 'models/subjects/address-created-subject'
// register to listen address updates
registerAddressListener()
-const { addressesUsedSubject, databaseInitSubject } = remote.require('./startup/sync-block-task/params')
+const { addressesUsedSubject, databaseInitSubject, addressCreatedSubject } = remote.require('./startup/sync-block-task/params')
+
+AddressCreatedSubject.setSubject(addressCreatedSubject)
// pass to task a main process subject
AddressesUsedSubject.setSubject(addressesUsedSubject)
diff --git a/packages/neuron-wallet/tests/services/tx/indexer-transaction.test.ts b/packages/neuron-wallet/tests/services/tx/indexer-transaction.test.ts
new file mode 100644
index 0000000000..b7d3f58ddf
--- /dev/null
+++ b/packages/neuron-wallet/tests/services/tx/indexer-transaction.test.ts
@@ -0,0 +1,108 @@
+import TransactionEntity from '../../../src/database/chain/entities/transaction'
+import OutputEntity from '../../../src/database/chain/entities/output'
+import InputEntity from '../../../src/database/chain/entities/input'
+import { getConnection } from 'typeorm'
+import { TransactionStatus, ScriptHashType } from '../../../src/types/cell-types'
+import initConnection from '../../../src/database/chain/ormconfig'
+import IndexerTransaction from '../../../src/services/tx/indexer-transaction'
+import { OutputStatus } from '../../../src/services/tx/params'
+
+describe('IndexerTransaction', () => {
+ const tx1: TransactionEntity = new TransactionEntity()
+ tx1.hash = '0x0001'
+ tx1.version = '0x0'
+ tx1.cellDeps = []
+ tx1.headerDeps = []
+ tx1.witnesses = []
+ tx1.timestamp = (+new Date()).toString()
+ tx1.blockNumber = '0x1'
+ tx1.blockHash = '0x000001'
+ tx1.status = TransactionStatus.Success
+ tx1.inputs = []
+ tx1.outputs = []
+
+ const tx1Input = new InputEntity()
+ tx1Input.outPointTxHash = '0x0000'
+ tx1Input.outPointIndex = '0'
+ tx1Input.since = '0'
+ tx1Input.lockHash = '0x'
+ tx1Input.capacity = '100'
+ tx1.inputs.push(tx1Input)
+
+ const tx1Output: OutputEntity = new OutputEntity()
+ tx1Output.capacity = '100000000000'
+ tx1Output.outPointTxHash = tx1.hash
+ tx1Output.outPointIndex = '0'
+ tx1Output.lock = {"args":"0x94c1720fdff98d1c0100cfb0e7ae42e470b53019","codeHash":"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8","hashType":"type" as ScriptHashType}
+ tx1Output.status = 'live'
+ tx1Output.lockHash = '0xb58a6fa6f2c57ed45fbdfc7fcebd5c79575590ecf190b72fa6e6a767d57cb105'
+ tx1Output.hasData = false
+
+ tx1.outputs.push(tx1Output)
+
+
+ const tx2 = new TransactionEntity()
+ tx2.hash = '0x0002'
+ tx2.version = '0x0'
+ tx2.cellDeps = []
+ tx2.headerDeps = []
+ tx2.witnesses = []
+ tx2.timestamp = (+new Date()).toString()
+ tx2.blockNumber = '0x2'
+ tx2.blockHash = '0x000002'
+ tx2.status = TransactionStatus.Success
+ tx2.inputs = []
+ tx2.outputs = []
+
+ const tx2Input = new InputEntity()
+ tx2Input.outPointTxHash = tx1.hash
+ tx2Input.outPointIndex = '0'
+ tx2Input.since = '0'
+ tx2Input.lockHash = null // tx1Output.lockHash
+ tx2Input.capacity = null // tx1Output.capacity
+ tx2.inputs.push(tx2Input)
+
+ const tx2Output = new OutputEntity()
+ tx2Output.capacity = '50000000000'
+ tx2Output.outPointTxHash = tx2.hash
+ tx2Output.outPointIndex = '0'
+ tx2Output.lock = {"args":"0x94c1720fdff98d1c0100cfb0e7ae42e470b53019","codeHash":"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8","hashType":"type" as ScriptHashType}
+ tx2Output.status = 'live'
+ tx2Output.lockHash = '0xb58a6fa6f2c57ed45fbdfc7fcebd5c79575590ecf190b72fa6e6a767d57cb105'
+ tx2Output.hasData = false
+ tx2.outputs.push(tx2Output)
+
+ beforeAll(async () => {
+ await initConnection('0x1234')
+ })
+
+ afterAll(async () => {
+ await getConnection().close()
+ })
+
+ it('updateInputLockHash', async () => {
+ await getConnection().manager.save([tx1, tx1Input, tx1Output, tx2, tx2Input, tx2Output])
+
+ await IndexerTransaction.updateInputLockHash(tx2Input.outPointTxHash!, tx2Input.outPointIndex!)
+
+ await tx1Output.reload()
+ expect(tx1Output.status).toEqual(OutputStatus.Dead)
+ await tx2Input.reload()
+ expect(tx2Input.lockHash).toEqual(tx1Output.lockHash)
+ expect(tx2Input.capacity).toEqual(tx1Output.capacity)
+
+ const inputCount: number = await getConnection()
+ .getRepository(InputEntity)
+ .createQueryBuilder('input')
+ .getCount()
+
+ expect(inputCount).toEqual(2)
+
+ const outputCount: number = await getConnection()
+ .getRepository(OutputEntity)
+ .createQueryBuilder('output')
+ .getCount()
+
+ expect(outputCount).toEqual(2)
+ })
+})
diff --git a/packages/neuron-wallet/tests/services/wallets.test.ts b/packages/neuron-wallet/tests/services/wallets.test.ts
index 0478b44101..7ffc0bbc03 100644
--- a/packages/neuron-wallet/tests/services/wallets.test.ts
+++ b/packages/neuron-wallet/tests/services/wallets.test.ts
@@ -3,6 +3,7 @@ import Keystore from '../../src/models/keys/keystore'
import Keychain from '../../src/models/keys/keychain'
import { mnemonicToSeedSync } from '../../src/models/keys/mnemonic'
import { ExtendedPrivateKey, AccountExtendedPublicKey } from '../../src/models/keys/key'
+import Core from '@nervosnetwork/ckb-sdk-core'
describe('wallet service', () => {
let walletService: WalletService
@@ -164,14 +165,21 @@ describe('wallet service', () => {
})
describe('sign witness', () => {
- const witness: string = ''
+ const witness = {
+ lock: undefined,
+ inputType: undefined,
+ outputType: undefined,
+ }
const privateKey: string = '0xe79f3207ea4980b7fed79956d5934249ceac4751a4fae01a0f7c4a96884bc4e3'
const txHash = '0x00f5f31941964004d665a8762df8eb4fab53b5ef8437b7d34a38e018b1409054'
- const expectedData = '0x5500000010000000550000005500000041000000aa6de884b0dd0378383cedddc39790b5cad66e42d5dc7655de728ee7eb3a53be071605d76641ad26766c6ed4864e67dbc2cd1526e006c9be7ccfa9b8cbf9e7c701'
+ const expectedData = ['0x5500000010000000550000005500000041000000aa6de884b0dd0378383cedddc39790b5cad66e42d5dc7655de728ee7eb3a53be071605d76641ad26766c6ed4864e67dbc2cd1526e006c9be7ccfa9b8cbf9e7c701']
it('success', () => {
- const wallet = new WalletService()
- const newWitness = wallet.signWitness(witness, privateKey, txHash)
+ const core = new Core('')
+ const newWitness = core.signWitnesses(privateKey)({
+ witnesses: [witness],
+ transactionHash: txHash
+ })
expect(newWitness).toEqual(expectedData)
})
})
diff --git a/yarn.lock b/yarn.lock
index f766729822..1090fbfeb1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2059,36 +2059,36 @@
call-me-maybe "^1.0.1"
glob-to-regexp "^0.3.0"
-"@nervosnetwork/ckb-sdk-core@0.25.0-alpha.0":
- version "0.25.0-alpha.0"
- resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-core/-/ckb-sdk-core-0.25.0-alpha.0.tgz#55d3286edffa01cc29b6a1e05de07b46fcd5f605"
- integrity sha512-7ygYqDYkdRYxn1gEWmQC1yYFvWCrNA6czsxgZwPI9LQdvzemMk+CgotXhT7uf5/zKTgrDDwFMTixqdkPMlL7Mw==
+"@nervosnetwork/ckb-sdk-core@0.25.0":
+ version "0.25.0"
+ resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-core/-/ckb-sdk-core-0.25.0.tgz#0a9852f78272297f39f3a722d8ab59c24d9afe7e"
+ integrity sha512-tD/mKge4zd56jOqvgeWb4703HOSF8ELhFpg0ieHhkV2uMoqqdW9eHyhdIisxPpW/y//RiS2Vxra8AL4uVBwRjQ==
dependencies:
- "@nervosnetwork/ckb-sdk-rpc" "0.25.0-alpha.0"
- "@nervosnetwork/ckb-sdk-utils" "0.25.0-alpha.0"
- "@nervosnetwork/ckb-types" "0.25.0-alpha.0"
+ "@nervosnetwork/ckb-sdk-rpc" "0.25.0"
+ "@nervosnetwork/ckb-sdk-utils" "0.25.0"
+ "@nervosnetwork/ckb-types" "0.25.0"
-"@nervosnetwork/ckb-sdk-rpc@0.25.0-alpha.0":
- version "0.25.0-alpha.0"
- resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-rpc/-/ckb-sdk-rpc-0.25.0-alpha.0.tgz#8f5009c8c67ef9e720ece0fb7c07b96f39f5af93"
- integrity sha512-ZefPXjKHBDaESvk1upCarTnGvDf8amsa6D8Xhaam02OVGUnTxt5n6WteniBQAsrORmI69PE5bfHesYo8sQRQVQ==
+"@nervosnetwork/ckb-sdk-rpc@0.25.0":
+ version "0.25.0"
+ resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-rpc/-/ckb-sdk-rpc-0.25.0.tgz#221cebe82da5c378c2e82fe8d8ca385f9ad4ca27"
+ integrity sha512-YwUmFd5YOR7OfWE0qxG8VWAmjf/lHxtW5U5ezYDecYV0Ey4qV70c/+yu3+o3koxSZwmZb2bJaEu5u4pXalyeHA==
dependencies:
- "@nervosnetwork/ckb-sdk-utils" "0.25.0-alpha.0"
+ "@nervosnetwork/ckb-sdk-utils" "0.25.0"
axios "0.19.0"
-"@nervosnetwork/ckb-sdk-utils@0.25.0-alpha.0":
- version "0.25.0-alpha.0"
- resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-utils/-/ckb-sdk-utils-0.25.0-alpha.0.tgz#8bce9f30b38ebe7776e756defb9f9111c7cfa8ad"
- integrity sha512-VqXcC+C9LrOni+ljyripbzl2fAf6ILHFz//rJqLkWCDeAeT3kGs4xVBCAHRGbAetI9wyTg3+n6BuSLOtzT5goQ==
+"@nervosnetwork/ckb-sdk-utils@0.25.0":
+ version "0.25.0"
+ resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-utils/-/ckb-sdk-utils-0.25.0.tgz#cc4708ecad678efc069d46aff5fcbecbd0f3a7fb"
+ integrity sha512-fFQBcnQTvvp7eLblY0ylZQaFWrIE7d/r+T/K7EAAG+XVdQhQXDJ6sb59kz7l0MWI/j4g9X5wZiFx/65VjotAJw==
dependencies:
- "@nervosnetwork/ckb-types" "0.25.0-alpha.0"
+ "@nervosnetwork/ckb-types" "0.25.0"
blake2b-wasm "1.1.7"
elliptic "6.5.1"
-"@nervosnetwork/ckb-types@0.25.0-alpha.0":
- version "0.25.0-alpha.0"
- resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-types/-/ckb-types-0.25.0-alpha.0.tgz#7aafb57e99525d54f632601055fb66d4f3f0f9b1"
- integrity sha512-xtSCjJkQjvuMcDqoMRCBtVc8W5jcsMlSwi7hAmDp5CIwNlMnU+xeUCTdnHX2Kt8vijhtUcIH90uEsdtzAPpWjQ==
+"@nervosnetwork/ckb-types@0.25.0":
+ version "0.25.0"
+ resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-types/-/ckb-types-0.25.0.tgz#8e8292e672954420b1068c0a99b7b652487a9ff9"
+ integrity sha512-KSrewk0Nz2LEUPJQOjGYl6maoHxDwsCpAMoN8gCznyei77BiD6k8Mq9xh+XoetZszk4SCjttYMUWEP+z/Lvmjw==
"@nodelib/fs.scandir@2.1.3":
version "2.1.3"
@@ -2920,10 +2920,10 @@
"@types/prop-types" "*"
"@types/react" "*"
-"@types/react-router-dom@4.3.5":
- version "4.3.5"
- resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.5.tgz#72f229967690c890d00f96e6b85e9ee5780db31f"
- integrity sha512-eFajSUASYbPHg2BDM1G8Btx+YqGgvROPIg6sBhl3O4kbDdYXdFdfrgQFf/pcBuQVObjfT9AL/dd15jilR5DIEA==
+"@types/react-router-dom@5.1.3":
+ version "5.1.3"
+ resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.3.tgz#b5d28e7850bd274d944c0fbbe5d57e6b30d71196"
+ integrity sha512-pCq7AkOvjE65jkGS5fQwQhvUp4+4PVD9g39gXLZViP2UqFiFzsEpB3PKf0O6mdbKsewSK8N14/eegisa/0CwnA==
dependencies:
"@types/history" "*"
"@types/react" "*"
@@ -14070,23 +14070,23 @@ react-resize-detector@^4.0.5:
raf-schd "^4.0.2"
resize-observer-polyfill "^1.5.1"
-react-router-dom@5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be"
- integrity sha512-zaVHSy7NN0G91/Bz9GD4owex5+eop+KvgbxXsP/O+iW1/Ln+BrJ8QiIR5a6xNPtrdTvLkxqlDClx13QO1uB8CA==
+react-router-dom@5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18"
+ integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
loose-envify "^1.3.1"
prop-types "^15.6.2"
- react-router "5.0.1"
+ react-router "5.1.2"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
-react-router@5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.0.1.tgz#04ee77df1d1ab6cb8939f9f01ad5702dbadb8b0f"
- integrity sha512-EM7suCPNKb1NxcTZ2LEOWFtQBQRQXecLxVpdsP4DW4PbbqYWeRiLyV/Tt1SdCrvT2jcyXAXmVTmzvSzrPR63Bg==
+react-router@5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418"
+ integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"