diff --git a/package.json b/package.json index 82e8c83d7..88694670c 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "prettier": "^2.2.0", "process": "^0.11.10", "react-native-piratechain": "^0.3.2", - "react-native-zcash": "^0.3.2", + "react-native-zcash": "0.4.0", "rimraf": "^3.0.2", "stream-browserify": "^2.0.2", "stream-http": "^3.2.0", @@ -155,6 +155,6 @@ }, "peerDependencies": { "react-native-piratechain": "^0.3.2", - "react-native-zcash": "^0.3.2" + "react-native-zcash": "^0.4.0" } } diff --git a/src/react-native.ts b/src/react-native.ts index d9fd35497..b5a38bc3b 100644 --- a/src/react-native.ts +++ b/src/react-native.ts @@ -7,10 +7,9 @@ import { Synchronizer as PirateSynchronizer } from 'react-native-piratechain' import { - AddressTool as ZcashAddressTool, - KeyTool as ZcashKeyTool, makeSynchronizer as ZcashMakeSynchronizer, - Synchronizer as ZcashSynchronizer + Synchronizer as ZcashSynchronizer, + Tools as ZcashNativeTools } from 'react-native-zcash' import { bridgifyObject, emit, onMethod } from 'yaob' @@ -79,20 +78,20 @@ const makeZcashSynchronizer = async ( const out: ZcashSynchronizer = bridgifyObject({ // @ts-expect-error on: onMethod, - start: async () => { - return await realSynchronizer.start() + deriveUnifiedAddress: async () => { + return await realSynchronizer.deriveUnifiedAddress() }, getTransactions: async blockRange => { return await realSynchronizer.getTransactions(blockRange) }, - rescan: height => { - return realSynchronizer.rescan(height) + rescan: () => { + return realSynchronizer.rescan() }, sendToAddress: async spendInfo => { return await realSynchronizer.sendToAddress(spendInfo) }, - getShieldedBalance: async () => { - return await realSynchronizer.getShieldedBalance() + getBalance: async () => { + return await realSynchronizer.getBalance() }, stop: async () => { return await realSynchronizer.stop() @@ -104,8 +103,7 @@ const makeZcashSynchronizer = async ( export function makePluginIo(): EdgeOtherMethods { bridgifyObject(PiratechainKeyTool) bridgifyObject(PiratechainAddressTool) - bridgifyObject(ZcashKeyTool) - bridgifyObject(ZcashAddressTool) + bridgifyObject(ZcashNativeTools) return { async fetchText(uri: string, opts: Object) { @@ -128,8 +126,7 @@ export function makePluginIo(): EdgeOtherMethods { } }), zcash: bridgifyObject({ - KeyTool: ZcashKeyTool, - AddressTool: ZcashAddressTool, + Tools: ZcashNativeTools, async makeSynchronizer(config: ZcashInitializerConfig) { return await makeZcashSynchronizer(config) } diff --git a/src/zcash/ZcashEngine.ts b/src/zcash/ZcashEngine.ts index f6f2cd546..0579d08c6 100644 --- a/src/zcash/ZcashEngine.ts +++ b/src/zcash/ZcashEngine.ts @@ -3,6 +3,7 @@ import { EdgeCurrencyEngine, EdgeCurrencyEngineOptions, EdgeEnginePrivateKeyOptions, + EdgeFreshAddress, EdgeSpendInfo, EdgeTransaction, EdgeWalletInfo, @@ -35,18 +36,21 @@ export class ZcashEngine extends CurrencyEngine< pluginId: string networkInfo: ZcashNetworkInfo otherData!: ZcashWalletOtherData - synchronizer!: ZcashSynchronizer synchronizerStatus!: ZcashSynchronizerStatus availableZatoshi!: string initialNumBlocksToDownload!: number initializer!: ZcashInitializerConfig - alias!: string progressRatio!: number queryMutex: boolean makeSynchronizer: ( config: ZcashInitializerConfig ) => Promise + // Synchronizer management + started: boolean + stopSyncing?: (value: number | PromiseLike) => void + synchronizer?: ZcashSynchronizer + constructor( env: PluginEnvironment, tools: ZcashTools, @@ -60,6 +64,8 @@ export class ZcashEngine extends CurrencyEngine< this.networkInfo = networkInfo this.makeSynchronizer = makeSynchronizer this.queryMutex = false + + this.started = false } setOtherData(raw: any): void { @@ -67,7 +73,7 @@ export class ZcashEngine extends CurrencyEngine< } initData(): void { - const { birthdayHeight, alias } = this.initializer + const { birthdayHeight } = this.initializer // walletLocalData if (this.otherData.blockRange.first === 0) { @@ -78,7 +84,6 @@ export class ZcashEngine extends CurrencyEngine< } // Engine variables - this.alias = alias this.initialNumBlocksToDownload = -1 this.synchronizerStatus = 'DISCONNECTED' this.availableZatoshi = '0' @@ -86,6 +91,7 @@ export class ZcashEngine extends CurrencyEngine< } initSubscriptions(): void { + if (this.synchronizer == null) return this.synchronizer.on('update', async payload => { const { lastDownloadedHeight, scanProgress, networkBlockHeight } = payload this.onUpdateBlockHeight(networkBlockHeight) @@ -175,10 +181,8 @@ export class ZcashEngine extends CurrencyEngine< } async startEngine(): Promise { - this.initData() - this.synchronizer = await this.makeSynchronizer(this.initializer) - await this.synchronizer.start() - this.initSubscriptions() + this.engineOn = true + this.started = true await super.startEngine() } @@ -188,9 +192,9 @@ export class ZcashEngine extends CurrencyEngine< } async queryBalance(): Promise { - if (!this.isSynced()) return + if (!this.isSynced() || this.synchronizer == null) return try { - const balances = await this.synchronizer.getShieldedBalance() + const balances = await this.synchronizer.getBalance() if (balances.totalZatoshi === '-1') return this.availableZatoshi = balances.availableZatoshi this.updateBalance(this.currencyInfo.currencyCode, balances.totalZatoshi) @@ -201,6 +205,7 @@ export class ZcashEngine extends CurrencyEngine< } async queryTransactions(): Promise { + if (this.synchronizer == null) return try { let first = this.otherData.blockRange.first let last = this.otherData.blockRange.last @@ -270,8 +275,37 @@ export class ZcashEngine extends CurrencyEngine< this.addTransaction(this.currencyInfo.currencyCode, edgeTransaction) } + async syncNetwork(opts: EdgeEnginePrivateKeyOptions): Promise { + if (!this.started) return 1000 + + const zcashPrivateKeys = asZcashPrivateKeys(this.currencyInfo.pluginId)( + opts?.privateKeys + ) + + const { rpcNode } = this.networkInfo + this.initializer = { + mnemonicSeed: zcashPrivateKeys.mnemonic, + birthdayHeight: zcashPrivateKeys.birthdayHeight, + alias: this.walletInfo.keys.publicKey.slice(0, 99), + ...rpcNode + } + + this.synchronizer = await this.makeSynchronizer(this.initializer) + this.initData() + this.initSubscriptions() + + return await new Promise(resolve => { + this.stopSyncing = resolve + }) + } + async killEngine(): Promise { - await this.synchronizer.stop() + this.started = false + if (this.stopSyncing != null) { + await this.stopSyncing(1000) + this.stopSyncing = undefined + } + await this.synchronizer?.stop() await super.killEngine() } @@ -285,8 +319,10 @@ export class ZcashEngine extends CurrencyEngine< await this.clearBlockchainCache() await this.startEngine() this.synchronizer - .rescan(this.walletInfo.keys.birthdayHeight) + ?.rescan() .catch((e: any) => this.warn('resyncBlockchain failed: ', e)) + this.initData() + this.synchronizerStatus = 'SYNCING' } async getMaxSpendable(): Promise { @@ -364,6 +400,7 @@ export class ZcashEngine extends CurrencyEngine< edgeTransaction: EdgeTransaction, opts?: EdgeEnginePrivateKeyOptions ): Promise { + if (this.synchronizer == null) throw new Error('Synchronizer undefined') const zcashPrivateKeys = asZcashPrivateKeys(this.pluginId)( opts?.privateKeys ) @@ -382,7 +419,7 @@ export class ZcashEngine extends CurrencyEngine< toAddress: spendTarget.publicAddress, memo: spendTarget.memo ?? spendTarget.uniqueIdentifier ?? '', fromAccountIndex: 0, - spendingKey: zcashPrivateKeys.spendKey + mnemonicSeed: zcashPrivateKeys.mnemonic } try { @@ -398,20 +435,15 @@ export class ZcashEngine extends CurrencyEngine< return edgeTransaction } - async loadEngine(): Promise { - const { walletInfo } = this - await super.loadEngine() - this.engineOn = true - - const { rpcNode } = this.networkInfo - this.initializer = { - fullViewingKey: walletInfo.keys.unifiedViewingKeys, - birthdayHeight: walletInfo.keys.birthdayHeight, - alias: walletInfo.keys.publicKey, - ...rpcNode + async getFreshAddress(): Promise { + if (this.synchronizer == null) throw new Error('Synchronizer undefined') + const unifiedAddress = await this.synchronizer.deriveUnifiedAddress() + return { + publicAddress: unifiedAddress } } } + export async function makeCurrencyEngine( env: PluginEnvironment, tools: ZcashTools, diff --git a/src/zcash/ZcashTools.ts b/src/zcash/ZcashTools.ts index da557f01d..589753eae 100644 --- a/src/zcash/ZcashTools.ts +++ b/src/zcash/ZcashTools.ts @@ -1,5 +1,5 @@ import { div } from 'biggystring' -import { entropyToMnemonic, mnemonicToSeed, validateMnemonic } from 'bip39' +import { entropyToMnemonic, validateMnemonic } from 'bip39' import { Buffer } from 'buffer' import { EdgeCurrencyInfo, @@ -12,10 +12,7 @@ import { EdgeWalletInfo, JsonObject } from 'edge-core-js/types' -import { - AddressTool as AddressToolType, - KeyTool as KeyToolType -} from 'react-native-zcash' +import { Tools as ToolsType } from 'react-native-zcash' import { PluginEnvironment } from '../common/innerPlugin' import { asIntegerString } from '../common/types' @@ -34,9 +31,7 @@ export class ZcashTools implements EdgeCurrencyTools { currencyInfo: EdgeCurrencyInfo io: EdgeIo networkInfo: ZcashNetworkInfo - - KeyTool: typeof KeyToolType - AddressTool: typeof AddressToolType + nativeTools: typeof ToolsType constructor(env: PluginEnvironment) { const { builtinTokens, currencyInfo, io, networkInfo } = env @@ -51,8 +46,7 @@ export class ZcashTools implements EdgeCurrencyTools { } const { Tools } = RNAccountbased.zcash - this.KeyTool = KeyTool - this.AddressTool = AddressTool + this.nativeTools = Tools } async getDisplayPrivateKey( @@ -65,12 +59,12 @@ export class ZcashTools implements EdgeCurrencyTools { async getDisplayPublicKey(publicWalletInfo: EdgeWalletInfo): Promise { const { keys } = asSafeZcashWalletInfo(publicWalletInfo) - return keys.unifiedViewingKeys?.extfvk + return keys.publicKey } async getNewWalletBirthdayBlockheight(): Promise { try { - return await this.KeyTool.getBirthdayHeight( + return await this.nativeTools.getBirthdayHeight( this.networkInfo.rpcNode.defaultHost, this.networkInfo.rpcNode.defaultPort ) @@ -80,10 +74,7 @@ export class ZcashTools implements EdgeCurrencyTools { } async isValidAddress(address: string): Promise { - return ( - (await this.AddressTool.isValidShieldedAddress(address)) || - (await this.AddressTool.isValidTransparentAddress(address)) - ) + return await this.nativeTools.isValidAddress(address) } // will actually use MNEMONIC version of private key @@ -95,13 +86,6 @@ export class ZcashTools implements EdgeCurrencyTools { const isValid = validateMnemonic(userInput) if (!isValid) throw new Error(`Invalid ${this.currencyInfo.currencyCode} mnemonic`) - const hexBuffer = await mnemonicToSeed(userInput) - const hex = hexBuffer.toString('hex') - const spendKey = await this.KeyTool.deriveSpendingKey( - hex, - this.networkInfo.rpcNode.networkName - ) - if (typeof spendKey !== 'string') throw new Error('Invalid spendKey type') // Get current network height for the birthday height const currentNetworkHeight = await this.getNewWalletBirthdayBlockheight() @@ -122,7 +106,6 @@ export class ZcashTools implements EdgeCurrencyTools { return { [`${pluginId}Mnemonic`]: userInput, - [`${pluginId}SpendKey`]: spendKey, [`${pluginId}BirthdayHeight`]: height } } @@ -157,21 +140,14 @@ export class ZcashTools implements EdgeCurrencyTools { if (typeof mnemonic !== 'string') { throw new Error('InvalidMnemonic') } - const hexBuffer = await mnemonicToSeed(mnemonic) - const hex = hexBuffer.toString('hex') - const unifiedViewingKeys: UnifiedViewingKey = - await this.KeyTool.deriveViewingKey( - hex, + const unifiedViewingKey: UnifiedViewingKey = + await this.nativeTools.deriveViewingKey( + mnemonic, this.networkInfo.rpcNode.networkName ) - const shieldedAddress = await this.AddressTool.deriveShieldedAddress( - unifiedViewingKeys.extfvk, - this.networkInfo.rpcNode.networkName - ) return { birthdayHeight: zcashPrivateKeys.birthdayHeight, - publicKey: shieldedAddress, - unifiedViewingKeys + publicKey: unifiedViewingKey } } diff --git a/src/zcash/zcashInfo.ts b/src/zcash/zcashInfo.ts index 1f2a603ad..45039aca4 100644 --- a/src/zcash/zcashInfo.ts +++ b/src/zcash/zcashInfo.ts @@ -40,6 +40,7 @@ export const currencyInfo: EdgeCurrencyInfo = { metaTokens: [], // Deprecated + unsafeSyncNetwork: true, unsafeBroadcastTx: true } diff --git a/src/zcash/zcashTypes.ts b/src/zcash/zcashTypes.ts index 9b645cf1f..26b4a353a 100644 --- a/src/zcash/zcashTypes.ts +++ b/src/zcash/zcashTypes.ts @@ -28,7 +28,7 @@ export interface ZcashSpendInfo { toAddress: string memo: string fromAccountIndex: number - spendingKey: string + mnemonicSeed: string } export interface ZcashTransaction { @@ -59,7 +59,7 @@ export interface ZcashInitializerConfig { networkName: ZcashNetworkName defaultHost: string defaultPort: number - fullViewingKey: UnifiedViewingKey + mnemonicSeed: string alias: string birthdayHeight: number } @@ -67,10 +67,7 @@ export interface ZcashInitializerConfig { export type ZcashSynchronizerStatus = | 'STOPPED' | 'DISCONNECTED' - | 'DOWNLOADING' - | 'VALIDATING' - | 'SCANNING' - | 'ENHANCING' + | 'SYNCING' | 'SYNCED' export interface ZcashStatusEvent { @@ -113,10 +110,11 @@ export interface ZcashSynchronizer { }> start: () => Promise stop: () => Promise + deriveUnifiedAddress: () => Promise getTransactions: (arg: ZcashBlockRange) => Promise - rescan: (arg: number) => Promise + rescan: () => Promise sendToAddress: (arg: ZcashSpendInfo) => Promise - getShieldedBalance: () => Promise + getBalance: () => Promise } export type ZcashMakeSynchronizer = () => ( @@ -125,11 +123,7 @@ export type ZcashMakeSynchronizer = () => ( export const asZecPublicKey = asObject({ birthdayHeight: asNumber, - publicKey: asString, - unifiedViewingKeys: asObject({ - extfvk: asString, - extpub: asString - }) + publicKey: asString }) export type SafeZcashWalletInfo = ReturnType @@ -137,7 +131,6 @@ export const asSafeZcashWalletInfo = asWalletInfo(asZecPublicKey) export interface ZcashPrivateKeys { mnemonic: string - spendKey: string birthdayHeight: number } export const asZcashPrivateKeys = ( @@ -145,7 +138,6 @@ export const asZcashPrivateKeys = ( ): Cleaner => { const asKeys = asObject({ [`${pluginId}Mnemonic`]: asString, - [`${pluginId}SpendKey`]: asString, [`${pluginId}BirthdayHeight`]: asNumber }) @@ -154,14 +146,12 @@ export const asZcashPrivateKeys = ( const clean = asKeys(raw) return { mnemonic: clean[`${pluginId}Mnemonic`] as string, - spendKey: clean[`${pluginId}SpendKey`] as string, birthdayHeight: clean[`${pluginId}BirthdayHeight`] as number } }, clean => { return { [`${pluginId}Mnemonic`]: clean.mnemonic, - [`${pluginId}SpendKey`]: clean.spendKey, [`${pluginId}BirthdayHeight`]: clean.birthdayHeight } } diff --git a/yarn.lock b/yarn.lock index cef35f7d7..7ce9960e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6681,10 +6681,10 @@ react-native-piratechain@^0.3.2: dependencies: rfc4648 "^1.3.0" -react-native-zcash@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/react-native-zcash/-/react-native-zcash-0.3.2.tgz#f2b77630ab104771e844b07e43269a0fb3d33a63" - integrity sha512-6qukZUUDzEeuVL7h9jq1wek2UDo2KT3QvDuw7FISeoXUug1LAAgPvLE36v51smcdNQamLMBqJWAZM1tS3B5xTw== +react-native-zcash@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/react-native-zcash/-/react-native-zcash-0.4.0.tgz#cd6cc045673a5bf843a2c5e342b66ee243239555" + integrity sha512-xL87jBLiCqqpKJ4Mir/cC4M+poJYvFu6z9Mad0rGO+zOxfBkVuvunm4kYBtx96bqVHDH3jX9CukE888MBZzFAQ== dependencies: rfc4648 "^1.3.0"