From 5012cbf2940da685d3e84f083c51c6d12fd85a6a Mon Sep 17 00:00:00 2001 From: classicalliu Date: Wed, 23 Oct 2019 14:56:16 +0800 Subject: [PATCH 01/33] chore: bump typeorm to 0.2.20 --- packages/neuron-wallet/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/neuron-wallet/package.json b/packages/neuron-wallet/package.json index 78109fab77..20425379cc 100644 --- a/packages/neuron-wallet/package.json +++ b/packages/neuron-wallet/package.json @@ -47,7 +47,7 @@ "rxjs": "6.5.3", "sha3": "2.0.7", "sqlite3": "4.1.0", - "typeorm": "0.2.19", + "typeorm": "0.2.20", "uuid": "3.3.3" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 210003eb60..6977c3373c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16613,10 +16613,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typeorm@0.2.19: - version "0.2.19" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.19.tgz#a0cff0714180e5720df157df02c5759a1a646dc3" - integrity sha512-xKVx/W41zckQ7v8WYcpRhSKpjXDKG/Jgjy0RWvYelR8ZnfyblNRL12jF4P8tIhwXv6l5t01s7HEc9lR+zb6Gtg== +typeorm@0.2.20: + version "0.2.20" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.20.tgz#efb60f2e55a7d08fc365f281ec2a71c87a9ebba5" + integrity sha512-VxB+9qH8D+PM19MIx18Zs3Fqv/ZINnnQvUGmBEiLYDrB9etdSdamgSTCIhWdFNndeJ6ldH4jbD0Z6HWsepMPlA== dependencies: app-root-path "^2.0.1" buffer "^5.1.0" From dc415f34edf61122703b56ec997857b6a6d09384 Mon Sep 17 00:00:00 2001 From: Keith Date: Wed, 23 Oct 2019 17:55:26 +0800 Subject: [PATCH 02/33] feat(neuron-ui): update the Send View according to the new transaction fee model. --- .../src/components/PasswordRequest/index.tsx | 12 +--- .../neuron-ui/src/components/Send/hooks.ts | 63 +++++++++---------- .../neuron-ui/src/components/Send/index.tsx | 11 +--- .../src/components/Transaction/index.tsx | 9 ++- .../components/TransactionFeePanel/index.tsx | 52 +++++++-------- .../src/services/remote/apiMethodWrapper.ts | 6 +- packages/neuron-ui/src/services/remote/app.ts | 6 +- .../neuron-ui/src/services/remote/networks.ts | 10 +-- .../neuron-ui/src/services/remote/wallets.ts | 16 ++--- .../neuron-ui/src/states/initStates/app.ts | 4 +- .../stateProvider/actionCreators/wallets.ts | 9 ++- .../src/states/stateProvider/reducer.ts | 15 ++--- packages/neuron-ui/src/types/App/index.d.ts | 2 +- .../neuron-ui/src/types/Controller/index.d.ts | 15 +++-- packages/neuron-ui/src/utils/calculateFee.ts | 15 +++++ packages/neuron-ui/src/utils/formatters.ts | 9 --- packages/neuron-wallet/src/controllers/api.ts | 5 +- .../neuron-wallet/src/controllers/wallets.ts | 7 +-- .../tests-e2e/tests/sendTransaction.ts | 18 +++--- 19 files changed, 140 insertions(+), 144 deletions(-) create mode 100644 packages/neuron-ui/src/utils/calculateFee.ts diff --git a/packages/neuron-ui/src/components/PasswordRequest/index.tsx b/packages/neuron-ui/src/components/PasswordRequest/index.tsx index b892f50401..89a2885a44 100644 --- a/packages/neuron-ui/src/components/PasswordRequest/index.tsx +++ b/packages/neuron-ui/src/components/PasswordRequest/index.tsx @@ -4,11 +4,10 @@ import { useTranslation } from 'react-i18next' import { Stack, Text, Label, Modal, TextField, PrimaryButton, DefaultButton } from 'office-ui-fabric-react' import { StateWithDispatch, AppActions } from 'states/stateProvider/reducer' import { sendTransaction, deleteWallet, backupWallet } from 'states/stateProvider/actionCreators' -import { priceToFee, CKBToShannonFormatter } from 'utils/formatters' const PasswordRequest = ({ app: { - send: { txID, outputs, description, price, cycles }, + send: { description, generatedTx }, loadings: { sending: isSending = false }, passwordRequest: { walletID = '', actionType = null, password = '' }, }, @@ -32,15 +31,10 @@ const PasswordRequest = ({ break } sendTransaction({ - id: txID, walletID, - items: outputs.map(output => ({ - address: output.address, - capacity: CKBToShannonFormatter(output.amount, output.unit), - })), + tx: generatedTx, description, password, - fee: priceToFee(price, cycles), })(dispatch, history) break } @@ -62,7 +56,7 @@ const PasswordRequest = ({ break } } - }, [dispatch, walletID, password, actionType, txID, description, outputs, cycles, price, history, isSending]) + }, [dispatch, walletID, password, actionType, description, history, isSending, generatedTx]) const onChange = useCallback( (_e, value?: string) => { diff --git a/packages/neuron-ui/src/components/Send/hooks.ts b/packages/neuron-ui/src/components/Send/hooks.ts index 6263d4a792..1f1bad2e5e 100644 --- a/packages/neuron-ui/src/components/Send/hooks.ts +++ b/packages/neuron-ui/src/components/Send/hooks.ts @@ -1,15 +1,16 @@ import React, { useState, useCallback, useEffect, useMemo } from 'react' import { AppActions, StateDispatch } from 'states/stateProvider/reducer' -import { calculateCycles } from 'services/remote/wallets' +import { generateTx } from 'services/remote/wallets' -import { outputsToTotalAmount, priceToFee } from 'utils/formatters' +import { outputsToTotalAmount, CKBToShannonFormatter } from 'utils/formatters' import { verifyAddress, verifyAmount, verifyAmountRange, verifyTransactionOutputs } from 'utils/validators' -import { ErrorCode } from 'utils/const' -import { MAX_DECIMAL_DIGITS } from '../../utils/const' +import { ErrorCode, MAX_DECIMAL_DIGITS } from 'utils/const' +import calculateFee from 'utils/calculateFee' + import { TransactionOutput } from '.' -let cyclesTimer: ReturnType +let generateTxTimer: ReturnType const useUpdateTransactionOutput = (dispatch: StateDispatch) => useCallback( @@ -49,49 +50,47 @@ const useRemoveTransactionOutput = (dispatch: StateDispatch) => const useOnTransactionChange = ( walletID: string, items: TransactionOutput[], + price: string, dispatch: StateDispatch, setIsTransactionValid: Function, setTotalAmount: Function ) => { useEffect(() => { - clearTimeout(cyclesTimer) - cyclesTimer = setTimeout(() => { + clearTimeout(generateTxTimer) + generateTxTimer = setTimeout(() => { + dispatch({ + type: AppActions.UpdateGeneratedTx, + payload: null, + }) if (verifyTransactionOutputs(items)) { setIsTransactionValid(true) const totalAmount = outputsToTotalAmount(items) setTotalAmount(totalAmount) - calculateCycles({ + const realParams = { walletID, - capacities: totalAmount, - }) - .then(response => { - if (response.status) { - if (Number.isNaN(+response.result)) { - throw new Error('Invalid Cycles') - } + items: items.map(item => ({ + address: item.address, + capacity: CKBToShannonFormatter(item.amount, item.unit), + })), + feeRate: price, + } + generateTx(realParams) + .then((res: any) => { + if (res.status === 1) { dispatch({ - type: AppActions.UpdateSendCycles, - payload: response.result, + type: AppActions.UpdateGeneratedTx, + payload: res.result, }) - } else { - throw new Error('Cycles Not Calculated') } }) - .catch(() => { - dispatch({ - type: AppActions.UpdateSendCycles, - payload: '0', - }) + .catch((err: Error) => { + console.error(err) }) } else { setIsTransactionValid(false) - dispatch({ - type: AppActions.UpdateSendCycles, - payload: '0', - }) } }, 300) - }, [walletID, items, dispatch, setIsTransactionValid, setTotalAmount]) + }, [walletID, items, price, dispatch, setIsTransactionValid, setTotalAmount]) } const useOnSubmit = (items: TransactionOutput[], dispatch: StateDispatch) => @@ -170,12 +169,12 @@ const useClear = (dispatch: StateDispatch) => useCallback(() => clear(dispatch), export const useInitialize = ( items: TransactionOutput[], - price: string, - cycles: string, + generatedTx: any | null, dispatch: React.Dispatch, t: any ) => { - const fee = useMemo(() => priceToFee(price, cycles), [price, cycles]) // in shannon + const fee = useMemo(() => calculateFee(generatedTx), [generatedTx]) + const [isTransactionValid, setIsTransactionValid] = useState(false) const [totalAmount, setTotalAmount] = useState('0') diff --git a/packages/neuron-ui/src/components/Send/index.tsx b/packages/neuron-ui/src/components/Send/index.tsx index f6a23e66b5..6784299e9a 100644 --- a/packages/neuron-ui/src/components/Send/index.tsx +++ b/packages/neuron-ui/src/components/Send/index.tsx @@ -57,8 +57,8 @@ const Send = ({ onGetAddressErrorMessage, onGetAmountErrorMessage, onClear, - } = useInitialize(send.outputs, send.price, send.cycles, dispatch, t) - useOnTransactionChange(walletID, send.outputs, dispatch, setIsTransactionValid, setTotalAmount) + } = useInitialize(send.outputs, send.generatedTx, dispatch, t) + useOnTransactionChange(walletID, send.outputs, send.price, dispatch, setIsTransactionValid, setTotalAmount) const leftStackWidth = '70%' const labelWidth = '140px' @@ -211,12 +211,7 @@ const Send = ({ - + diff --git a/packages/neuron-ui/src/components/Transaction/index.tsx b/packages/neuron-ui/src/components/Transaction/index.tsx index 4d07dcbdf8..54b83c0b54 100644 --- a/packages/neuron-ui/src/components/Transaction/index.tsx +++ b/packages/neuron-ui/src/components/Transaction/index.tsx @@ -210,8 +210,15 @@ const Transaction = () => { const currentWallet = currentWalletCache.load() if (currentWallet) { const hash = window.location.href.split('/').pop() + if (!hash) { + showErrorMessage( + t(`messages.error`), + t(`messages.codes.${ErrorCode.FieldNotFound}`, { fieldName: 'transaction hash' }) + ) + return + } getTransaction({ hash, walletID: currentWallet.id }) - .then(res => { + .then((res: any) => { if (res.status) { setTransaction(res.result) } else { diff --git a/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx b/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx index 948b395ce2..a37d689ad8 100644 --- a/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx +++ b/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx @@ -4,30 +4,24 @@ import { useTranslation } from 'react-i18next' interface TransactionFee { fee: string - cycles: string price: string onPriceChange: any } const calculateSpeed = (price: number) => { - if (price >= 160) { - return '180' + if (price >= 16000) { + return '18000' } - if (price >= 40) { - return '60' + if (price >= 4000) { + return '6000' } - if (price >= 20) { - return '30' + if (price >= 2000) { + return '3000' } return '0' } -const TransactionFee: React.FunctionComponent = ({ - cycles, - price, - fee, - onPriceChange, -}: TransactionFee) => { +const TransactionFee: React.FunctionComponent = ({ price, fee, onPriceChange }: TransactionFee) => { const [t] = useTranslation() const [showDetail, setShowDetail] = useState(false) const leftStackWidth = '70%' @@ -48,7 +42,18 @@ const TransactionFee: React.FunctionComponent = ({ - + {actionSpacer} @@ -79,7 +84,7 @@ const TransactionFee: React.FunctionComponent = ({ - + {actionSpacer} @@ -93,10 +98,10 @@ const TransactionFee: React.FunctionComponent = ({ dropdownWidth={140} selectedKey={selectedSpeed} options={[ - { key: '180', text: 'immediately' }, - { key: '60', text: '~ 30s' }, - { key: '30', text: '~ 1min' }, - { key: '0', text: '~ 3min' }, + { key: '18000', text: 'immediately' }, + { key: '6000', text: '~ 10 blocks' }, + { key: '3000', text: '~ 100 blocks' }, + { key: '0', text: '~ 500 blocks' }, ]} onRenderCaretDown={() => { return @@ -110,15 +115,6 @@ const TransactionFee: React.FunctionComponent = ({ /> - - - - - - - {cycles} - - ) diff --git a/packages/neuron-ui/src/services/remote/apiMethodWrapper.ts b/packages/neuron-ui/src/services/remote/apiMethodWrapper.ts index ddf5a26940..c6623c23d7 100644 --- a/packages/neuron-ui/src/services/remote/apiMethodWrapper.ts +++ b/packages/neuron-ui/src/services/remote/apiMethodWrapper.ts @@ -21,17 +21,17 @@ export const RemoteNotLoadError = { }, } -export const apiMethodWrapper = ( +export const apiMethodWrapper = ( callControllerMethod: ( controller: any ) => ( - params: any + params: T ) => Promise<{ status: any result: any message: { code?: number; content?: string; meta?: { [key: string]: string } } }> -) => async (realParams?: any): Promise => { +) => async (realParams: T): Promise => { if (!window.remote) { return RemoteNotLoadError } diff --git a/packages/neuron-ui/src/services/remote/app.ts b/packages/neuron-ui/src/services/remote/app.ts index 5a7856d64a..6b31ba7645 100644 --- a/packages/neuron-ui/src/services/remote/app.ts +++ b/packages/neuron-ui/src/services/remote/app.ts @@ -1,11 +1,11 @@ import { apiMethodWrapper } from './apiMethodWrapper' -export const getNeuronWalletState = apiMethodWrapper(controller => () => controller.loadInitData()) +export const getNeuronWalletState = apiMethodWrapper(controller => () => controller.loadInitData()) -export const handleViewError = apiMethodWrapper(controller => (errorMessage: string) => +export const handleViewError = apiMethodWrapper(controller => errorMessage => controller.handleViewError(errorMessage) ) -export const contextMenu = apiMethodWrapper(controller => (params: { type: string; id: string }) => +export const contextMenu = apiMethodWrapper<{ type: string; id: string }>(controller => params => controller.contextMenu(params) ) diff --git a/packages/neuron-ui/src/services/remote/networks.ts b/packages/neuron-ui/src/services/remote/networks.ts index 9ae86fe729..f45ef7382f 100644 --- a/packages/neuron-ui/src/services/remote/networks.ts +++ b/packages/neuron-ui/src/services/remote/networks.ts @@ -1,22 +1,22 @@ import { apiMethodWrapper } from './apiMethodWrapper' -export const setCurrentNetowrk = apiMethodWrapper((api: any) => (networkID: string) => { +export const setCurrentNetowrk = apiMethodWrapper(api => networkID => { return api.setCurrentNetowrk(networkID) }) -export const createNetwork = apiMethodWrapper(api => (params: Controller.CreateNetworkParams) => { +export const createNetwork = apiMethodWrapper(api => params => { return api.createNetwork(params) }) -export const updateNetwork = apiMethodWrapper(api => ({ networkID, options }: Controller.UpdateNetworkParams) => { +export const updateNetwork = apiMethodWrapper(api => ({ networkID, options }) => { return api.updateNetwork(networkID, options) }) -export const getAllNetworks = apiMethodWrapper(api => () => { +export const getAllNetworks = apiMethodWrapper(api => () => { return api.getAllNetworks() }) -export const getCurrentNetworkID = apiMethodWrapper(api => () => { +export const getCurrentNetworkID = apiMethodWrapper(api => () => { return api.getCurrentNetworkID() }) diff --git a/packages/neuron-ui/src/services/remote/wallets.ts b/packages/neuron-ui/src/services/remote/wallets.ts index 594f7d8870..2ab31713d1 100644 --- a/packages/neuron-ui/src/services/remote/wallets.ts +++ b/packages/neuron-ui/src/services/remote/wallets.ts @@ -2,9 +2,9 @@ import { apiMethodWrapper } from './apiMethodWrapper' export const updateWallet = apiMethodWrapper(api => (params: Controller.UpdateWalletParams) => api.updateWallet(params)) -export const getCurrentWallet = apiMethodWrapper(api => () => api.getCurrentWallet()) +export const getCurrentWallet = apiMethodWrapper(api => () => api.getCurrentWallet()) -export const getWalletList = apiMethodWrapper(api => () => api.getAllWallets()) +export const getWalletList = apiMethodWrapper(api => () => api.getAllWallets()) export const createWallet = apiMethodWrapper(api => (params: Controller.CreateWalletParams) => api.createWallet(params)) @@ -24,7 +24,11 @@ export const setCurrentWallet = apiMethodWrapper(api => (id: Controller.SetCurre api.setCurrentWallet(id) ) -export const sendCapacity = apiMethodWrapper(api => (params: Controller.SendTransaction) => api.sendCapacity(params)) +export const generateTx = apiMethodWrapper(api => (params: Controller.GenerateTransactionParams) => + api.generateTx(params) +) + +export const sendTx = apiMethodWrapper(api => (params: Controller.SendTransactionParams) => api.sendTx(params)) export const getAddressesByWalletID = apiMethodWrapper(api => (walletID: Controller.GetAddressesByWalletIDParams) => api.getAddressesByWalletID(walletID) @@ -34,8 +38,6 @@ export const updateAddressDescription = apiMethodWrapper(api => (params: Control api.updateAddressDescription(params) ) -export const calculateCycles = apiMethodWrapper(api => (params: Controller.ComputeCycles) => api.computeCycles(params)) - export default { updateWallet, getWalletList, @@ -45,8 +47,8 @@ export default { deleteWallet, backupWallet, getCurrentWallet, - sendCapacity, - calculateCycles, + generateTx, + sendTx, getAddressesByWalletID, updateAddressDescription, } diff --git a/packages/neuron-ui/src/states/initStates/app.ts b/packages/neuron-ui/src/states/initStates/app.ts index c6eca66d88..c4532a26db 100644 --- a/packages/neuron-ui/src/states/initStates/app.ts +++ b/packages/neuron-ui/src/states/initStates/app.ts @@ -14,9 +14,9 @@ const appState: State.App = { unit: CapacityUnit.CKB, }, ], - price: '0', - cycles: '0', + price: '1000', description: '', + generatedTx: '', }, passwordRequest: { actionType: null, diff --git a/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts b/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts index 9aaf4757d9..4e59511fbf 100644 --- a/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts +++ b/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts @@ -7,7 +7,7 @@ import { getCurrentWallet, updateWallet, setCurrentWallet as setRemoteCurrentWallet, - sendCapacity, + sendTx, getAddressesByWalletID, updateAddressDescription as updateRemoteAddressDescription, deleteWallet as deleteRemoteWallet, @@ -141,7 +141,10 @@ export const setCurrentWallet = (id: string) => (dispatch: StateDispatch) => { }) } -export const sendTransaction = (params: Controller.SendTransaction) => (dispatch: StateDispatch, history: any) => { +export const sendTransaction = (params: Controller.SendTransactionParams) => ( + dispatch: StateDispatch, + history: any +) => { dispatch({ type: AppActions.UpdateLoadings, payload: { @@ -149,7 +152,7 @@ export const sendTransaction = (params: Controller.SendTransaction) => (dispatch }, }) setTimeout(() => { - sendCapacity(params) + sendTx(params) .then(res => { if (res.status === 1) { dispatch({ diff --git a/packages/neuron-ui/src/states/stateProvider/reducer.ts b/packages/neuron-ui/src/states/stateProvider/reducer.ts index a040a2eeb7..5f20b4d629 100644 --- a/packages/neuron-ui/src/states/stateProvider/reducer.ts +++ b/packages/neuron-ui/src/states/stateProvider/reducer.ts @@ -28,8 +28,8 @@ export enum AppActions { RemoveSendOutput = 'removeSendOutput', UpdateSendOutput = 'updateSendOutput', UpdateSendPrice = 'updateSendPrice', - UpdateSendCycles = 'updateSendCycles', UpdateSendDescription = 'updateSendDescription', + UpdateGeneratedTx = 'updateGeneratedTx', ClearSendState = 'clearSendState', UpdateMessage = 'updateMessage', AddNotification = 'addNotification', @@ -357,9 +357,9 @@ export const reducer = ( }, } } - case AppActions.UpdateSendCycles: { + case AppActions.UpdateSendDescription: { /** - * payload: new cycles + * payload: new description */ return { ...state, @@ -367,22 +367,19 @@ export const reducer = ( ...app, send: { ...app.send, - cycles: payload, + description: payload, }, }, } } - case AppActions.UpdateSendDescription: { - /** - * payload: new description - */ + case AppActions.UpdateGeneratedTx: { return { ...state, app: { ...app, send: { ...app.send, - description: payload, + generatedTx: payload || null, }, }, } diff --git a/packages/neuron-ui/src/types/App/index.d.ts b/packages/neuron-ui/src/types/App/index.d.ts index ac4fd95909..6fbf936369 100644 --- a/packages/neuron-ui/src/types/App/index.d.ts +++ b/packages/neuron-ui/src/types/App/index.d.ts @@ -63,8 +63,8 @@ declare namespace State { txID: string outputs: Output[] price: string - cycles: string description: string + generatedTx: any | null } interface Popup { diff --git a/packages/neuron-ui/src/types/Controller/index.d.ts b/packages/neuron-ui/src/types/Controller/index.d.ts index 5f1f5ae489..b7d56a6a98 100644 --- a/packages/neuron-ui/src/types/Controller/index.d.ts +++ b/packages/neuron-ui/src/types/Controller/index.d.ts @@ -33,16 +33,21 @@ declare namespace Controller { } type SetCurrentWalletParams = string - interface SendTransaction { - id: string + + interface SendTransactionParams { + walletID: string + tx: string + password: string + description?: string + } + + interface GenerateTransactionParams { walletID: string items: { address: string capacity: string }[] - password: string - fee: string - description: string + feeRate: string } interface ComputeCycles { diff --git a/packages/neuron-ui/src/utils/calculateFee.ts b/packages/neuron-ui/src/utils/calculateFee.ts new file mode 100644 index 0000000000..e36fa958e5 --- /dev/null +++ b/packages/neuron-ui/src/utils/calculateFee.ts @@ -0,0 +1,15 @@ +export default (tx: any) => { + if (!tx) { + return '0' + } + const inputCapacities = tx.inputs.reduce( + (result: bigint, input: { capacity: string }) => result + BigInt(input.capacity), + BigInt(0) + ) + const outputCapacities = tx.outputs.reduce( + (result: bigint, output: { capacity: string }) => result + BigInt(output.capacity), + BigInt(0) + ) + + return (inputCapacities - outputCapacities).toString() +} diff --git a/packages/neuron-ui/src/utils/formatters.ts b/packages/neuron-ui/src/utils/formatters.ts index 5b11497609..a39bbc2835 100644 --- a/packages/neuron-ui/src/utils/formatters.ts +++ b/packages/neuron-ui/src/utils/formatters.ts @@ -145,14 +145,6 @@ export const uniformTimeFormatter = (time: string | number | Date) => { return timeFormatter.format(+time).replace(/\//g, '-') } -export const priceToFee = (price: string, cycles: string) => { - if (Number.isNaN(+price)) { - console.warn(`Price is not a valid number`) - return `0` - } - return (BigInt(price) * BigInt(cycles)).toString() -} - export const addressesToBalance = (addresses: State.Address[] = []) => { return addresses .reduce((total, addr) => { @@ -191,7 +183,6 @@ export default { shannonToCKBFormatter, localNumberFormatter, uniformTimeFormatter, - priceToFee, addressesToBalance, outputsToTotalAmount, failureResToNotification, diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts index 9638e00c3f..4d54316abf 100644 --- a/packages/neuron-wallet/src/controllers/api.ts +++ b/packages/neuron-wallet/src/controllers/api.ts @@ -10,7 +10,7 @@ import { SyncInfoController, SkipDataAndTypeController, NetworksController - } from 'controllers' +} from 'controllers' import { NetworkType, NetworkID, Network } from 'types/network' import NetworksService from 'services/networks' import WalletsService from 'services/wallets' @@ -193,7 +193,6 @@ export default class ApiController { @CatchControllerError public static async sendTx(params: { - id: string walletID: string tx: TransactionWithoutHash, password: string @@ -204,7 +203,6 @@ export default class ApiController { @CatchControllerError public static async generateTx(params: { - id: string walletID: string items: { address: string @@ -218,7 +216,6 @@ export default class ApiController { @CatchControllerError public static async calculateFee(params: { - id: string tx: TransactionWithoutHash }) { return WalletsController.calculateFee(params) diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index eacaa5d865..0177c1e9c4 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -393,7 +393,6 @@ export default class WalletsController { @CatchControllerError public static async sendTx(params: { - id: string walletID: string tx: TransactionWithoutHash password: string @@ -425,7 +424,6 @@ export default class WalletsController { @CatchControllerError public static async generateTx(params: { - id: string walletID: string items: { address: string @@ -459,10 +457,7 @@ export default class WalletsController { } @CatchControllerError - public static async calculateFee(params: { - id: string - tx: TransactionWithoutHash - }) { + public static async calculateFee(params: { tx: TransactionWithoutHash }) { if (!params) { throw new IsRequired('Parameters') } diff --git a/packages/neuron-wallet/tests-e2e/tests/sendTransaction.ts b/packages/neuron-wallet/tests-e2e/tests/sendTransaction.ts index e179b240ce..468f880932 100644 --- a/packages/neuron-wallet/tests-e2e/tests/sendTransaction.ts +++ b/packages/neuron-wallet/tests-e2e/tests/sendTransaction.ts @@ -115,36 +115,36 @@ export default (app: Application) => { await app.waitUntilLoaded() }) - app.test('default price should be 0 and default speed should be 3min', async () => { + app.test('default price should be 1000 and default speed should be 500 blocks', async () => { const { client } = app.spectron const transactionFeePanel = client.$('div[aria-label="transaction fee"]') const [, priceField] = await transactionFeePanel.$$('input') - expect((await client.elementIdAttribute(priceField.value.ELEMENT, 'value')).value).toBe('0') + expect((await client.elementIdAttribute(priceField.value.ELEMENT, 'value')).value).toBe('1000') const speedDropdown = await client.$('div[role=listbox]') - expect((await client.elementIdAttribute(speedDropdown.value.ELEMENT, 'innerText')).value).toBe('~ 3min') + expect((await client.elementIdAttribute(speedDropdown.value.ELEMENT, 'innerText')).value).toBe('~ 500 blocks') }) - app.test('Change speed to immediately and the price should be 180', async () => { + app.test('Change speed to ~ 100 blocks and the price should be 3000', async () => { const { client } = app.spectron client.click('div[role=listbox]') await app.waitUntilLoaded() - client.click('button[title=immediately]') + client.click('button[title="~ 100 blocks"]') await app.waitUntilLoaded() const transactionFeePanel = client.$('div[aria-label="transaction fee"]') const [, priceField] = await transactionFeePanel.$$('input') - expect((await client.elementIdAttribute(priceField.value.ELEMENT, 'value')).value).toBe('180') + expect((await client.elementIdAttribute(priceField.value.ELEMENT, 'value')).value).toBe('3000') }) - app.test('Change the price to 150 and the speed should switch to ~ 30s', async () => { + app.test('Change the price to 100000 and the speed should switch to immediately', async () => { const { client } = app.spectron const transactionFeePanel = client.$('div[aria-label="transaction fee"]') const [, priceField] = await transactionFeePanel.$$('input') client.elementIdClear(priceField.value.ELEMENT) await app.waitUntilLoaded() - client.elementIdValue(priceField.value.ELEMENT, '150') + client.elementIdValue(priceField.value.ELEMENT, '00') const speedDropdown = await client.$('div[role=listbox]') - expect((await client.elementIdAttribute(speedDropdown.value.ELEMENT, 'innerText')).value).toBe('~ 30s') + expect((await client.elementIdAttribute(speedDropdown.value.ELEMENT, 'innerText')).value).toBe('immediately') }) }) } From 1d579bca71064ba78bb4a0f316d8b0c632155444 Mon Sep 17 00:00:00 2001 From: Keith Date: Wed, 23 Oct 2019 18:01:45 +0800 Subject: [PATCH 03/33] feat(neuron-wallet0: remove the calculate fee method from neuron-wallet --- packages/neuron-wallet/src/controllers/api.ts | 7 ------- .../neuron-wallet/src/controllers/wallets.ts | 21 ------------------- 2 files changed, 28 deletions(-) diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts index 4d54316abf..93d44130d0 100644 --- a/packages/neuron-wallet/src/controllers/api.ts +++ b/packages/neuron-wallet/src/controllers/api.ts @@ -214,13 +214,6 @@ export default class ApiController { return WalletsController.generateTx(params) } - @CatchControllerError - public static async calculateFee(params: { - tx: TransactionWithoutHash - }) { - return WalletsController.calculateFee(params) - } - @CatchControllerError public static async computeCycles(params: { walletID: string; capacities: string }) { return WalletsController.computeCycles(params) diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index 0177c1e9c4..00d0d0ce1b 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -456,27 +456,6 @@ export default class WalletsController { } } - @CatchControllerError - public static async calculateFee(params: { tx: TransactionWithoutHash }) { - if (!params) { - throw new IsRequired('Parameters') - } - try { - const walletsService = WalletsService.getInstance() - const fee = await walletsService.calculateFee(params.tx) - return { - status: ResponseCode.Success, - result: fee, - } - } catch (err) { - logger.error(`calculateFee:`, err) - return { - status: err.code || ResponseCode.Fail, - message: `Error: "${err.message}"`, - } - } - } - @CatchControllerError public static async computeCycles(params: { walletID: string; capacities: string }) { if (!params) { From 9aebede3e808012e32ede1689fac7d85ae0c4fd4 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 24 Oct 2019 00:00:42 +0800 Subject: [PATCH 04/33] feat(neuron-ui): display confirmations of pending transactions in the recent activity list --- packages/neuron-ui/src/components/CustomRows/ActivityRow.tsx | 4 ++-- packages/neuron-ui/src/components/Overview/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/neuron-ui/src/components/CustomRows/ActivityRow.tsx b/packages/neuron-ui/src/components/CustomRows/ActivityRow.tsx index 692d395c89..7b83f0be21 100644 --- a/packages/neuron-ui/src/components/CustomRows/ActivityRow.tsx +++ b/packages/neuron-ui/src/components/CustomRows/ActivityRow.tsx @@ -34,13 +34,13 @@ const ActivityRow = (props?: ActivityRowProps) => { return (
{`${typeLabel} ${shannonToCKBFormatter(value)} CKB`}
{statusLabel}
{time}
-
{status === 'success' ? confirmations : ''}
+
{confirmations}
) } diff --git a/packages/neuron-ui/src/components/Overview/index.tsx b/packages/neuron-ui/src/components/Overview/index.tsx index bbc376b1e3..8694c28e1f 100644 --- a/packages/neuron-ui/src/components/Overview/index.tsx +++ b/packages/neuron-ui/src/components/Overview/index.tsx @@ -118,7 +118,7 @@ const Overview = ({ const activityItems: ActivityItem[] = useMemo( () => items.map(item => { - let confirmations = '(-)' + let confirmations = '' let typeLabel: string = item.type let { status } = item if (item.blockNumber !== undefined) { @@ -147,7 +147,7 @@ const Overview = ({ status, statusLabel: t(`overview.statusLabel.${status}`), value: item.value.replace(/^-/, ''), - confirmations: item.status === 'success' ? confirmations : '', + confirmations: ['success', 'pending'].includes(item.status) ? confirmations : '', typeLabel: t(`overview.${typeLabel}`), } }), From 35d8b879067452f4b9e5d146777b28f77c30579b Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 24 Oct 2019 16:21:32 +0800 Subject: [PATCH 05/33] feat(neuron-ui): update history list to make it more compact --- .../src/components/CustomRows/GroupHeader.tsx | 27 ++++++++++++++ .../src/components/CustomRows/HistoryRow.tsx | 22 ++++++++++++ .../src/components/TransactionList/index.tsx | 36 +++++++------------ 3 files changed, 61 insertions(+), 24 deletions(-) create mode 100644 packages/neuron-ui/src/components/CustomRows/GroupHeader.tsx create mode 100644 packages/neuron-ui/src/components/CustomRows/HistoryRow.tsx diff --git a/packages/neuron-ui/src/components/CustomRows/GroupHeader.tsx b/packages/neuron-ui/src/components/CustomRows/GroupHeader.tsx new file mode 100644 index 0000000000..29d727052b --- /dev/null +++ b/packages/neuron-ui/src/components/CustomRows/GroupHeader.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import { Stack, Text, getTheme } from 'office-ui-fabric-react' + +const { + palette: { neutralLighterAlt, neutralSecondary, neutralLighter }, +} = getTheme() + +const GroupHeader = ({ group }: any) => { + const { name } = group + return ( + + {name} + + ) +} +export default GroupHeader diff --git a/packages/neuron-ui/src/components/CustomRows/HistoryRow.tsx b/packages/neuron-ui/src/components/CustomRows/HistoryRow.tsx new file mode 100644 index 0000000000..3a3e0a4539 --- /dev/null +++ b/packages/neuron-ui/src/components/CustomRows/HistoryRow.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import { DetailsRow, IDetailsRowProps } from 'office-ui-fabric-react' + +const HistoryRow = (props?: IDetailsRowProps) => { + return props ? ( + + ) : null +} + +export default HistoryRow diff --git a/packages/neuron-ui/src/components/TransactionList/index.tsx b/packages/neuron-ui/src/components/TransactionList/index.tsx index b3c34caf5c..174df1fbf0 100644 --- a/packages/neuron-ui/src/components/TransactionList/index.tsx +++ b/packages/neuron-ui/src/components/TransactionList/index.tsx @@ -1,17 +1,19 @@ import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { - Stack, - Text, ShimmeredDetailsList, TextField, IconButton, IColumn, IGroup, CheckboxVisibility, + CollapseAllVisibility, getTheme, } from 'office-ui-fabric-react' +import GroupHeader from 'components/CustomRows/GroupHeader' +import HistoryRow from 'components/CustomRows/HistoryRow' + import { StateDispatch } from 'states/stateProvider/reducer' import { contextMenu, showTransactionDetails } from 'services/remote' @@ -22,7 +24,6 @@ import { uniformTimeFormatter, localNumberFormatter, } from 'utils/formatters' -import { onRenderRow } from 'utils/fabricUIRender' import { CONFIRMATION_THRESHOLD } from 'utils/const' const theme = getTheme() @@ -32,24 +33,6 @@ interface FormatTransaction extends State.Transaction { date: string } -const onRenderHeader = ({ group }: any) => { - const { name } = group - return ( - - {name} - - ) -} - const TransactionList = ({ isLoading = false, items = [], @@ -185,6 +168,10 @@ const TransactionList = ({ borderless readOnly={!isSelected} styles={{ + root: { + flex: '1', + paddingRight: '15px', + }, fieldGroup: { backgroundColor: isSelected ? '#fff' : 'transparent', borderColor: 'transparent', @@ -268,9 +255,10 @@ const TransactionList = ({ enableShimmer={isLoading} columns={transactionColumns} items={txs} - groups={groups.filter(group => group.count !== 0)} + groups={groups} groupProps={{ - onRenderHeader, + collapseAllVisibility: CollapseAllVisibility.hidden, + onRenderHeader: GroupHeader, }} checkboxVisibility={CheckboxVisibility.hidden} onItemInvoked={item => { @@ -282,7 +270,7 @@ const TransactionList = ({ } }} className="listWithDesc" - onRenderRow={onRenderRow} + onRenderRow={HistoryRow} /> ) } From 9e52fef5b571cbb0ceb235652223814b4e2eda7a Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 25 Oct 2019 10:06:45 +0800 Subject: [PATCH 06/33] feat(neuron-ui): add a tooltip to display synchronized block number and the tip block number --- packages/neuron-ui/src/containers/Footer/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/neuron-ui/src/containers/Footer/index.tsx b/packages/neuron-ui/src/containers/Footer/index.tsx index 3cece43d72..be9ff24b00 100644 --- a/packages/neuron-ui/src/containers/Footer/index.tsx +++ b/packages/neuron-ui/src/containers/Footer/index.tsx @@ -34,7 +34,7 @@ export const SyncStatus = ({ const percentage = +syncedBlockNumber / +tipBlockNumber return ( -
+
{+syncedBlockNumber + bufferBlockNumber < +tipBlockNumber ? ( <> {t('sync.syncing')} From 273588b86124e3c1c38b10db6403c0fb5eecdcad Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 25 Oct 2019 10:11:06 +0800 Subject: [PATCH 07/33] refactor(neuron-ui): add spaces in the tooltip of synchornization status --- packages/neuron-ui/src/containers/Footer/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/neuron-ui/src/containers/Footer/index.tsx b/packages/neuron-ui/src/containers/Footer/index.tsx index be9ff24b00..1ac36af58f 100644 --- a/packages/neuron-ui/src/containers/Footer/index.tsx +++ b/packages/neuron-ui/src/containers/Footer/index.tsx @@ -34,7 +34,7 @@ export const SyncStatus = ({ const percentage = +syncedBlockNumber / +tipBlockNumber return ( -
+
{+syncedBlockNumber + bufferBlockNumber < +tipBlockNumber ? ( <> {t('sync.syncing')} From 5443c2ff1fe0d58b9cf94840b3ef74a8c706b516 Mon Sep 17 00:00:00 2001 From: James Chen Date: Fri, 25 Oct 2019 13:36:46 +0900 Subject: [PATCH 08/33] chore(lint): Update eslint to not treat _xxx as unused arg --- packages/neuron-wallet/.eslintrc.js | 5 +++-- .../neuron-wallet/src/database/chain/entities/input.ts | 1 - .../neuron-wallet/src/database/chain/entities/output.ts | 1 - .../src/database/chain/entities/transaction.ts | 1 - packages/neuron-wallet/src/services/cells.ts | 3 --- packages/neuron-wallet/src/services/indexer/queue.ts | 5 ----- packages/neuron-wallet/src/services/sync/get-blocks.ts | 9 +++------ .../src/services/tx/transaction-persistor.ts | 3 --- .../neuron-wallet/src/services/tx/transaction-service.ts | 3 --- 9 files changed, 6 insertions(+), 25 deletions(-) diff --git a/packages/neuron-wallet/.eslintrc.js b/packages/neuron-wallet/.eslintrc.js index 8eae8dced0..0e675fe66c 100644 --- a/packages/neuron-wallet/.eslintrc.js +++ b/packages/neuron-wallet/.eslintrc.js @@ -14,13 +14,14 @@ module.exports = { "@typescript-eslint/no-unused-vars": ["error", { "vars": "local", "args": "after-used", - "ignoreRestSiblings": false + "ignoreRestSiblings": false, + "argsIgnorePattern": "^_" }], "curly": [2, "all"], "implicit-arrow-linebreak": "off", "arrow-parens": [2, "as-needed"], "max-len": [2, { - "code": 120, + "code": 140, "ignoreComments": true, "ignoreTrailingComments": true, "ignoreUrls": true, diff --git a/packages/neuron-wallet/src/database/chain/entities/input.ts b/packages/neuron-wallet/src/database/chain/entities/input.ts index b72bb12f30..4c4fb018c4 100644 --- a/packages/neuron-wallet/src/database/chain/entities/input.ts +++ b/packages/neuron-wallet/src/database/chain/entities/input.ts @@ -2,7 +2,6 @@ import { Entity, BaseEntity, Column, ManyToOne, PrimaryGeneratedColumn } from 't import { OutPoint, Input as InputInterface, Script } from 'types/cell-types' import Transaction from './transaction' -/* eslint @typescript-eslint/no-unused-vars: "warn" */ // cellbase input may have same OutPoint @Entity() export default class Input extends BaseEntity { diff --git a/packages/neuron-wallet/src/database/chain/entities/output.ts b/packages/neuron-wallet/src/database/chain/entities/output.ts index 8ec0559ebd..33a39688ff 100644 --- a/packages/neuron-wallet/src/database/chain/entities/output.ts +++ b/packages/neuron-wallet/src/database/chain/entities/output.ts @@ -2,7 +2,6 @@ import { Entity, BaseEntity, Column, PrimaryColumn, ManyToOne } from 'typeorm' import { Script, OutPoint, Cell } from 'types/cell-types' import TransactionEntity from './transaction' -/* eslint @typescript-eslint/no-unused-vars: "warn" */ @Entity() export default class Output extends BaseEntity { @PrimaryColumn({ diff --git a/packages/neuron-wallet/src/database/chain/entities/transaction.ts b/packages/neuron-wallet/src/database/chain/entities/transaction.ts index 3cbcae9063..d69d34b1b1 100644 --- a/packages/neuron-wallet/src/database/chain/entities/transaction.ts +++ b/packages/neuron-wallet/src/database/chain/entities/transaction.ts @@ -21,7 +21,6 @@ const txDbChangedSubject = isRenderer ? remote.require('./models/subjects/tx-db-changed-subject').default.getSubject() : TxDbChangedSubject.getSubject() -/* eslint @typescript-eslint/no-unused-vars: "warn" */ @Entity() export default class Transaction extends BaseEntity { @PrimaryColumn({ diff --git a/packages/neuron-wallet/src/services/cells.ts b/packages/neuron-wallet/src/services/cells.ts index 28ad494d26..483f7d5577 100644 --- a/packages/neuron-wallet/src/services/cells.ts +++ b/packages/neuron-wallet/src/services/cells.ts @@ -7,9 +7,6 @@ import SkipDataAndType from './settings/skip-data-and-type' export const MIN_CELL_CAPACITY = '6100000000' -/* eslint @typescript-eslint/no-unused-vars: "warn" */ -/* eslint no-await-in-loop: "warn" */ -/* eslint no-restricted-syntax: "warn" */ export default class CellsService { // exclude hasData = true and typeScript != null public static getBalance = async ( diff --git a/packages/neuron-wallet/src/services/indexer/queue.ts b/packages/neuron-wallet/src/services/indexer/queue.ts index cbd0487059..ca406a9956 100644 --- a/packages/neuron-wallet/src/services/indexer/queue.ts +++ b/packages/neuron-wallet/src/services/indexer/queue.ts @@ -26,7 +26,6 @@ enum TxPointType { } export default class IndexerQueue { - // private lockHashes: string[] private lockHashInfos: LockHashInfo[] private indexerRPC: IndexerRPC private getBlocksService: GetBlocks @@ -48,7 +47,6 @@ export default class IndexerQueue { private url: string constructor(url: string, lockHashInfos: LockHashInfo[], tipNumberSubject: Subject) { - // this.lockHashes = lockHashes this.lockHashInfos = lockHashInfos this.url = url this.indexerRPC = new IndexerRPC(url) @@ -62,7 +60,6 @@ export default class IndexerQueue { } public setLockHashInfos = (lockHashInfos: LockHashInfo[]): void => { - // this.lockHashes = lockHashes this.lockHashInfos = lockHashInfos this.indexed = false } @@ -76,8 +73,6 @@ export default class IndexerQueue { this.resetFlag = true } - /* eslint no-await-in-loop: "off" */ - /* eslint no-restricted-syntax: "off" */ public start = async () => { while (!this.stopped) { try { diff --git a/packages/neuron-wallet/src/services/sync/get-blocks.ts b/packages/neuron-wallet/src/services/sync/get-blocks.ts index cc162cc3f4..a5017f8375 100644 --- a/packages/neuron-wallet/src/services/sync/get-blocks.ts +++ b/packages/neuron-wallet/src/services/sync/get-blocks.ts @@ -64,16 +64,14 @@ export default class GetBlocks { public retryGetBlock = async (num: string): Promise => { const block: Block = await Utils.retry(this.retryTime, this.retryInterval, async () => { - const b: Block = await this.getBlockByNumber(num) - return b + return await this.getBlockByNumber(num) }) return block } public getTransaction = async (hash: string): Promise => { - const tx = await this.core.rpc.getTransaction(hash) - return tx + return await this.core.rpc.getTransaction(hash) } public getHeader = async (hash: string): Promise => { @@ -88,8 +86,7 @@ export default class GetBlocks { public genesisBlockHash = async (): Promise => { const hash: string = await Utils.retry(3, 100, async () => { - const h: string = await this.core.rpc.getBlockHash('0x0') - return h + return await this.core.rpc.getBlockHash('0x0') }) return hash diff --git a/packages/neuron-wallet/src/services/tx/transaction-persistor.ts b/packages/neuron-wallet/src/services/tx/transaction-persistor.ts index 1f885fa49e..45af9d2047 100644 --- a/packages/neuron-wallet/src/services/tx/transaction-persistor.ts +++ b/packages/neuron-wallet/src/services/tx/transaction-persistor.ts @@ -7,9 +7,6 @@ import LockUtils from 'models/lock-utils' import { OutputStatus, TxSaveType } from './params' import Utils from 'services/sync/utils' -/* eslint @typescript-eslint/no-unused-vars: "warn" */ -/* eslint no-await-in-loop: "off" */ -/* eslint no-restricted-syntax: "off" */ export class TransactionPersistor { // After the tx is sent: // 1. If the tx is not persisted before sending, output = sent, input = pending diff --git a/packages/neuron-wallet/src/services/tx/transaction-service.ts b/packages/neuron-wallet/src/services/tx/transaction-service.ts index 28022851f0..cd71bbe516 100644 --- a/packages/neuron-wallet/src/services/tx/transaction-service.ts +++ b/packages/neuron-wallet/src/services/tx/transaction-service.ts @@ -38,9 +38,6 @@ export enum SearchType { Unknown = 'unknown', } -/* eslint @typescript-eslint/no-unused-vars: "warn" */ -/* eslint no-await-in-loop: "off" */ -/* eslint no-restricted-syntax: "off" */ export class TransactionsService { public static filterSearchType = (value: string) => { if (value === '') { From 2d5ff5a5d985c06fe4ab91f687d59ca1579c7516 Mon Sep 17 00:00:00 2001 From: James Chen Date: Fri, 25 Oct 2019 13:38:50 +0900 Subject: [PATCH 09/33] refactor: Rename IndexerRPC.getTransactionByLockHash to getTransactionsByLockHash --- .../src/services/indexer/indexer-rpc.ts | 20 ++++--------------- .../src/services/indexer/queue.ts | 5 ++--- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/packages/neuron-wallet/src/services/indexer/indexer-rpc.ts b/packages/neuron-wallet/src/services/indexer/indexer-rpc.ts index be389a51cd..2abcc574b9 100644 --- a/packages/neuron-wallet/src/services/indexer/indexer-rpc.ts +++ b/packages/neuron-wallet/src/services/indexer/indexer-rpc.ts @@ -16,27 +16,15 @@ export default class IndexerRPC { return this.core.rpc.indexLockHash(lockHash, indexFrom) } - public getTransactionByLockHash = async ( - lockHash: string, - page: string, - per: string, - reverseOrder: boolean = false - ) => { - const result = await this.core.rpc.getTransactionsByLockHash(lockHash, page, per, reverseOrder) - return result + public getTransactionsByLockHash = async (lockHash: string, page: string, per: string, reverseOrder: boolean = false) => { + return await this.core.rpc.getTransactionsByLockHash(lockHash, page, per, reverseOrder) } public getLockHashIndexStates = async () => { return this.core.rpc.getLockHashIndexStates() } - public getLiveCellsByLockHash = async ( - lockHash: string, - page: string, - per: string, - reverseOrder: boolean = false - ) => { - const result = await this.core.rpc.getLiveCellsByLockHash(lockHash, page, per, reverseOrder) - return result + public getLiveCellsByLockHash = async (lockHash: string, page: string, per: string, reverseOrder: boolean = false) => { + return await this.core.rpc.getLiveCellsByLockHash(lockHash, page, per, reverseOrder) } } diff --git a/packages/neuron-wallet/src/services/indexer/queue.ts b/packages/neuron-wallet/src/services/indexer/queue.ts index ca406a9956..caa6b9eaca 100644 --- a/packages/neuron-wallet/src/services/indexer/queue.ts +++ b/packages/neuron-wallet/src/services/indexer/queue.ts @@ -143,8 +143,7 @@ export default class IndexerQueue { .map(state => HexUtils.toDecimal(state.blockNumber)) const uniqueBlockNumbers = [...new Set(blockNumbers)] const blockNumbersBigInt = uniqueBlockNumbers.map(num => BigInt(num)) - const minBlockNumber = Utils.min(blockNumbersBigInt) - return minBlockNumber + return Utils.min(blockNumbersBigInt) } public indexLockHashes = async (lockHashInfos: LockHashInfo[]) => { @@ -163,7 +162,7 @@ export default class IndexerQueue { let page = 0 let stopped = false while (!stopped) { - const txs = await this.indexerRPC.getTransactionByLockHash(lockHash, `0x${page.toString(16)}`, `0x${this.per.toString(16)}`) + const txs = await this.indexerRPC.getTransactionsByLockHash(lockHash, `0x${page.toString(16)}`, `0x${this.per.toString(16)}`) if (txs.length < this.per) { stopped = true } From e866c6697c0bac826e2fb0586699040194662793 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Fri, 25 Oct 2019 14:11:22 +0800 Subject: [PATCH 10/33] =?UTF-8?q?feat:=20display=20disconnection=20errors?= =?UTF-8?q?=20and=20dismiss=20them=20on=20getting=20connec=E2=80=A6=20(#10?= =?UTF-8?q?19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: display disconnection errors and dismiss them on getting connected. UI will display the alerts of disconnection from neuron-wallet, and clear them once UI find that connection is built. There may be some status conflicts between UI and wallet, but it's acceptable. * test: fix the e2e test of notification --- .../neuron-ui/src/containers/Main/hooks.ts | 6 +- .../stateProvider/actionCreators/app.ts | 3 - .../neuron-wallet/src/controllers/wallets.ts | 106 ++++++------------ .../neuron-wallet/src/decorators/errors.ts | 4 +- .../tests-e2e/tests/notification.ts | 14 ++- 5 files changed, 53 insertions(+), 80 deletions(-) diff --git a/packages/neuron-ui/src/containers/Main/hooks.ts b/packages/neuron-ui/src/containers/Main/hooks.ts index 1dae29b28e..f696bea215 100644 --- a/packages/neuron-ui/src/containers/Main/hooks.ts +++ b/packages/neuron-ui/src/containers/Main/hooks.ts @@ -21,7 +21,7 @@ import { Command as CommandSubject, } from 'services/subjects' import { ckbCore, getTipBlockNumber, getBlockchainInfo } from 'services/chain' -import { ConnectionStatus } from 'utils/const' +import { ConnectionStatus, ErrorCode } from 'utils/const' import { networks as networksCache, currentNetworkID as currentNetworkIDCache, @@ -40,6 +40,10 @@ export const useSyncChainData = ({ chainURL, dispatch }: { chainURL: string; dis type: AppActions.UpdateTipBlockNumber, payload: BigInt(tipBlockNumber).toString(), }) + dispatch({ + type: AppActions.ClearNotificationsOfCode, + payload: ErrorCode.NodeDisconnected, + }) }) .catch((err: Error) => { if (process.env.NODE_ENV === 'development') { diff --git a/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts b/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts index 2efad915fa..1111c41d9d 100644 --- a/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts +++ b/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts @@ -76,9 +76,6 @@ export const addPopup = (text: string) => (dispatch: StateDispatch) => { } export const addNotification = (message: State.Message) => (dispatch: StateDispatch) => { - if (message && message.code === ErrorCode.NodeDisconnected) { - return - } dispatch({ type: AppActions.AddNotification, payload: message, diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index 00d0d0ce1b..8f64d15fbd 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -20,7 +20,6 @@ import { import i18n from 'utils/i18n' import AddressService from 'services/addresses' import WalletCreatedSubject from 'models/subjects/wallet-created-subject' -import logger from 'utils/logger' import { TransactionWithoutHash } from 'types/cell-types'; export default class WalletsController { @@ -368,26 +367,18 @@ export default class WalletsController { if (!params.fee || params.fee === '0') { feeRate = '1000' } - try { - 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, - } - } catch (err) { - logger.error(`sendCapacity:`, err) - return { - status: err.code || ResponseCode.Fail, - message: `Error: "${err.message}"`, - } + 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, } } @@ -401,24 +392,16 @@ export default class WalletsController { if (!params) { throw new IsRequired('Parameters') } - try { - const walletsService = WalletsService.getInstance() - const hash = await walletsService.sendTx( - params.walletID, - params.tx, - params.password, - params.description - ) - return { - status: ResponseCode.Success, - result: hash, - } - } catch (err) { - logger.error(`sendTx:`, err) - return { - status: err.code || ResponseCode.Fail, - message: `Error: "${err.message}"`, - } + const walletsService = WalletsService.getInstance() + const hash = await walletsService.sendTx( + params.walletID, + params.tx, + params.password, + params.description + ) + return { + status: ResponseCode.Success, + result: hash, } } @@ -435,24 +418,16 @@ export default class WalletsController { if (!params) { throw new IsRequired('Parameters') } - try { - const walletsService = WalletsService.getInstance() - const tx = await walletsService.generateTx( - params.walletID, - params.items, - params.fee, - params.feeRate, - ) - return { - status: ResponseCode.Success, - result: tx, - } - } catch (err) { - logger.error(`generateTx:`, err) - return { - status: err.code || ResponseCode.Fail, - message: `Error: "${err.message}"`, - } + const walletsService = WalletsService.getInstance() + const tx = await walletsService.generateTx( + params.walletID, + params.items, + params.fee, + params.feeRate, + ) + return { + status: ResponseCode.Success, + result: tx, } } @@ -461,18 +436,11 @@ export default class WalletsController { if (!params) { throw new IsRequired('Parameters') } - try { - const walletsService = WalletsService.getInstance() - const cycles = await walletsService.computeCycles(params.walletID, params.capacities) - return { - status: ResponseCode.Success, - result: cycles, - } - } catch (err) { - return { - status: ResponseCode.Fail, - message: `Error: "${err.message}"`, - } + const walletsService = WalletsService.getInstance() + const cycles = await walletsService.computeCycles(params.walletID, params.capacities) + return { + status: ResponseCode.Success, + result: cycles, } } diff --git a/packages/neuron-wallet/src/decorators/errors.ts b/packages/neuron-wallet/src/decorators/errors.ts index d61af6c823..4c4e345d2f 100644 --- a/packages/neuron-wallet/src/decorators/errors.ts +++ b/packages/neuron-wallet/src/decorators/errors.ts @@ -3,7 +3,7 @@ import logger from 'utils/logger' const NODE_DISCONNECTED_CODE = 104 -export const CatchControllerError = (_target: any, _name: string, descriptor: PropertyDescriptor) => { +export const CatchControllerError = (target: any, name: string, descriptor: PropertyDescriptor) => { const originalMethod = descriptor.value return { ...descriptor, @@ -11,7 +11,7 @@ export const CatchControllerError = (_target: any, _name: string, descriptor: Pr try { return await originalMethod(...args) } catch (err) { - logger.error(`CatchControllerError:`, err) + logger.error(`${target.name}.${name}:`, err) if (err.code === 'ECONNREFUSED') { err.code = NODE_DISCONNECTED_CODE } diff --git a/packages/neuron-wallet/tests-e2e/tests/notification.ts b/packages/neuron-wallet/tests-e2e/tests/notification.ts index a6e01c329a..103e139618 100644 --- a/packages/neuron-wallet/tests-e2e/tests/notification.ts +++ b/packages/neuron-wallet/tests-e2e/tests/notification.ts @@ -2,7 +2,7 @@ import Application from '../application' import { createWallet } from '../operations' /** - * 1. check the alert, it should be empty + * 1. check the alert, it should be disconnected to the network * 2. navigate to wallet settingsState * 3. delete a wallet * 4. input a wrong password @@ -32,14 +32,15 @@ export default (app: Application) => { describe('Test alert message and notification', () => { const messages = { + disconnected: 'Fail to connect to the node', incorrectPassword: 'Password is incorrect', } - app.test('There is no alerts after the app launched', async () => { + app.test('It should have an alert message of disconnection', async () => { const { client } = app.spectron const alertComponent = await client.$('.ms-MessageBar-text') const msg = await client.elementIdText(alertComponent.value.ELEMENT) - expect(msg.value).toBe('') + expect(msg.value).toBe(messages.disconnected) }) app.test('It should have an alert message of incorrect password', async () => { @@ -56,12 +57,14 @@ export default (app: Application) => { expect(msg.value).toBe(messages.incorrectPassword) }) - app.test('It should have a message in the notification', async () => { + app.test('It should have two messages in the notification', async () => { const { client } = app.spectron const messageComponents = await client.$$('.ms-Panel-content p') expect(messageComponents.length).toBe(Object.keys(messages).length) const incorrectPasswordMsg = await client.element(`//P[text()="${messages.incorrectPassword}"]`) + const disconnectMsg = await client.element(`//P[text()="${messages.disconnected}"]`) expect(incorrectPasswordMsg.state).not.toBe('failure') + expect(disconnectMsg.state).not.toBe('failed') }) // TODO: dismiss a message @@ -76,7 +79,8 @@ export default (app: Application) => { await app.waitUntilLoaded() app.wait(4000) const alertComponent = await client.$('.ms-MessageBar--error') - expect(alertComponent.state).toBe('failure') + const msg = await client.elementIdText(alertComponent.value.ELEMENT) + expect(msg.value).toBe(messages.disconnected) }) }) } From 97b8ea45c8870a06fe2e88ebb479dff48a9910a9 Mon Sep 17 00:00:00 2001 From: classicalliu Date: Sat, 26 Oct 2019 11:40:25 +0800 Subject: [PATCH 11/33] fix: return => break --- packages/neuron-wallet/src/services/indexer/queue.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/neuron-wallet/src/services/indexer/queue.ts b/packages/neuron-wallet/src/services/indexer/queue.ts index caa6b9eaca..9d000ab37b 100644 --- a/packages/neuron-wallet/src/services/indexer/queue.ts +++ b/packages/neuron-wallet/src/services/indexer/queue.ts @@ -191,7 +191,7 @@ export default class IndexerQueue { addresses: [address], url: this.url, }) - return + break } logger.debug('indexer fetched tx:', type, txPoint.txHash) From 2db32f9b040599b7c41d9032a3ffca25b7758f90 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Sat, 26 Oct 2019 14:02:31 +0800 Subject: [PATCH 12/33] refactor(neuron-wallet): all errors will be caught in api controller (#1022) * refactor(neuron-wallet): all errors will be caught in api controller * refactor(neuron-wallet): all errors will be caught in api controller --- .../neuron-wallet/src/controllers/networks.ts | 9 ------- .../src/controllers/skip-data-and-type.ts | 3 --- .../src/controllers/sync-info.ts | 2 -- .../src/controllers/transactions.ts | 6 ----- .../neuron-wallet/src/controllers/wallets.ts | 25 +++---------------- 5 files changed, 4 insertions(+), 41 deletions(-) diff --git a/packages/neuron-wallet/src/controllers/networks.ts b/packages/neuron-wallet/src/controllers/networks.ts index 4e0c718de0..a7bc506784 100644 --- a/packages/neuron-wallet/src/controllers/networks.ts +++ b/packages/neuron-wallet/src/controllers/networks.ts @@ -1,13 +1,11 @@ import { NetworkType, NetworkID, Network } from 'types/network' import NetworksService from 'services/networks' -import { CatchControllerError } from 'decorators' import { ResponseCode } from 'utils/const' import { IsRequired, InvalidName, NetworkNotFound, CurrentNetworkNotSet } from 'exceptions' const networksService = NetworksService.getInstance() export default class NetworksController { - @CatchControllerError public static async getAll() { const networks = await networksService.getAll() return { @@ -16,7 +14,6 @@ export default class NetworksController { } } - @CatchControllerError public static async get(id: NetworkID) { if (typeof id === 'undefined') { throw new IsRequired('ID') @@ -33,7 +30,6 @@ export default class NetworksController { } } - @CatchControllerError public static async create({ name, remote, type = NetworkType.Normal }: Network) { if (!name || !remote) { throw new IsRequired('Name and address') @@ -49,7 +45,6 @@ export default class NetworksController { } } - @CatchControllerError public static async update(id: NetworkID, options: Partial) { if (options.name && options.name === 'error') { throw new InvalidName('Network') @@ -62,7 +57,6 @@ export default class NetworksController { } } - @CatchControllerError public static async delete(id: NetworkID) { await networksService.delete(id) @@ -72,7 +66,6 @@ export default class NetworksController { } } - @CatchControllerError public static async currentID() { const currentID = await networksService.getCurrentID() if (currentID) { @@ -84,7 +77,6 @@ export default class NetworksController { throw new CurrentNetworkNotSet() } - @CatchControllerError public static async activate(id: NetworkID) { await networksService.activate(id) return { @@ -93,7 +85,6 @@ export default class NetworksController { } } - @CatchControllerError public static async clear() { await networksService.clear() return { diff --git a/packages/neuron-wallet/src/controllers/skip-data-and-type.ts b/packages/neuron-wallet/src/controllers/skip-data-and-type.ts index 5357dc450d..9b88d1d19f 100644 --- a/packages/neuron-wallet/src/controllers/skip-data-and-type.ts +++ b/packages/neuron-wallet/src/controllers/skip-data-and-type.ts @@ -1,9 +1,7 @@ -import { CatchControllerError } from 'decorators/errors' import { ResponseCode } from 'utils/const' import SkipDataAndType from 'services/settings/skip-data-and-type' export default class SkipDataAndTypeController { - @CatchControllerError public static async update(skip: boolean): Promise> { SkipDataAndType.getInstance().update(skip) @@ -13,7 +11,6 @@ export default class SkipDataAndTypeController { } } - @CatchControllerError public static async get(): Promise> { const skip = SkipDataAndType.getInstance().get() diff --git a/packages/neuron-wallet/src/controllers/sync-info.ts b/packages/neuron-wallet/src/controllers/sync-info.ts index fc94e804b2..786380441e 100644 --- a/packages/neuron-wallet/src/controllers/sync-info.ts +++ b/packages/neuron-wallet/src/controllers/sync-info.ts @@ -1,9 +1,7 @@ -import { CatchControllerError } from 'decorators' import BlockNumber from 'services/sync/block-number' import { ResponseCode } from 'utils/const' export default class SyncInfoController { - @CatchControllerError public static async currentBlockNumber() { const blockNumber = new BlockNumber() const current: bigint = await blockNumber.getCurrent() diff --git a/packages/neuron-wallet/src/controllers/transactions.ts b/packages/neuron-wallet/src/controllers/transactions.ts index 3781bc94fb..b2e1a0a18c 100644 --- a/packages/neuron-wallet/src/controllers/transactions.ts +++ b/packages/neuron-wallet/src/controllers/transactions.ts @@ -4,7 +4,6 @@ import { TransactionsService, PaginationResult, TransactionsByLockHashesParam } import AddressesService from 'services/addresses' import WalletsService from 'services/wallets' -import { CatchControllerError } from 'decorators' import { ResponseCode } from 'utils/const' import { TransactionNotFound, CurrentWalletNotSet, ServiceHasNoResponse } from 'exceptions' import LockUtils from 'models/lock-utils' @@ -12,7 +11,6 @@ import LockUtils from 'models/lock-utils' const CELL_COUNT_THRESHOLD = 10 export default class TransactionsController { - @CatchControllerError public static async getAll( params: TransactionsByLockHashesParam, ): Promise>> { @@ -28,7 +26,6 @@ export default class TransactionsController { } } - @CatchControllerError public static async getAllByKeywords( params: Controller.Params.TransactionsByKeywords, ): Promise & Controller.Params.TransactionsByKeywords>> { @@ -52,7 +49,6 @@ export default class TransactionsController { } } - @CatchControllerError public static async getAllByAddresses( params: Controller.Params.TransactionsByAddresses, ): Promise & Controller.Params.TransactionsByAddresses>> { @@ -82,7 +78,6 @@ export default class TransactionsController { } } - @CatchControllerError public static async get( walletID: string, hash: string, @@ -132,7 +127,6 @@ export default class TransactionsController { } } - @CatchControllerError public static async updateDescription({ hash, description }: { hash: string; description: string }) { await TransactionsService.updateDescription(hash, description) diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index 8f64d15fbd..f194b7ec31 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -5,7 +5,6 @@ import Keystore from 'models/keys/keystore' import Keychain from 'models/keys/keychain' import { validateMnemonic, mnemonicToSeedSync } from 'models/keys/mnemonic' import { AccountExtendedPublicKey, ExtendedPrivateKey } from 'models/keys/key' -import { CatchControllerError } from 'decorators' import { ResponseCode } from 'utils/const' import { CurrentWalletNotSet, @@ -20,10 +19,9 @@ import { import i18n from 'utils/i18n' import AddressService from 'services/addresses' import WalletCreatedSubject from 'models/subjects/wallet-created-subject' -import { TransactionWithoutHash } from 'types/cell-types'; +import { TransactionWithoutHash } from 'types/cell-types' export default class WalletsController { - @CatchControllerError public static async getAll(): Promise[]>> { const walletsService = WalletsService.getInstance() const wallets = walletsService.getAll() @@ -52,7 +50,6 @@ export default class WalletsController { } } - @CatchControllerError public static async get(id: string): Promise> { const walletsService = WalletsService.getInstance() if (typeof id === 'undefined') { @@ -69,7 +66,6 @@ export default class WalletsController { } } - @CatchControllerError public static async importMnemonic({ name, password, @@ -91,7 +87,6 @@ export default class WalletsController { return result } - @CatchControllerError public static async create({ name, password, @@ -164,7 +159,6 @@ export default class WalletsController { } } - @CatchControllerError public static async importKeystore({ name, password, @@ -192,7 +186,7 @@ export default class WalletsController { const accountKeychain = masterKeychain.derivePath(AccountExtendedPublicKey.ckbAccountPath) const accountExtendedPublicKey = new AccountExtendedPublicKey( accountKeychain.publicKey.toString('hex'), - accountKeychain.chainCode.toString('hex') + accountKeychain.chainCode.toString('hex'), ) const walletsService = WalletsService.getInstance() @@ -213,7 +207,7 @@ export default class WalletsController { } // TODO: update addresses? - @CatchControllerError + public static async update({ id, name, @@ -248,7 +242,6 @@ export default class WalletsController { } } - @CatchControllerError public static async delete({ id = '', password = '', @@ -267,7 +260,6 @@ export default class WalletsController { } } - @CatchControllerError public static async backup({ id = '', password = '', @@ -290,12 +282,10 @@ export default class WalletsController { result: true, }) } - } - ) + }) }) } - @CatchControllerError public static async getCurrent() { const currentWallet = WalletsService.getInstance().getCurrent() || null return { @@ -304,7 +294,6 @@ export default class WalletsController { } } - @CatchControllerError public static async activate(id: string) { const walletsService = WalletsService.getInstance() walletsService.setCurrent(id) @@ -318,7 +307,6 @@ export default class WalletsController { } } - @CatchControllerError public static async getAllAddresses(id: string) { const addresses = await AddressService.allAddressesByWalletId(id).then(addrs => addrs.map( @@ -347,7 +335,6 @@ export default class WalletsController { } } - @CatchControllerError public static async sendCapacity(params: { id: string walletID: string @@ -382,7 +369,6 @@ export default class WalletsController { } } - @CatchControllerError public static async sendTx(params: { walletID: string tx: TransactionWithoutHash @@ -405,7 +391,6 @@ export default class WalletsController { } } - @CatchControllerError public static async generateTx(params: { walletID: string items: { @@ -431,7 +416,6 @@ export default class WalletsController { } } - @CatchControllerError public static async computeCycles(params: { walletID: string; capacities: string }) { if (!params) { throw new IsRequired('Parameters') @@ -444,7 +428,6 @@ export default class WalletsController { } } - @CatchControllerError public static async updateAddressDescription({ walletID, address, From 05ae69e8c450f5a9b08217136d5099f83fb5da56 Mon Sep 17 00:00:00 2001 From: classicalliu Date: Sat, 26 Oct 2019 14:55:54 +0800 Subject: [PATCH 13/33] fix: break => continue --- packages/neuron-wallet/src/services/indexer/queue.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/neuron-wallet/src/services/indexer/queue.ts b/packages/neuron-wallet/src/services/indexer/queue.ts index 9d000ab37b..99f34d2629 100644 --- a/packages/neuron-wallet/src/services/indexer/queue.ts +++ b/packages/neuron-wallet/src/services/indexer/queue.ts @@ -191,7 +191,7 @@ export default class IndexerQueue { addresses: [address], url: this.url, }) - break + continue } logger.debug('indexer fetched tx:', type, txPoint.txHash) From 1f4d4ea6fece9fc7c3d31120bd34d67e48423518 Mon Sep 17 00:00:00 2001 From: Keith Date: Sat, 26 Oct 2019 21:17:43 +0800 Subject: [PATCH 14/33] feat: stringify the result from api controller --- .../src/services/remote/apiMethodWrapper.ts | 23 +++--- packages/neuron-wallet/src/controllers/api.ts | 70 ++++++++++--------- .../neuron-wallet/src/decorators/index.ts | 6 +- .../src/decorators/{errors.ts => mappers.ts} | 14 ++-- 4 files changed, 59 insertions(+), 54 deletions(-) rename packages/neuron-wallet/src/decorators/{errors.ts => mappers.ts} (63%) diff --git a/packages/neuron-ui/src/services/remote/apiMethodWrapper.ts b/packages/neuron-ui/src/services/remote/apiMethodWrapper.ts index c6623c23d7..d21f8db749 100644 --- a/packages/neuron-ui/src/services/remote/apiMethodWrapper.ts +++ b/packages/neuron-ui/src/services/remote/apiMethodWrapper.ts @@ -1,4 +1,3 @@ -// TODO: use error code interface SuccessFromController { status: 1 result: any @@ -12,6 +11,7 @@ interface FailureFromController { meta?: { [key: string]: string } } } + export type ControllerResponse = SuccessFromController | FailureFromController export const RemoteNotLoadError = { @@ -22,15 +22,7 @@ export const RemoteNotLoadError = { } export const apiMethodWrapper = ( - callControllerMethod: ( - controller: any - ) => ( - params: T - ) => Promise<{ - status: any - result: any - message: { code?: number; content?: string; meta?: { [key: string]: string } } - }> + callControllerMethod: (controller: any) => (params: T) => Promise ) => async (realParams: T): Promise => { if (!window.remote) { return RemoteNotLoadError @@ -44,7 +36,16 @@ export const apiMethodWrapper = ( }, } } - const res = await callControllerMethod(controller)(realParams) + + const res: SuccessFromController | FailureFromController = await callControllerMethod(controller)(realParams) + .then(stringifiedRes => (stringifiedRes ? JSON.parse(stringifiedRes) : stringifiedRes)) + .catch(() => ({ + status: 0, + message: { + content: 'Invalid response format', + }, + })) + if (process.env.NODE_ENV === 'development' && window.localStorage.getItem('log-response')) { console.group('api controller') console.info(JSON.stringify(res, null, 2)) diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts index 93d44130d0..8187715338 100644 --- a/packages/neuron-wallet/src/controllers/api.ts +++ b/packages/neuron-wallet/src/controllers/api.ts @@ -17,7 +17,7 @@ import WalletsService from 'services/wallets' import SkipDataAndType from 'services/settings/skip-data-and-type' import { ConnectionStatusSubject } from 'models/subjects/node' import { SystemScriptSubject } from 'models/subjects/system-script' -import { CatchControllerError } from 'decorators/errors' +import { MapApiResponse } from 'decorators' import { ResponseCode } from 'utils/const' import { TransactionWithoutHash } from 'types/cell-types' @@ -27,7 +27,9 @@ import { TransactionWithoutHash } from 'types/cell-types' */ export default class ApiController { // App - public static loadInitData = async () => { + + @MapApiResponse + public static async loadInitData() { const walletsService = WalletsService.getInstance() const networksService = NetworksService.getInstance() const [ @@ -114,69 +116,71 @@ export default class ApiController { return { status: ResponseCode.Success, result: initState } } - public static handleViewError = (error: string) => { + @MapApiResponse + public static handleViewError(error: string) { if (env.isDevMode) { console.error(error) } } + @MapApiResponse public static async contextMenu(params: { type: string; id: string }) { return popContextMenu(params) } // Wallets - @CatchControllerError + @MapApiResponse public static async getAllWallets() { return WalletsController.getAll() } - @CatchControllerError + @MapApiResponse public static async getCurrentWallet() { return WalletsController.getCurrent() } - @CatchControllerError - public static async importMnemonic(params: { name: string, password: string, mnemonic: string }) { + @MapApiResponse + public static async importMnemonic(params: { name: string; password: string; mnemonic: string }) { return WalletsController.importMnemonic(params) } - @CatchControllerError - public static async importKeystore(params: { name: string, password: string, keystorePath: string }) { + @MapApiResponse + public static async importKeystore(params: { name: string; password: string; keystorePath: string }) { return WalletsController.importKeystore(params) } - @CatchControllerError - public static async createWallet(params: { name: string, password: string, mnemonic: string }) { + @MapApiResponse + public static async createWallet(params: { name: string; password: string; mnemonic: string }) { return WalletsController.create(params) } - @CatchControllerError - public static async updateWallet(params: { id: string, password: string, name: string, newPassword?: string }) { - WalletsController.update(params) + @MapApiResponse + public static async updateWallet(params: { id: string; password: string; name: string; newPassword?: string }) { + return WalletsController.update(params) } - @CatchControllerError + @MapApiResponse public static async deleteWallet({ id = '', password = '' }) { return WalletsController.delete({ id, password }) } - @CatchControllerError + @MapApiResponse public static async backupWallet({ id = '', password = '' }) { return WalletsController.backup({ id, password }) } - @CatchControllerError + @MapApiResponse public static async setCurrentWallet(id: string) { return WalletsController.activate(id) } - @CatchControllerError + @MapApiResponse public static async getAddressesByWalletID(id: string) { return WalletsController.getAllAddresses(id) } - @CatchControllerError + @MapApiResponse public static async sendCapacity(params: { id: string walletID: string @@ -191,17 +195,17 @@ export default class ApiController { return WalletsController.sendCapacity(params) } - @CatchControllerError + @MapApiResponse public static async sendTx(params: { walletID: string - tx: TransactionWithoutHash, + tx: TransactionWithoutHash password: string description?: string }) { return WalletsController.sendTx(params) } - @CatchControllerError + @MapApiResponse public static async generateTx(params: { walletID: string items: { @@ -214,12 +218,12 @@ export default class ApiController { return WalletsController.generateTx(params) } - @CatchControllerError + @MapApiResponse public static async computeCycles(params: { walletID: string; capacities: string }) { return WalletsController.computeCycles(params) } - @CatchControllerError + @MapApiResponse public static async updateAddressDescription(params: { walletID: string address: string @@ -230,46 +234,46 @@ export default class ApiController { // Networks - @CatchControllerError + @MapApiResponse public static async getAllNetworks() { return NetworksController.getAll() } - @CatchControllerError + @MapApiResponse public static async createNetwork({ name, remote, type = NetworkType.Normal, chain = 'ckb' }: Network) { return NetworksController.create({ name, remote, type, chain }) } - @CatchControllerError + @MapApiResponse public static async updateNetwork(id: NetworkID, options: Partial) { return NetworksController.update(id, options) } - @CatchControllerError + @MapApiResponse public static async getCurrentNetworkID() { return NetworksController.currentID() } - @CatchControllerError + @MapApiResponse public static async setCurrentNetowrk(id: NetworkID) { return NetworksController.activate(id) } // Transactions - @CatchControllerError + @MapApiResponse public static async getTransactionList( params: Controller.Params.TransactionsByKeywords, ) { return TransactionsController.getAllByKeywords(params) } - @CatchControllerError + @MapApiResponse public static async getTransaction(walletID: string, hash: string) { return TransactionsController.get(walletID, hash) } - @CatchControllerError + @MapApiResponse public static async updateTransactionDescription(params: { hash: string; description: string }) { return TransactionsController.updateDescription(params) } @@ -280,7 +284,7 @@ export default class ApiController { // Misc - @CatchControllerError + @MapApiResponse public static async updateSkipDataAndType(skip: boolean) { return SkipDataAndTypeController.update(skip) } diff --git a/packages/neuron-wallet/src/decorators/index.ts b/packages/neuron-wallet/src/decorators/index.ts index 55b5deea58..3581bbe329 100644 --- a/packages/neuron-wallet/src/decorators/index.ts +++ b/packages/neuron-wallet/src/decorators/index.ts @@ -1,10 +1,10 @@ -import errorDecorators from './errors' +import mappers from './mappers' import validatorDecorators from './validators' -export const { CatchControllerError } = errorDecorators +export const { MapApiResponse } = mappers export const { Validate, Password, Required } = validatorDecorators export default { - ...errorDecorators, + ...mappers, ...validatorDecorators, } diff --git a/packages/neuron-wallet/src/decorators/errors.ts b/packages/neuron-wallet/src/decorators/mappers.ts similarity index 63% rename from packages/neuron-wallet/src/decorators/errors.ts rename to packages/neuron-wallet/src/decorators/mappers.ts index 4c4e345d2f..8907e5dc63 100644 --- a/packages/neuron-wallet/src/decorators/errors.ts +++ b/packages/neuron-wallet/src/decorators/mappers.ts @@ -3,27 +3,27 @@ import logger from 'utils/logger' const NODE_DISCONNECTED_CODE = 104 -export const CatchControllerError = (target: any, name: string, descriptor: PropertyDescriptor) => { +export const MapApiResponse = (target: any, name: string, descriptor: PropertyDescriptor) => { const originalMethod = descriptor.value return { ...descriptor, - async value(...args: any[]) { + async value(...args: any[]): Promise { try { - return await originalMethod(...args) + const res = await originalMethod(...args) + return JSON.stringify(res) } catch (err) { logger.error(`${target.name}.${name}:`, err) if (err.code === 'ECONNREFUSED') { err.code = NODE_DISCONNECTED_CODE } - return { + const res = { status: err.code || ResponseCode.Fail, message: typeof err.message === 'string' ? { content: err.message } : err.message, } + return JSON.stringify(res) } }, } } -export default { - CatchControllerError, -} +export default { MapApiResponse } From feb75aaed3e473e1354afa07b447d426874e1a99 Mon Sep 17 00:00:00 2001 From: James Chen Date: Fri, 25 Oct 2019 14:35:22 +0900 Subject: [PATCH 15/33] refactor: Only load inputs relation when querying transactions from DB --- .../neuron-wallet/src/services/tx/transaction-persistor.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/neuron-wallet/src/services/tx/transaction-persistor.ts b/packages/neuron-wallet/src/services/tx/transaction-persistor.ts index 45af9d2047..9b8656794d 100644 --- a/packages/neuron-wallet/src/services/tx/transaction-persistor.ts +++ b/packages/neuron-wallet/src/services/tx/transaction-persistor.ts @@ -262,11 +262,8 @@ export class TransactionPersistor { } public static get = async (txHash: string) => { - const txEntity: TransactionEntity | undefined = await getConnection() - .getRepository(TransactionEntity) - .findOne(txHash, { relations: ['inputs', 'outputs'] }) - - return txEntity + return await getConnection().getRepository(TransactionEntity) + .findOne(txHash, { relations: ['inputs'] }) } public static saveSentTx = async ( From d948a59b4c18847e0cde429fea797413102d62bc Mon Sep 17 00:00:00 2001 From: James Chen Date: Fri, 25 Oct 2019 15:53:17 +0900 Subject: [PATCH 16/33] refactor: Do not process output multiple times if its status is dead --- packages/neuron-wallet/src/services/tx/indexer-transaction.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/neuron-wallet/src/services/tx/indexer-transaction.ts b/packages/neuron-wallet/src/services/tx/indexer-transaction.ts index 5beb9393d7..5255f987b3 100644 --- a/packages/neuron-wallet/src/services/tx/indexer-transaction.ts +++ b/packages/neuron-wallet/src/services/tx/indexer-transaction.ts @@ -64,7 +64,7 @@ export default class IndexerTransaction { }) .getOne() - if (output) { + if (output && output.status !== OutputStatus.Dead) { await getConnection().manager.update( InputEntity, { @@ -83,7 +83,7 @@ export default class IndexerTransaction { .getRepository(TransactionEntity) .createQueryBuilder('tx') .where({ - hash: output.outPointTxHash, + hash: txHash, }) .getOne() if (tx) { From 68e00217f7f38ee06882ee3d8618f02b582714f7 Mon Sep 17 00:00:00 2001 From: James Chen Date: Fri, 25 Oct 2019 16:41:48 +0900 Subject: [PATCH 17/33] chore: Log DB queries that take more than 100ms to execute --- packages/neuron-wallet/src/database/chain/ormconfig.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/neuron-wallet/src/database/chain/ormconfig.ts b/packages/neuron-wallet/src/database/chain/ormconfig.ts index d71471384b..2f04e72f68 100644 --- a/packages/neuron-wallet/src/database/chain/ormconfig.ts +++ b/packages/neuron-wallet/src/database/chain/ormconfig.ts @@ -40,6 +40,7 @@ const connectOptions = async (genesisBlockHash: string): Promise Date: Fri, 25 Oct 2019 17:16:11 +0900 Subject: [PATCH 18/33] chore: Always log DB under dev or test mode --- packages/neuron-wallet/src/database/chain/ormconfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/neuron-wallet/src/database/chain/ormconfig.ts b/packages/neuron-wallet/src/database/chain/ormconfig.ts index 2f04e72f68..00dc144dd0 100644 --- a/packages/neuron-wallet/src/database/chain/ormconfig.ts +++ b/packages/neuron-wallet/src/database/chain/ormconfig.ts @@ -26,7 +26,7 @@ const connectOptions = async (genesisBlockHash: string): Promise Date: Fri, 25 Oct 2019 18:09:03 +0900 Subject: [PATCH 19/33] chore: Log indexer tx process --- packages/neuron-wallet/src/services/indexer/queue.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/neuron-wallet/src/services/indexer/queue.ts b/packages/neuron-wallet/src/services/indexer/queue.ts index 99f34d2629..14749824db 100644 --- a/packages/neuron-wallet/src/services/indexer/queue.ts +++ b/packages/neuron-wallet/src/services/indexer/queue.ts @@ -166,6 +166,8 @@ export default class IndexerQueue { if (txs.length < this.per) { stopped = true } + logger.debug('indexer txs for : ', type, lockHash, 'page: ', page, ', tx count: ', txs.length) + for (const tx of txs) { let txPoint: CKBComponents.TransactionPoint | null = null if (type === TxPointType.CreatedBy) { @@ -178,6 +180,8 @@ export default class IndexerQueue { txPoint && (BigInt(txPoint.blockNumber) >= startBlockNumber || this.tipBlockNumber - BigInt(txPoint.blockNumber) < 1000) ) { + logger.debug('\tprocess tx: ', txPoint.txHash) + const transactionWithStatus = await this.getBlocksService.getTransaction(txPoint.txHash) const ckbTransaction: CKBComponents.Transaction = transactionWithStatus.transaction const transaction: Transaction = TypeConvert.toTransaction(ckbTransaction) @@ -194,8 +198,6 @@ export default class IndexerQueue { continue } - logger.debug('indexer fetched tx:', type, txPoint.txHash) - // tx timestamp / blockNumber / blockHash let txEntity: TransactionEntity | undefined = await TransactionPersistor.get(transaction.hash) if (!txEntity || !txEntity.blockHash) { From 2fc14bca49ac7fcd65e24f0ff4911bcf8182e8c2 Mon Sep 17 00:00:00 2001 From: James Chen Date: Fri, 25 Oct 2019 21:14:05 +0900 Subject: [PATCH 20/33] chore: Do not log query by default --- packages/neuron-wallet/src/database/chain/ormconfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/neuron-wallet/src/database/chain/ormconfig.ts b/packages/neuron-wallet/src/database/chain/ormconfig.ts index 00dc144dd0..61f9c12f41 100644 --- a/packages/neuron-wallet/src/database/chain/ormconfig.ts +++ b/packages/neuron-wallet/src/database/chain/ormconfig.ts @@ -26,7 +26,7 @@ const connectOptions = async (genesisBlockHash: string): Promise Date: Fri, 25 Oct 2019 21:43:57 +0900 Subject: [PATCH 21/33] feat: Add a few db indices --- .../migrations/1572006450765-AddIndices.ts | 17 +++++++++++++++++ .../src/database/chain/ormconfig.ts | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 packages/neuron-wallet/src/database/chain/migrations/1572006450765-AddIndices.ts diff --git a/packages/neuron-wallet/src/database/chain/migrations/1572006450765-AddIndices.ts b/packages/neuron-wallet/src/database/chain/migrations/1572006450765-AddIndices.ts new file mode 100644 index 0000000000..7c6b4ba054 --- /dev/null +++ b/packages/neuron-wallet/src/database/chain/migrations/1572006450765-AddIndices.ts @@ -0,0 +1,17 @@ +import {MigrationInterface, QueryRunner, TableIndex} from "typeorm"; + +export class AddIndices1572006450765 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createIndex("input", new TableIndex({ columnNames: ["transactionHash", "lockHash"] })) + await queryRunner.createIndex("output", new TableIndex({ columnNames: ["transactionHash", "lockHash"] })) + await queryRunner.createIndex("transaction", new TableIndex({ columnNames: ["status"] })) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropIndex("input", new TableIndex({ columnNames: ["transactionHash", "lockHash"] })) + await queryRunner.dropIndex("output", new TableIndex({ columnNames: ["transactionHash", "lockHash"] })) + await queryRunner.dropIndex("transaction", new TableIndex({ columnNames: ["status"] })) + } + +} diff --git a/packages/neuron-wallet/src/database/chain/ormconfig.ts b/packages/neuron-wallet/src/database/chain/ormconfig.ts index 61f9c12f41..4b05b0d224 100644 --- a/packages/neuron-wallet/src/database/chain/ormconfig.ts +++ b/packages/neuron-wallet/src/database/chain/ormconfig.ts @@ -13,6 +13,7 @@ import { InitMigration1566959757554 } from './migrations/1566959757554-InitMigra import { AddTypeAndHasData1567144517514 } from './migrations/1567144517514-AddTypeAndHasData' import { ChangeHasDataDefault1568621556467 } from './migrations/1568621556467-ChangeHasDataDefault' import { AddLockToInput1570522869590 } from './migrations/1570522869590-AddLockToInput' +import { AddIndices1572006450765 } from './migrations/1572006450765-AddIndices' export const CONNECTION_NOT_FOUND_NAME = 'ConnectionNotFoundError' @@ -38,6 +39,7 @@ const connectOptions = async (genesisBlockHash: string): Promise Date: Fri, 25 Oct 2019 23:49:25 +0900 Subject: [PATCH 22/33] chore: Set maxQueryExecutionTime to 70ms --- packages/neuron-wallet/src/database/chain/ormconfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/neuron-wallet/src/database/chain/ormconfig.ts b/packages/neuron-wallet/src/database/chain/ormconfig.ts index 4b05b0d224..4f9ece9a58 100644 --- a/packages/neuron-wallet/src/database/chain/ormconfig.ts +++ b/packages/neuron-wallet/src/database/chain/ormconfig.ts @@ -42,7 +42,7 @@ const connectOptions = async (genesisBlockHash: string): Promise Date: Sun, 27 Oct 2019 09:52:41 +0900 Subject: [PATCH 23/33] refactor: Add index on transaction timestamp column --- .../1572137226866-AddIndexToTxTimestamp.ts | 13 +++++++++++++ .../neuron-wallet/src/database/chain/ormconfig.ts | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 packages/neuron-wallet/src/database/chain/migrations/1572137226866-AddIndexToTxTimestamp.ts diff --git a/packages/neuron-wallet/src/database/chain/migrations/1572137226866-AddIndexToTxTimestamp.ts b/packages/neuron-wallet/src/database/chain/migrations/1572137226866-AddIndexToTxTimestamp.ts new file mode 100644 index 0000000000..4d9e78704b --- /dev/null +++ b/packages/neuron-wallet/src/database/chain/migrations/1572137226866-AddIndexToTxTimestamp.ts @@ -0,0 +1,13 @@ +import {MigrationInterface, QueryRunner, TableIndex} from "typeorm"; + +export class AddIndexToTxTimestamp1572137226866 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createIndex("transaction", new TableIndex({ columnNames: ["timestamp"] })) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropIndex("transaction", new TableIndex({ columnNames: ["timestamp"] })) + } + +} diff --git a/packages/neuron-wallet/src/database/chain/ormconfig.ts b/packages/neuron-wallet/src/database/chain/ormconfig.ts index 4f9ece9a58..e115632070 100644 --- a/packages/neuron-wallet/src/database/chain/ormconfig.ts +++ b/packages/neuron-wallet/src/database/chain/ormconfig.ts @@ -14,6 +14,7 @@ import { AddTypeAndHasData1567144517514 } from './migrations/1567144517514-AddTy import { ChangeHasDataDefault1568621556467 } from './migrations/1568621556467-ChangeHasDataDefault' import { AddLockToInput1570522869590 } from './migrations/1570522869590-AddLockToInput' import { AddIndices1572006450765 } from './migrations/1572006450765-AddIndices' +import { AddIndexToTxTimestamp1572137226866 } from './migrations/1572137226866-AddIndexToTxTimestamp' export const CONNECTION_NOT_FOUND_NAME = 'ConnectionNotFoundError' @@ -39,7 +40,8 @@ const connectOptions = async (genesisBlockHash: string): Promise Date: Sun, 27 Oct 2019 11:20:57 +0900 Subject: [PATCH 24/33] chore(db): Congifure chain sqlite db parameters --- packages/neuron-wallet/src/database/chain/ormconfig.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/neuron-wallet/src/database/chain/ormconfig.ts b/packages/neuron-wallet/src/database/chain/ormconfig.ts index e115632070..60a9d7aa87 100644 --- a/packages/neuron-wallet/src/database/chain/ormconfig.ts +++ b/packages/neuron-wallet/src/database/chain/ormconfig.ts @@ -48,10 +48,6 @@ const connectOptions = async (genesisBlockHash: string): Promise { - await getConnection().manager.query(`PRAGMA busy_timeout = 3000;`) -} - export const initConnection = async (genesisBlockHash: string) => { // try to close connection, if not exist, will throw ConnectionNotFoundError when call getConnection() try { @@ -63,7 +59,8 @@ export const initConnection = async (genesisBlockHash: string) => { try { await createConnection(connectionOptions) - await setBusyTimeout() + await getConnection().manager.query(`PRAGMA busy_timeout = 3000;`) + await getConnection().manager.query(`PRAGMA temp_store = MEMORY;`) } catch (err) { logger.error(err.message) } From 2779ab6c3a0209494aedac7a6cfc8e13471624a1 Mon Sep 17 00:00:00 2001 From: James Chen Date: Sun, 27 Oct 2019 11:22:39 +0900 Subject: [PATCH 25/33] refactor: Only order txs by timestamp When a tx is created set its timestamp value from createdAt. --- .../src/database/chain/entities/transaction.ts | 3 +++ packages/neuron-wallet/src/services/indexer/queue.ts | 4 +--- .../neuron-wallet/src/services/tx/transaction-service.ts | 9 +++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/neuron-wallet/src/database/chain/entities/transaction.ts b/packages/neuron-wallet/src/database/chain/entities/transaction.ts index d69d34b1b1..3ec73f0077 100644 --- a/packages/neuron-wallet/src/database/chain/entities/transaction.ts +++ b/packages/neuron-wallet/src/database/chain/entities/transaction.ts @@ -124,6 +124,9 @@ export default class Transaction extends BaseEntity { updateCreatedAt() { this.createdAt = Date.now().toString() this.updatedAt = this.createdAt + if (!this.timestamp) { + this.timestamp = this.createdAt + } } @BeforeUpdate() diff --git a/packages/neuron-wallet/src/services/indexer/queue.ts b/packages/neuron-wallet/src/services/indexer/queue.ts index 14749824db..6391300c77 100644 --- a/packages/neuron-wallet/src/services/indexer/queue.ts +++ b/packages/neuron-wallet/src/services/indexer/queue.ts @@ -166,7 +166,7 @@ export default class IndexerQueue { if (txs.length < this.per) { stopped = true } - logger.debug('indexer txs for : ', type, lockHash, 'page: ', page, ', tx count: ', txs.length) + logger.debug(`indexer txs for: ${type} ${lockHash}, page: ${page}, tx count: ${txs.length}`) for (const tx of txs) { let txPoint: CKBComponents.TransactionPoint | null = null @@ -180,8 +180,6 @@ export default class IndexerQueue { txPoint && (BigInt(txPoint.blockNumber) >= startBlockNumber || this.tipBlockNumber - BigInt(txPoint.blockNumber) < 1000) ) { - logger.debug('\tprocess tx: ', txPoint.txHash) - const transactionWithStatus = await this.getBlocksService.getTransaction(txPoint.txHash) const ckbTransaction: CKBComponents.Transaction = transactionWithStatus.transaction const transaction: Transaction = TypeConvert.toTransaction(ckbTransaction) diff --git a/packages/neuron-wallet/src/services/tx/transaction-service.ts b/packages/neuron-wallet/src/services/tx/transaction-service.ts index cd71bbe516..52c7e7ccbd 100644 --- a/packages/neuron-wallet/src/services/tx/transaction-service.ts +++ b/packages/neuron-wallet/src/services/tx/transaction-service.ts @@ -81,7 +81,7 @@ export class TransactionsService { return [ `${ base[0] - } AND (CAST(ifnull("tx"."timestamp", "tx"."createdAt") AS UNSIGNED BIG INT) >= :beginTimestamp AND CAST(ifnull("tx"."timestamp", "tx"."createdAt") AS UNSIGNED BIG INT) < :endTimestamp)`, + } AND (CAST("tx"."timestamp") AS UNSIGNED BIG INT) >= :beginTimestamp AND CAST("tx"."timestamp") AS UNSIGNED BIG INT) < :endTimestamp)`, { lockHashes: params.lockHashes, beginTimestamp, @@ -152,15 +152,14 @@ export class TransactionsService { const query = getConnection() .getRepository(TransactionEntity) .createQueryBuilder('tx') - .addSelect(`ifnull('tx'.timestamp, 'tx'.createdAt)`, 'tt') .leftJoinAndSelect('tx.inputs', 'input') .leftJoinAndSelect('tx.outputs', 'output') .where(searchParams[0], searchParams[1] as ObjectLiteral) - .orderBy(`tt`, 'DESC') const totalCount: number = await query.getCount() const transactions: TransactionEntity[] = await query + .orderBy(`tx.timestamp`, 'DESC') .skip(skip) .take(params.pageSize) .getMany() @@ -285,10 +284,8 @@ export class TransactionsService { const count: number = await getConnection() .getRepository(TransactionEntity) .createQueryBuilder('tx') - .leftJoinAndSelect('tx.inputs', 'input') - .leftJoinAndSelect('tx.outputs', 'output') .where( - `(input.lockHash IN (:...lockHashes) OR output.lockHash IN (:...lockHashes)) AND tx.status IN (:...status)`, + `tx.hash in (select output.transactionHash from output where output.lockHash in (:...lockHashes) union select input.transactionHash from input where input.lockHash in (:...lockHashes)) AND tx.status IN (:...status)`, { lockHashes, status, From 9de193b2714627df1cde6058b5c42a89bca3e4a3 Mon Sep 17 00:00:00 2001 From: James Chen Date: Mon, 28 Oct 2019 10:29:16 +0900 Subject: [PATCH 26/33] chore: Lower maxQueryExecutionTime to 30 --- packages/neuron-wallet/src/database/chain/ormconfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/neuron-wallet/src/database/chain/ormconfig.ts b/packages/neuron-wallet/src/database/chain/ormconfig.ts index 60a9d7aa87..87d18a3871 100644 --- a/packages/neuron-wallet/src/database/chain/ormconfig.ts +++ b/packages/neuron-wallet/src/database/chain/ormconfig.ts @@ -44,7 +44,7 @@ const connectOptions = async (genesisBlockHash: string): Promise Date: Mon, 28 Oct 2019 10:53:42 +0900 Subject: [PATCH 27/33] feat: Optimize output db query --- .../migrations/1572226722928-AddOutputIndex.ts | 13 +++++++++++++ .../neuron-wallet/src/database/chain/ormconfig.ts | 4 +++- packages/neuron-wallet/src/services/cells.ts | 13 ++++++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 packages/neuron-wallet/src/database/chain/migrations/1572226722928-AddOutputIndex.ts diff --git a/packages/neuron-wallet/src/database/chain/migrations/1572226722928-AddOutputIndex.ts b/packages/neuron-wallet/src/database/chain/migrations/1572226722928-AddOutputIndex.ts new file mode 100644 index 0000000000..1efc10a913 --- /dev/null +++ b/packages/neuron-wallet/src/database/chain/migrations/1572226722928-AddOutputIndex.ts @@ -0,0 +1,13 @@ +import {MigrationInterface, QueryRunner, TableIndex} from "typeorm"; + +export class AddOutputIndex1572226722928 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createIndex("output", new TableIndex({ columnNames: ["lockHash", "status", "hasData", "typeScript"] })) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropIndex("output", new TableIndex({ columnNames: ["lockHash", "status", "hasData", "typeScript"] })) + } + +} diff --git a/packages/neuron-wallet/src/database/chain/ormconfig.ts b/packages/neuron-wallet/src/database/chain/ormconfig.ts index 87d18a3871..75b405fe6e 100644 --- a/packages/neuron-wallet/src/database/chain/ormconfig.ts +++ b/packages/neuron-wallet/src/database/chain/ormconfig.ts @@ -15,6 +15,7 @@ import { ChangeHasDataDefault1568621556467 } from './migrations/1568621556467-Ch import { AddLockToInput1570522869590 } from './migrations/1570522869590-AddLockToInput' import { AddIndices1572006450765 } from './migrations/1572006450765-AddIndices' import { AddIndexToTxTimestamp1572137226866 } from './migrations/1572137226866-AddIndexToTxTimestamp' +import { AddOutputIndex1572226722928 } from './migrations/1572226722928-AddOutputIndex' export const CONNECTION_NOT_FOUND_NAME = 'ConnectionNotFoundError' @@ -41,7 +42,8 @@ const connectOptions = async (genesisBlockHash: string): Promise BigInt(c.capacity)).reduce((result, c) => result + c, BigInt(0)) From b451e0dd69c4c56c82a4366675bb9f923b8a97d3 Mon Sep 17 00:00:00 2001 From: James Chen Date: Mon, 28 Oct 2019 12:47:15 +0900 Subject: [PATCH 28/33] chore: Delete inline eslint comments(rules) --- packages/neuron-wallet/src/listeners/tx-status.ts | 2 -- packages/neuron-wallet/src/services/addresses.ts | 2 -- packages/neuron-wallet/src/services/sync/block-listener.ts | 2 -- packages/neuron-wallet/src/services/sync/check-and-save/tx.ts | 2 -- packages/neuron-wallet/src/services/sync/queue.ts | 1 - packages/neuron-wallet/src/services/sync/utils.ts | 2 -- packages/neuron-wallet/src/startup/sync-block-task/create.ts | 1 - 7 files changed, 12 deletions(-) diff --git a/packages/neuron-wallet/src/listeners/tx-status.ts b/packages/neuron-wallet/src/listeners/tx-status.ts index 3f9be3f9e8..b33499320d 100644 --- a/packages/neuron-wallet/src/listeners/tx-status.ts +++ b/packages/neuron-wallet/src/listeners/tx-status.ts @@ -36,8 +36,6 @@ const getTransactionStatus = async (hash: string) => { } } -/* eslint no-await-in-loop: "off" */ -/* eslint no-restricted-syntax: "off" */ const trackingStatus = async () => { const pendingTransactions = await FailedTransaction.pendings() if (!pendingTransactions.length) { diff --git a/packages/neuron-wallet/src/services/addresses.ts b/packages/neuron-wallet/src/services/addresses.ts index 552d8472ae..717d88b50d 100644 --- a/packages/neuron-wallet/src/services/addresses.ts +++ b/packages/neuron-wallet/src/services/addresses.ts @@ -95,8 +95,6 @@ export default class AddressService { ) } - /* eslint no-await-in-loop: "off" */ - /* eslint no-restricted-syntax: "off" */ public static updateTxCountAndBalances = async ( addresses: string[], url: string = NodeService.getInstance().core.rpc.node.url diff --git a/packages/neuron-wallet/src/services/sync/block-listener.ts b/packages/neuron-wallet/src/services/sync/block-listener.ts index 92932aa024..e509503e56 100644 --- a/packages/neuron-wallet/src/services/sync/block-listener.ts +++ b/packages/neuron-wallet/src/services/sync/block-listener.ts @@ -77,8 +77,6 @@ export default class BlockListener { }) } - /* eslint no-await-in-loop: "off" */ - /* eslint no-restricted-syntax: "off" */ public setToTip = async () => { const timeout = 5000 let number: bigint = BigInt(0) diff --git a/packages/neuron-wallet/src/services/sync/check-and-save/tx.ts b/packages/neuron-wallet/src/services/sync/check-and-save/tx.ts index 537f236017..a3766fff2e 100644 --- a/packages/neuron-wallet/src/services/sync/check-and-save/tx.ts +++ b/packages/neuron-wallet/src/services/sync/check-and-save/tx.ts @@ -64,8 +64,6 @@ export default class CheckTx { return cells } - /* eslint no-await-in-loop: "off" */ - /* eslint no-restricted-syntax: "warn" */ public filterInputs = async (lockHashes: string[]): Promise => { const inputs = this.tx.inputs! diff --git a/packages/neuron-wallet/src/services/sync/queue.ts b/packages/neuron-wallet/src/services/sync/queue.ts index 68dd417d24..4de91d9042 100644 --- a/packages/neuron-wallet/src/services/sync/queue.ts +++ b/packages/neuron-wallet/src/services/sync/queue.ts @@ -46,7 +46,6 @@ export default class Queue { this.lockHashes = lockHashes } - /* eslint no-await-in-loop: "off" */ public start = async () => { while (!this.stopped) { try { diff --git a/packages/neuron-wallet/src/services/sync/utils.ts b/packages/neuron-wallet/src/services/sync/utils.ts index eeb6d3cd1f..668319ebe8 100644 --- a/packages/neuron-wallet/src/services/sync/utils.ts +++ b/packages/neuron-wallet/src/services/sync/utils.ts @@ -21,7 +21,6 @@ export default class Utils { return new Promise(resolve => setTimeout(resolve, ms)) } - /* eslint no-await-in-loop: "off" */ public static retry = async (times: number, interval: number, callback: any): Promise => { let retryTime = 0 @@ -39,7 +38,6 @@ export default class Utils { return undefined } - /* eslint no-restricted-syntax: "off" */ public static mapSeries = async (array: any[], callback: any): Promise => { const result = [] for (const item of array) { 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 93558e0aa3..8a19b7e5fe 100644 --- a/packages/neuron-wallet/src/startup/sync-block-task/create.ts +++ b/packages/neuron-wallet/src/startup/sync-block-task/create.ts @@ -47,7 +47,6 @@ const loadURL = `file://${path.join(__dirname, 'index.html')}` export { networkSwitchSubject } -/* eslint global-require: "off" */ // create a background task to sync transactions // this task is a renderer process const createSyncBlockTask = () => { From e8bdf539b2694c702998c16e715d8975b861d109 Mon Sep 17 00:00:00 2001 From: James Chen Date: Mon, 28 Oct 2019 13:39:57 +0900 Subject: [PATCH 29/33] refactor: Don't have to async wait rangeForCheck.clearRange() --- packages/neuron-wallet/src/services/sync/queue.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/neuron-wallet/src/services/sync/queue.ts b/packages/neuron-wallet/src/services/sync/queue.ts index 68dd417d24..89687c7bd2 100644 --- a/packages/neuron-wallet/src/services/sync/queue.ts +++ b/packages/neuron-wallet/src/services/sync/queue.ts @@ -136,7 +136,7 @@ export default class Queue { const range = await this.rangeForCheck.getRange() const rangeFirstBlockHeader: BlockHeader = range[0] await this.currentBlockNumber.updateCurrent(BigInt(rangeFirstBlockHeader.number)) - await this.rangeForCheck.clearRange() + this.rangeForCheck.clearRange() await TransactionPersistor.deleteWhenFork(rangeFirstBlockHeader.number) throw new Error(`chain forked: ${checkResult.type}`) } else if (checkResult.type === CheckResultType.BlockHeadersNotMatch) { From 4a9ed704bba9a12e09e204fcc42ffbe81e633c24 Mon Sep 17 00:00:00 2001 From: James Chen Date: Mon, 28 Oct 2019 13:42:43 +0900 Subject: [PATCH 30/33] refactor: Convert several functions from async to normal --- .../services/sync/check-and-save/output.ts | 2 +- .../src/services/sync/check-and-save/tx.ts | 22 +++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/neuron-wallet/src/services/sync/check-and-save/output.ts b/packages/neuron-wallet/src/services/sync/check-and-save/output.ts index 04c49684ce..7537f83dd1 100644 --- a/packages/neuron-wallet/src/services/sync/check-and-save/output.ts +++ b/packages/neuron-wallet/src/services/sync/check-and-save/output.ts @@ -18,7 +18,7 @@ export default class CheckOutput { return this.output } - public checkLockHash = async (lockHashList: string[]): Promise => { + public checkLockHash = (lockHashList: string[]): boolean => { return lockHashList.includes(this.output.lockHash!) } } diff --git a/packages/neuron-wallet/src/services/sync/check-and-save/tx.ts b/packages/neuron-wallet/src/services/sync/check-and-save/tx.ts index 537f236017..6f823187e1 100644 --- a/packages/neuron-wallet/src/services/sync/check-and-save/tx.ts +++ b/packages/neuron-wallet/src/services/sync/check-and-save/tx.ts @@ -25,7 +25,7 @@ export default class CheckTx { } public check = async (lockHashes: string[]): Promise => { - const outputs: Cell[] = await this.filterOutputs(lockHashes) + const outputs: Cell[] = this.filterOutputs(lockHashes) const inputAddresses = await this.filterInputs(lockHashes) const outputAddresses: string[] = outputs.map(output => { @@ -50,17 +50,15 @@ export default class CheckTx { return false } - public filterOutputs = async (lockHashes: string[]) => { - const cells: Cell[] = (await Promise.all( - this.tx.outputs!.map(async output => { - const checkOutput = new CheckOutput(output) - const result = await checkOutput.checkLockHash(lockHashes) - if (result) { - return output - } - return false - }) - )).filter(cell => !!cell) as Cell[] + public filterOutputs = (lockHashes: string[]) => { + const cells: Cell[] = this.tx.outputs!.map(output => { + const checkOutput = new CheckOutput(output) + const result = checkOutput.checkLockHash(lockHashes) + if (result) { + return output + } + return false + }).filter(cell => !!cell) as Cell[] return cells } From 44bb8f834bbed73b01d1ce0bd70b581c4718a0d9 Mon Sep 17 00:00:00 2001 From: James Chen Date: Mon, 28 Oct 2019 13:46:17 +0900 Subject: [PATCH 31/33] refactor: Cache inputs' txs from the same block This optimizes particularly for our test purpose batch txs, which send a few hundreds from and to the same address. --- .../neuron-wallet/src/services/sync/get-blocks.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/neuron-wallet/src/services/sync/get-blocks.ts b/packages/neuron-wallet/src/services/sync/get-blocks.ts index a5017f8375..9fa65b663f 100644 --- a/packages/neuron-wallet/src/services/sync/get-blocks.ts +++ b/packages/neuron-wallet/src/services/sync/get-blocks.ts @@ -9,6 +9,7 @@ import CheckTx from 'services/sync/check-and-save/tx' import { TransactionPersistor } from 'services/tx' import LockUtils from 'models/lock-utils' import { addressesUsedSubject } from './renderer-params' +import logger from 'utils/logger' export default class GetBlocks { private retryTime: number @@ -34,18 +35,24 @@ export default class GetBlocks { } public getTipBlockNumber = async (): Promise => { - const tip: string = await this.core.rpc.getTipBlockNumber() - return tip + return this.core.rpc.getTipBlockNumber() } public checkAndSave = async (blocks: Block[], lockHashes: string[]): Promise => { + const cachedPreviousTxs = new Map() for (const block of blocks) { + logger.debug(`checking block #${block.header.number}, ${block.transactions.length} txs`) for (const tx of block.transactions) { const checkTx = new CheckTx(tx, this.url) const addresses = await checkTx.check(lockHashes) if (addresses.length > 0) { for (const input of tx.inputs!) { - const previousTxWithStatus = await this.getTransaction(input.previousOutput!.txHash) + const previousTxHash = input.previousOutput!.txHash + let previousTxWithStatus = cachedPreviousTxs.get(previousTxHash) + if (!previousTxWithStatus) { + previousTxWithStatus = await this.getTransaction(previousTxHash) + cachedPreviousTxs.set(previousTxHash, previousTxWithStatus) + } const previousTx = TypeConvert.toTransaction(previousTxWithStatus.transaction) const previousOutput = previousTx.outputs![+input.previousOutput!.index] input.lock = previousOutput.lock @@ -60,6 +67,7 @@ export default class GetBlocks { } } } + cachedPreviousTxs.clear() } public retryGetBlock = async (num: string): Promise => { From c5b5c862dfd45d39c1f649ee9e831774d59ff19d Mon Sep 17 00:00:00 2001 From: James Chen Date: Mon, 28 Oct 2019 15:21:28 +0900 Subject: [PATCH 32/33] chore: Bump to v0.23.1 --- lerna.json | 2 +- package.json | 2 +- packages/neuron-ui/package.json | 2 +- packages/neuron-wallet/package.json | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lerna.json b/lerna.json index 5ca6b56c1f..2dda7cfd60 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,7 @@ "packages": [ "packages/*" ], - "version": "0.23.0", + "version": "0.23.1", "npmClient": "yarn", "useWorkspaces": true } diff --git a/package.json b/package.json index 4a3bc1a13d..81517a7c77 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "neuron", "productName": "Neuron", "description": "CKB Neuron Wallet", - "version": "0.23.0", + "version": "0.23.1", "private": true, "author": { "name": "Nervos Core Dev", diff --git a/packages/neuron-ui/package.json b/packages/neuron-ui/package.json index b6f9ba3d03..7167113c56 100644 --- a/packages/neuron-ui/package.json +++ b/packages/neuron-ui/package.json @@ -1,6 +1,6 @@ { "name": "neuron-ui", - "version": "0.23.0", + "version": "0.23.1", "private": true, "author": { "name": "Nervos Core Dev", diff --git a/packages/neuron-wallet/package.json b/packages/neuron-wallet/package.json index 20425379cc..1c2ee19132 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.23.0", + "version": "0.23.1", "private": true, "author": { "name": "Nervos Core Dev", @@ -64,7 +64,7 @@ "electron-devtools-installer": "2.2.4", "electron-notarize": "0.1.1", "lint-staged": "9.2.5", - "neuron-ui": "0.23.0", + "neuron-ui": "0.23.1", "rimraf": "3.0.0", "spectron": "8.0.0", "ts-transformer-imports": "0.4.3", From f53e2589c001419b64c101453272aac96941371d Mon Sep 17 00:00:00 2001 From: James Chen Date: Mon, 28 Oct 2019 15:25:52 +0900 Subject: [PATCH 33/33] docs: Update changelog --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28d0b2fe98..e195f0def8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## [0.23.1](https://github.com/nervosnetwork/neuron/compare/v0.23.0...v0.23.1) (2019-10-28) + + +### Bug Fixes + +* break => continue ([05ae69e](https://github.com/nervosnetwork/neuron/commit/05ae69e)) +* return => break ([97b8ea4](https://github.com/nervosnetwork/neuron/commit/97b8ea4)) + + +### Features + +* Add a few db indices ([83dffb5](https://github.com/nervosnetwork/neuron/commit/83dffb5)) +* display disconnection errors and dismiss them on getting connec… ([#1019](https://github.com/nervosnetwork/neuron/issues/1019)) ([e866c66](https://github.com/nervosnetwork/neuron/commit/e866c66)) +* Optimize output db query ([6e36550](https://github.com/nervosnetwork/neuron/commit/6e36550)) +* stringify the result from api controller ([1f4d4ea](https://github.com/nervosnetwork/neuron/commit/1f4d4ea)) +* **neuron-ui:** add a tooltip to display synchronized block number and the tip block number ([9e52fef](https://github.com/nervosnetwork/neuron/commit/9e52fef)) +* **neuron-ui:** display confirmations of pending transactions in the recent activity list ([9aebede](https://github.com/nervosnetwork/neuron/commit/9aebede)) +* **neuron-ui:** update history list to make it more compact ([35d8b87](https://github.com/nervosnetwork/neuron/commit/35d8b87)) +* **neuron-ui:** update the Send View according to the new transaction fee model. ([dc415f3](https://github.com/nervosnetwork/neuron/commit/dc415f3)) + + + # [0.23.0](https://github.com/nervosnetwork/neuron/compare/v0.22.2...v0.23.0) (2019-10-23)