From 8778cb430f5a7c3d664ff060c979a78c59476cc5 Mon Sep 17 00:00:00 2001 From: xaur <24484727+xaur@users.noreply.github.com> Date: Fri, 30 Jun 2023 20:12:02 +0000 Subject: [PATCH 01/15] Remove odd git submodule (#3886) Looks like an unintended submodule from e446972. --- .yarncahce/v6/.tmp/5d8efc829b465332f7e452d93a85c4bc | 1 - 1 file changed, 1 deletion(-) delete mode 160000 .yarncahce/v6/.tmp/5d8efc829b465332f7e452d93a85c4bc diff --git a/.yarncahce/v6/.tmp/5d8efc829b465332f7e452d93a85c4bc b/.yarncahce/v6/.tmp/5d8efc829b465332f7e452d93a85c4bc deleted file mode 160000 index 6a024e4769..0000000000 --- a/.yarncahce/v6/.tmp/5d8efc829b465332f7e452d93a85c4bc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6a024e4769ae1508f1c5e7a19f75d4a287a13467 From 50de9f17af2f2dc18ba6e0cb925275c5a03e1124 Mon Sep 17 00:00:00 2001 From: JoeGruffins <34998433+JoeGruffins@users.noreply.github.com> Date: Mon, 18 Sep 2023 21:32:19 +0900 Subject: [PATCH 02/15] multi: Add ledger backend functions. (#3869) --- app/actions/LedgerActions.js | 205 +++++++++++++++++++++++++++++++ app/helpers/ledger.js | 164 +++++++++++++++++++++++++ app/main.development.js | 12 ++ package.json | 2 + test/mocks/walletMock.js | 94 ++++++++++++++ test/unit/helpers/ledger.spec.js | 26 ++++ yarn.lock | 152 +++++++++++++++++++++-- 7 files changed, 644 insertions(+), 11 deletions(-) create mode 100644 app/actions/LedgerActions.js create mode 100644 app/helpers/ledger.js create mode 100644 test/unit/helpers/ledger.spec.js diff --git a/app/actions/LedgerActions.js b/app/actions/LedgerActions.js new file mode 100644 index 0000000000..4c9d7e1b09 --- /dev/null +++ b/app/actions/LedgerActions.js @@ -0,0 +1,205 @@ +import TransportWebUSB from "@ledgerhq/hw-transport-webusb"; +import { createTransaction } from "@ledgerhq/hw-app-btc/lib/createTransaction"; +import Btc from "@ledgerhq/hw-app-btc"; +import * as ledgerHelpers from "../helpers/ledger"; +import { wallet } from "wallet-preload-shim"; +import { publishTransactionAttempt } from "./ControlActions"; +import { hexToBytes } from "helpers"; +import { + SIGNTX_ATTEMPT, + SIGNTX_FAILED, + SIGNTX_SUCCESS +} from "./ControlActions"; + +const coin = "decred"; + +import * as selectors from "selectors"; +import * as cfgConstants from "constants/config"; + +export const LDG_LEDGER_ENABLED = "LDG_LEDGER_ENABLED"; +export const LDG_WALLET_CLOSED = "LDG_WALLET_CLOSED"; + +// This is an error's message when an app is open but we are trying to get +// device info. +// const DEVICE_ON_DASHBOARD_EXPECTED = "DeviceOnDashboardExpected"; + +// enableLedger only sets a value in the config. Ledger connections are made +// per action then dropped. +export const enableLedger = () => (dispatch, getState) => { + const walletName = selectors.getWalletName(getState()); + + if (walletName) { + const config = wallet.getWalletCfg( + selectors.isTestNet(getState()), + walletName + ); + config.set(cfgConstants.LEDGER, true); + } + + dispatch({ type: LDG_LEDGER_ENABLED }); + + connect()(dispatch, getState); +}; + +export const LDG_CONNECT_ATTEMPT = "LDG_CONNECT_ATTEMPT"; +export const LDG_CONNECT_FAILED = "LDG_CONNECT_FAILED"; +export const LDG_CONNECT_SUCCESS = "LDG_CONNECT_SUCCESS"; + +// connect only checks that a connection does not error, so a device exists and +// is plugged in. +export const connect = () => async (dispatch /*, getState*/) => { + dispatch({ type: LDG_CONNECT_ATTEMPT }); + try { + await doWithTransport(async () => {}); + } catch (error) { + dispatch({ type: LDG_CONNECT_FAILED }); + throw error; + } + dispatch({ type: LDG_CONNECT_SUCCESS }); +}; + +export const LDG_LEDGER_DISABLED = "LDG_LEDGER_DISABLED"; + +// disableLedger disables ledger integration for the current wallet. Note +// that it does **not** disable in the config, so the wallet will restart as a +// ledger wallet next time it's opened. +export const disableLedger = () => (dispatch) => { + dispatch({ type: LDG_LEDGER_DISABLED }); +}; + +export const LDG_NOCONNECTEDDEVICE = "LDG_NOCONNECTEDDEVICE"; + +export const alertNoConnectedDevice = () => (dispatch) => { + dispatch({ type: LDG_NOCONNECTEDDEVICE }); +}; + +// checkLedgerIsDcrwallet verifies whether the wallet currently running on +// dcrwallet (presumably a watch only wallet created from a ledger provided +// xpub) is the same wallet as the one of the currently connected ledger. This +// function throws an error if they are not the same. +// This is useful for making sure, prior to performing some wallet related +// function such as transaction signing, that ledger will correctly perform the +// operation. +// Note that this might trigger pin/passphrase modals, depending on the current +// ledger configuration. +// The way the check is performed is by generating the first address from the +// ledger wallet and then validating this address agains dcrwallet, ensuring +// this is an owned address at the appropriate branch/index. +// This check is only valid for a single session (ie, a single execution of +// `deviceRun`) as the physical device might change between sessions. +const checkLedgerIsDcrwallet = () => async (dispatch, getState) => { + const { + grpc: { walletService } + } = getState(); + + const path = ledgerHelpers.addressPath(0, 0); + const payload = await getAddress(path); + const addr = payload.bitcoinAddress; + + const addrValidResp = await wallet.validateAddress(walletService, addr); + if (!addrValidResp.isValid) + throw "Ledger provided an invalid address " + addr; + + if (!addrValidResp.isMine) + throw "Ledger and dcrwallet not running from the same extended public key"; + + if (addrValidResp.index !== 0) throw "Wallet replied with wrong index."; +}; + +export const signTransactionAttemptLedger = + (rawUnsigTx) => async (dispatch, getState) => { + dispatch({ type: SIGNTX_ATTEMPT }); + const { + grpc: { walletService } + } = getState(); + const chainParams = selectors.chainParams(getState()); + + try { + const arg = await ledgerHelpers.signArg( + rawUnsigTx, + chainParams, + walletService, + dispatch + ); + + await dispatch(checkLedgerIsDcrwallet()); + const signedRaw = await createTx(arg); + if (signedRaw.message) { + throw signedRaw.message; + } + + dispatch({ type: SIGNTX_SUCCESS }); + dispatch(publishTransactionAttempt(hexToBytes(signedRaw))); + } catch (error) { + dispatch({ error, type: SIGNTX_FAILED }); + } + }; + +export const LDG_GETWALLETCREATIONMASTERPUBKEY_ATTEMPT = + "LDG_GETWALLETCREATIONMASTERPUBKEY_ATTEMPT"; +export const LDG_GETWALLETCREATIONMASTERPUBKEY_FAILED = + "LDG_GETWALLETCREATIONMASTERPUBKEY_FAILED"; +export const LDG_GETWALLETCREATIONMASTERPUBKEY_SUCCESS = + "LDG_GETWALLETCREATIONMASTERPUBKEY_SUCCESS"; + +export const getWalletCreationMasterPubKey = + () => async (dispatch /*, getState*/) => { + dispatch({ type: LDG_GETWALLETCREATIONMASTERPUBKEY_ATTEMPT }); + // TODO: Enable on mainnet. + const isTestnet = true; + try { + const payload = await getPubKey(isTestnet); + const hdpk = ledgerHelpers.fixPubKeyChecksum(payload); + dispatch({ type: LDG_GETWALLETCREATIONMASTERPUBKEY_SUCCESS }); + return hdpk; + } catch (error) { + dispatch({ error, type: LDG_GETWALLETCREATIONMASTERPUBKEY_FAILED }); + throw error; + } + }; + +function doWithTransport(fn) { + return TransportWebUSB.create() + .then((transport) => { + return fn(transport).then((r) => + transport + .close() + .catch((/*e*/) => {}) // throw? + .then(() => r) + ); + }) + .catch((e) => { + throw e; + }); +} + +function getAddress(path) { + const fn = async (transport) => { + const btc = new Btc({ transport, currency: coin }); + return await btc.getWalletPublicKey(path, { + verify: false + }); + }; + return doWithTransport(fn); +} + +function getPubKey(isTestnet) { + const fn = async (transport) => { + const btc = new Btc({ transport, currency: coin }); + let hdPublicKeyID = 0x02fda926; // dpub + if (isTestnet) { + hdPublicKeyID = 0x043587d1; // tpub + } + return await btc.getWalletXpub({ + path: "44'/42'/0'", + xpubVersion: hdPublicKeyID + }); + }; + return doWithTransport(fn); +} + +function createTx(arg) { + return doWithTransport((transport) => { + return createTransaction(transport, arg); + }); +} diff --git a/app/helpers/ledger.js b/app/helpers/ledger.js new file mode 100644 index 0000000000..3948b27e89 --- /dev/null +++ b/app/helpers/ledger.js @@ -0,0 +1,164 @@ +import { wallet } from "wallet-preload-shim"; +import { strHashToRaw } from "helpers"; +import { default as blake } from "blake-hash"; +import * as bs58 from "bs58"; +import toBuffer from "typedarray-to-buffer"; +import { getTxFromInputs } from "../actions/TransactionActions"; + +export function addressPath(branch, index) { + const prefix = "44'/42'/0'/"; + const i = (index || 0).toString(); + const b = (branch || 0).toString(); + return prefix + b + "/" + i; +} + +// fixPubKeyChecksum replaces the sha256 checksum, or last four bytes, of a +// pubkey with a blake256 checksum. +export function fixPubKeyChecksum(pubKey) { + const buff = bs58.decode(pubKey).slice(0, -4); + const firstPass = blake("blake256").update(Buffer.from(buff)).digest(); + const secondPass = blake("blake256").update(firstPass).digest(); + const fullSerialize = Buffer.concat([ + Buffer.from(buff), + secondPass.slice(0, 4) + ]); + return bs58.encode(fullSerialize); +} + +function writeUint16LE(n) { + const buff = new Buffer(2); + buff.writeUInt16LE(n, 0); + return buff; +} + +function writeUint32LE(n) { + const buff = new Buffer(4); + buff.writeUInt32LE(n, 0); + return buff; +} + +function writeUint64LE(n) { + const buff = new Buffer(8); + const lower = 0xffffffff & n; + // bitshift right (>>>) does not seem to throw away the lower half, so + // dividing and throwing away the remainder. + const upper = Math.floor(n / 0xffffffff); + buff.writeUInt32LE(lower, 0); + buff.writeUInt32LE(upper, 4); + return buff; +} + +function inputToTx(tx) { + const inputs = []; + for (const inp of tx.inputs) { + const sequence = writeUint32LE(inp.sequence); + const tree = new Uint8Array(1); + tree[0] = inp.outputTree; + const prevout = new Uint8Array(36); + prevout.set(strHashToRaw(inp.prevTxId), 0); + prevout.set(writeUint32LE(inp.outputIndex), 32); + const input = { + prevout: toBuffer(prevout), + script: toBuffer(new Uint8Array(25)), + sequence: sequence, + tree: toBuffer(tree) + }; + inputs.push(input); + } + const outputs = []; + for (const out of tx.outputs) { + const output = { + amount: writeUint64LE(out.value), + script: out.script + }; + outputs.push(output); + } + return { + version: writeUint32LE(tx.version), // Pretty sure this is a uint16 but ledger does not want that. + inputs: inputs, + outputs: outputs, + locktime: writeUint32LE(tx.lockTime), + nExpiryHeight: writeUint32LE(tx.expiry) + }; +} + +function createPrefix(tx) { + const numOuts = tx.outputs.length; + // TODO: Allow more outputs if possible. + if (numOuts > 2) { + throw "more than two outputs is not expected"; + } + let buffLen = 1; + for (const out of tx.outputs) { + buffLen += 11 + out.script.length; + } + const buff = new Uint8Array(buffLen); // 1 varInt + ( 8 value + 2 tx version + 1 varInt + (23/25?) variable script length) * number of outputs + let i = 0; + buff[i] = numOuts; + i += 1; + for (const out of tx.outputs) { + buff.set(writeUint64LE(out.value), i); + i += 8; + buff.set(writeUint16LE(out.version), i); + i += 2; + // TODO: Clean this up for production? Should use smarter logic to get varInts? + buff[i] = out.script.length; // varInt for 23/25 bytes + i += 1; + buff.set(out.script, i); + i += out.script.length; + } + return toBuffer(buff); +} + +export async function signArg(txHex, chainParams, walletService, dispatch) { + const tx = await wallet.decodeTransactionLocal(txHex, chainParams); + const inputTxs = await dispatch(getTxFromInputs(tx)); + const inputs = []; + const paths = []; + for (const inp of tx.inputs) { + let verboseInp; + for (const it of inputTxs) { + if (it.hash === inp.prevTxId) { + verboseInp = it; + break; + } + } + if (!verboseInp) { + throw "cound not find input"; + } + const prevOut = inputToTx(verboseInp); + const idx = inp.outputIndex; + inputs.push([prevOut, idx]); + const addr = verboseInp.outputs[idx].decodedScript.address; + const val = await wallet.validateAddress(walletService, addr); + const acct = val.accountNumber.toString(); + const branch = val.isInternal ? "1" : "0"; + const index = val.index.toString(); + paths.push("44'/42'/" + acct + "'/" + branch + "/" + index); + } + let changePath = null; + for (const out of tx.outputs) { + const addr = out.decodedScript.address; + const val = await wallet.validateAddress(walletService, addr); + if (!val.isInternal) { + continue; + } // assume the internal address is change + const acct = val.accountNumber.toString(); + const index = val.index.toString(); + changePath = "44'/42'/" + acct + "'/1/" + index; + break; + } + + return { + inputs: inputs, + associatedKeysets: paths, + changePath: changePath, + outputScriptHex: createPrefix(tx), + lockTime: tx.lockTime, + sigHashType: 1, // SIGHASH_ALL + segwit: false, + expiryHeight: writeUint32LE(tx.expiry), + useTrustedInputForSegwit: false, + additionals: ["decred"] + }; +} diff --git a/app/main.development.js b/app/main.development.js index a7c1344abb..7060ef2e48 100644 --- a/app/main.development.js +++ b/app/main.development.js @@ -873,6 +873,18 @@ app.on("ready", async () => { height: 1000, icon: __dirname + "/dcrdex.png" }); + + mainWindow.webContents.session.setDevicePermissionHandler((details) => { + // Allow Ledger devices which share a unique vendor ID. + if ( + details.deviceType === "usb" && + details.device && + details.device.vendorId === 0x2c97 + ) { + return true; + } + return false; + }); }); app.on("before-quit", async (event) => { diff --git a/package.json b/package.json index 1332d1e9b2..7a92245444 100644 --- a/package.json +++ b/package.json @@ -273,6 +273,8 @@ "@formatjs/intl-utils": "^1.6.0", "@grpc/grpc-js": "1.7.3", "@hot-loader/react-dom": "16.14.0", + "@ledgerhq/hw-app-btc": "^10.0.5", + "@ledgerhq/hw-transport-webusb": "^6.27.16", "@peculiar/webcrypto": "1.4.3", "@xstate/react": "^0.8.1", "blake-hash": "^2.0.0", diff --git a/test/mocks/walletMock.js b/test/mocks/walletMock.js index 54f59301c3..21c897b9fb 100644 --- a/test/mocks/walletMock.js +++ b/test/mocks/walletMock.js @@ -28,3 +28,97 @@ export const getAvailableWallets = jest.fn(() => []); export const getPreviousWallet = jest.fn(() => null); export const decodeRawTransaction = jest.fn((...args) => drt(...args)); + +const vaMap = { + TsVRPhzyuvCuxPipBx2YeLarrTPPEAsv4Bh: { + isValid: true, + isMine: true, + accountNumber: 0, + pubKeyAddr: "TkQ4CFL6gY6iF87MnP1a2hrFZeZ9wmR7uU1aaiVPBiVEE8iaMCXyK", + pubKey: "A20UfEkHp8ZG4WZc24AQ4c0gYrkkwGOKeeW9LDG75yij", + isScript: false, + pkScriptAddrsList: [], + scriptType: 2, + payToAddrScript: "dqkUMEbI+i8hd+yvj70Ll1XrcOkp0peIrA==", + sigsRequired: 0, + isInternal: true, + index: 71 + }, + TsiHcAi5c2CpMSmQm8BasAoYgMr91y6Pq3h: { + isValid: true, + isMine: true, + accountNumber: 0, + pubKeyAddr: "TkQ3bghS4na8dL6kuXwTVvE3Jhe7LH2NbxPbVMkFYZtz412vsk8Qu", + pubKey: "Ax6Z8n4/KE0W0bKpdqksPg0KIDxv8sXXRFStW8sgFz/V", + isScript: false, + pkScriptAddrsList: [], + scriptType: 2, + payToAddrScript: "dqkUvWd+epUkLC1UIn45KM7Yicw6rqeIrA==", + sigsRequired: 0, + isInternal: true, + index: 72 + } +}; + +const dtlReturn = { + version: 1, + serType: 0, + numInputs: 1, + inputs: [ + { + prevTxId: + "d29e701778f726ca5f1b50dddb9529f61911732586fa032eee9c2418f617ce89", + outputIndex: 0, + outputTree: 0, + sequence: 4294967295, + index: 0, + valueIn: 106606220, + blockHeight: 0, + blockIndex: 4294967295, + sigScript: {} + } + ], + numOutputs: 2, + outputs: [ + { + value: 105373690, + version: 0, + script: Buffer.from([ + 118, 169, 20, 189, 103, 126, 122, 149, 36, 44, 45, 84, 34, 126, 57, 40, + 206, 216, 137, 204, 58, 174, 167, 136, 172 + ]), + index: 0, + decodedScript: { + scriptClass: 2, + address: "TsiHcAi5c2CpMSmQm8BasAoYgMr91y6Pq3h", + requiredSig: 1, + asm: "OP_DUP OP_HASH160 OP_DATA_20 bd677e7a95242c2d54227e3928ced889cc3aaea7 OP_EQUALVERIFY OP_CHECKSIG" + } + }, + { + value: 1230000, + version: 0, + script: Buffer.from([ + 118, 169, 20, 210, 81, 146, 28, 152, 87, 121, 24, 21, 233, 119, 80, 175, + 206, 164, 143, 189, 48, 86, 138, 136, 172 + ]), + index: 1, + decodedScript: { + scriptClass: 2, + address: "TskCC6UkcKfBRsrJVPhSjwGjn7ptD34HA4T", + requiredSig: 1, + asm: "OP_DUP OP_HASH160 OP_DATA_20 d251921c9857791815e97750afcea48fbd30568a OP_EQUALVERIFY OP_CHECKSIG" + } + } + ], + lockTime: 0, + expiry: 0 +}; + +export const decodeTransactionLocal = jest.fn(() => { + return dtlReturn; +}); +export const validateAddress = jest.fn((...args) => { + return vaMap[args[1]]; +}); +export const getTxFromInputs = jest.fn(() => null); diff --git a/test/unit/helpers/ledger.spec.js b/test/unit/helpers/ledger.spec.js new file mode 100644 index 0000000000..4a9d2ff655 --- /dev/null +++ b/test/unit/helpers/ledger.spec.js @@ -0,0 +1,26 @@ +import { addressPath, fixPubKeyChecksum, signArg } from "helpers/ledger"; + +test("test ledger address path", () => { + expect(addressPath(1, 2)).toStrictEqual("44'/42'/0'/1/2"); +}); + +test("test ledger pubkey to hd key", () => { + // testnet + expect(fixPubKeyChecksum("tpubVpeRVBDM14ydoJ7jCzTayDHiP6CU4NcbKEW6bWQGSghmi14BfNx3omc6GjV3AxbtkTYcPAedw48XywVgSyY8H9ef73zWWtoZ6MCMLLJiUyq")).toStrictEqual("tpubVpeRVBDM14ydoJ7jCzTayDHiP6CU4NcbKEW6bWQGSghmi14BfNx3omc6GjV3AxbtkTYcPAedw48XywVgSyY8H9ef73zWWtoZ6MCMLGM6GV3"); + // mainet + expect(fixPubKeyChecksum("dpubZFs9f5ex4qxoiqCHVEnotAaYpZhCgwDAndMyLicfcXJysBmNxMyx7X3QHW2NiUN14KnekWcMQt4XwyF5wAqDPzbLcv59mQTRhL6foyvvqLK")).toStrictEqual("dpubZFs9f5ex4qxoiqCHVEnotAaYpZhCgwDAndMyLicfcXJysBmNxMyx7X3QHW2NiUN14KnekWcMQt4XwyF5wAqDPzbLcv59mQTRhL6fp1csmnS"); +}); + +function dispatchFn (inputs) { + return function () { + return inputs; + }; +} + +test("test ledger constructing arg to sign", async () => { + const txHex = "010000000189ce17f618249cee2e03fa8625731119f62995dbdd501b5fca26f77817709ed20000000000ffffffff02fadf47060000000000001976a914bd677e7a95242c2d54227e3928ced889cc3aaea788acb0c412000000000000001976a914d251921c9857791815e97750afcea48fbd30568a88ac0000000000000000018cae5a060000000000000000ffffffff00"; + const mockDispatch = dispatchFn([{ "version": 1, "serType": 0, "numInputs": 1, "inputs": [{ "prevTxId": "9874aacfe5b9179092f260882807e7088e7fca96fbce908999f31f42c37158be", "outputIndex": 0, "outputTree": 0, "sequence": 4294967295, "index": 0, "valueIn": 0, "blockHeight": 0, "blockIndex": 4294967295, "sigScript": { "0": 71, "1": 48, "2": 68, "3": 2, "4": 32, "5": 91, "6": 228, "7": 127, "8": 22, "9": 156, "10": 231, "11": 152, "12": 180, "13": 69, "14": 163, "15": 217, "16": 181, "17": 246, "18": 76, "19": 150, "20": 237, "21": 250, "22": 47, "23": 87, "24": 202, "25": 47, "26": 129, "27": 182, "28": 234, "29": 133, "30": 42, "31": 191, "32": 188, "33": 219, "34": 16, "35": 99, "36": 68, "37": 2, "38": 32, "39": 104, "40": 94, "41": 54, "42": 96, "43": 225, "44": 218, "45": 133, "46": 125, "47": 122, "48": 238, "49": 201, "50": 207, "51": 102, "52": 34, "53": 30, "54": 199, "55": 128, "56": 204, "57": 74, "58": 161, "59": 142, "60": 18, "61": 14, "62": 185, "63": 194, "64": 34, "65": 150, "66": 21, "67": 130, "68": 145, "69": 244, "70": 114, "71": 1, "72": 33, "73": 3, "74": 51, "75": 241, "76": 244, "77": 132, "78": 192, "79": 73, "80": 61, "81": 162, "82": 82, "83": 113, "84": 188, "85": 186, "86": 229, "87": 98, "88": 168, "89": 15, "90": 128, "91": 182, "92": 75, "93": 174, "94": 184, "95": 118, "96": 235, "97": 168, "98": 34, "99": 43, "100": 42, "101": 98, "102": 50, "103": 181, "104": 90, "105": 163 } }], "numOutputs": 2, "outputs": [{ "value": 106606220, "version": 0, "script": Buffer.from([118, 169, 20, 48, 70, 200, 250, 47, 33, 119, 236, 175, 143, 189, 11, 151, 85, 235, 112, 233, 41, 210, 151, 136, 172]), "index": 0, "decodedScript": { "scriptClass": 2, "address": "TsVRPhzyuvCuxPipBx2YeLarrTPPEAsv4Bh", "requiredSig": 1, "asm": "OP_DUP OP_HASH160 OP_DATA_20 3046c8fa2f2177ecaf8fbd0b9755eb70e929d297 OP_EQUALVERIFY OP_CHECKSIG" } }, { "value": 1230000, "version": 0, "script": Buffer.from([118, 169, 20, 210, 81, 146, 28, 152, 87, 121, 24, 21, 233, 119, 80, 175, 206, 164, 143, 189, 48, 86, 138, 136, 172]), "index": 1, "decodedScript": { "scriptClass": 2, "address": "TskCC6UkcKfBRsrJVPhSjwGjn7ptD34HA4T", "requiredSig": 1, "asm": "OP_DUP OP_HASH160 OP_DATA_20 d251921c9857791815e97750afcea48fbd30568a OP_EQUALVERIFY OP_CHECKSIG" } }], "lockTime": 0, "expiry": 0, "hash": "d29e701778f726ca5f1b50dddb9529f61911732586fa032eee9c2418f617ce89" }]); + const want = { "inputs": [[{ "version": { "type": "Buffer", "data": [1, 0, 0, 0] }, "inputs": [{ "prevout": { "type": "Buffer", "data": [190, 88, 113, 195, 66, 31, 243, 153, 137, 144, 206, 251, 150, 202, 127, 142, 8, 231, 7, 40, 136, 96, 242, 146, 144, 23, 185, 229, 207, 170, 116, 152, 0, 0, 0, 0] }, "script": { "type": "Buffer", "data": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "sequence": { "type": "Buffer", "data": [255, 255, 255, 255] }, "tree": { "type": "Buffer", "data": [0] } }], "outputs": [{ "amount": { "type": "Buffer", "data": [140, 174, 90, 6, 0, 0, 0, 0] }, "script": { "type": "Buffer", "data": [118, 169, 20, 48, 70, 200, 250, 47, 33, 119, 236, 175, 143, 189, 11, 151, 85, 235, 112, 233, 41, 210, 151, 136, 172] } }, { "amount": { "type": "Buffer", "data": [176, 196, 18, 0, 0, 0, 0, 0] }, "script": { "type": "Buffer", "data": [118, 169, 20, 210, 81, 146, 28, 152, 87, 121, 24, 21, 233, 119, 80, 175, 206, 164, 143, 189, 48, 86, 138, 136, 172] } }], "locktime": { "type": "Buffer", "data": [0, 0, 0, 0] }, "nExpiryHeight": { "type": "Buffer", "data": [0, 0, 0, 0] } }, 0]], "associatedKeysets": ["44'/42'/0'/1/71"], "changePath": "44'/42'/0'/1/72", "outputScriptHex": { "type": "Buffer", "data": [2, 250, 223, 71, 6, 0, 0, 0, 0, 0, 0, 25, 118, 169, 20, 189, 103, 126, 122, 149, 36, 44, 45, 84, 34, 126, 57, 40, 206, 216, 137, 204, 58, 174, 167, 136, 172, 176, 196, 18, 0, 0, 0, 0, 0, 0, 0, 25, 118, 169, 20, 210, 81, 146, 28, 152, 87, 121, 24, 21, 233, 119, 80, 175, 206, 164, 143, 189, 48, 86, 138, 136, 172] }, "lockTime": 0, "sigHashType": 1, "segwit": false, "expiryHeight": { "type": "Buffer", "data": [0, 0, 0, 0] }, "useTrustedInputForSegwit": false, "additionals": ["decred"] }; + const data = await signArg(txHex, null, null, mockDispatch); + expect(JSON.stringify(data)).toStrictEqual(JSON.stringify(want)); +}); diff --git a/yarn.lock b/yarn.lock index 8b35365cdb..18f30b2d85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1795,6 +1795,63 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@ledgerhq/devices@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.0.4.tgz#ebc7779adbbec2d046424603a481623eb3fbe306" + integrity sha512-dxOiWZmtEv1tgw70+rW8gviCRZUeGDUnxY6HUPiRqTAc0Ts2AXxiJChgAsPvIywWTGW+S67Nxq1oTZdpRbdt+A== + dependencies: + "@ledgerhq/errors" "^6.12.7" + "@ledgerhq/logs" "^6.10.1" + rxjs "6" + semver "^7.3.5" + +"@ledgerhq/errors@^6.12.7": + version "6.12.7" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.12.7.tgz#c7b630488d5713bc7b1e1682d6ab5d08918c69f1" + integrity sha512-1BpjzFErPK7qPFx0oItcX0mNLJMplVAm2Dpl5urZlubewnTyyw5sahIBjU+8LLCWJ2eGEh/0wyvh0jMtR0n2Mg== + +"@ledgerhq/hw-app-btc@^10.0.5": + version "10.0.5" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-10.0.5.tgz#2125480db24ae7c508d3922aeb990dca884ca5a6" + integrity sha512-X1QfEvkA/XP7x6/vg5+6iWTIaEc79gZ5c+qU2X/HzUAOJQK8uJtNX0/68nEAObUHNhbN8BrOrUo40ARbPDqNgA== + dependencies: + "@ledgerhq/hw-transport" "^6.28.5" + "@ledgerhq/logs" "^6.10.1" + bip32-path "^0.4.2" + bitcoinjs-lib "^5.2.0" + bs58 "^4.0.1" + bs58check "^2.1.2" + invariant "^2.2.4" + ripemd160 "2" + semver "^7.3.5" + sha.js "2" + tiny-secp256k1 "1.1.6" + varuint-bitcoin "1.1.2" + +"@ledgerhq/hw-transport-webusb@^6.27.16": + version "6.27.16" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webusb/-/hw-transport-webusb-6.27.16.tgz#b8e20e772f78c312fc7f2ce3a469c99ecf59dc67" + integrity sha512-A3S2p5Rh9Ot402pWNZw8v5EpO3wOHP8ch/Dcz0AjInmwNouQ9nIYd1+eLSL7QiyG9X7+tuHxFF1IjrEgvAzQuQ== + dependencies: + "@ledgerhq/devices" "^8.0.4" + "@ledgerhq/errors" "^6.12.7" + "@ledgerhq/hw-transport" "^6.28.5" + "@ledgerhq/logs" "^6.10.1" + +"@ledgerhq/hw-transport@^6.28.5": + version "6.28.5" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.28.5.tgz#675193be2f695a596068145351da598316c25831" + integrity sha512-xmw5RhYbqExBBqTvOnOjN/RYNIGMBxFJ+zcYNfkfw/E+uEY3L7xq8Z7sC/n7URTT6xtEctElqduBJnBQE4OQtw== + dependencies: + "@ledgerhq/devices" "^8.0.4" + "@ledgerhq/errors" "^6.12.7" + events "^3.3.0" + +"@ledgerhq/logs@^6.10.1": + version "6.10.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.10.1.tgz#5bd16082261d7364eabb511c788f00937dac588d" + integrity sha512-z+ILK8Q3y+nfUl43ctCPuR4Y2bIxk/ooCQFwZxhtci1EhAtMDzMAx2W25qx8G1PPL9UUOdnUax19+F0OjXoj4w== + "@malept/cross-spawn-promise@^1.1.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz#504af200af6b98e198bce768bc1730c6936ae01d" @@ -2547,6 +2604,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb" integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ== +"@types/node@10.12.18": + version "10.12.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" + integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== + "@types/node@^16.11.26": version "16.18.36" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.36.tgz#0db5d7efc4760d36d0d1d22c85d1a53accd5dc27" @@ -3633,6 +3695,11 @@ bchaddrjs@^0.5.2: cashaddrjs "0.4.4" stream-browserify "^3.0.0" +bech32@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + bech32@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" @@ -3670,18 +3737,62 @@ bindings@~1.2.1: resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" integrity sha512-u4cBQNepWxYA55FunZSM7wMi55yQaN0otnhhilNoWHq0MfOfJeQx0v0mRRpolGOExPjZcl6FtB0BB8Xkb88F0g== -bip66@^1.1.5: +bip174@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/bip174/-/bip174-2.1.0.tgz#cd3402581feaa5116f0f00a0eaee87a5843a2d30" + integrity sha512-lkc0XyiX9E9KiVAS1ZiOqK1xfiwvf4FXDDdkDq5crcDzOq+xGytY+14qCsqz7kCiy8rpN1CRNfacRhf9G3JNSA== + +bip32-path@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/bip32-path/-/bip32-path-0.4.2.tgz#5db0416ad6822712f077836e2557b8697c0c7c99" + integrity sha512-ZBMCELjJfcNMkz5bDuJ1WrYvjlhEF5k6mQ8vUr4N7MbVRsXei7ZOg8VhhwMfNiW68NWmLkgkc6WvTickrLGprQ== + +bip32@^2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/bip32/-/bip32-2.0.6.tgz#6a81d9f98c4cd57d05150c60d8f9e75121635134" + integrity sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA== + dependencies: + "@types/node" "10.12.18" + bs58check "^2.1.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + tiny-secp256k1 "^1.1.3" + typeforce "^1.11.5" + wif "^2.0.6" + +bip66@^1.1.0, bip66@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" integrity sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw== dependencies: safe-buffer "^5.0.1" -bitcoin-ops@^1.3.0, bitcoin-ops@^1.4.1: +bitcoin-ops@^1.3.0, bitcoin-ops@^1.4.0, bitcoin-ops@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz#e45de620398e22fd4ca6023de43974ff42240278" integrity sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow== +bitcoinjs-lib@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-5.2.0.tgz#caf8b5efb04274ded1b67e0706960b93afb9d332" + integrity sha512-5DcLxGUDejgNBYcieMIUfjORtUeNWl828VWLHJGVKZCb4zIS1oOySTUr0LGmcqJBQgTBz3bGbRQla4FgrdQEIQ== + dependencies: + bech32 "^1.1.2" + bip174 "^2.0.1" + bip32 "^2.0.4" + bip66 "^1.1.0" + bitcoin-ops "^1.4.0" + bs58check "^2.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.3" + merkle-lib "^2.0.10" + pushdata-bitcoin "^1.0.1" + randombytes "^2.0.1" + tiny-secp256k1 "^1.1.1" + typeforce "^1.11.3" + varuint-bitcoin "^1.0.4" + wif "^2.0.1" + bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -3907,7 +4018,7 @@ bs58@^5.0.0: dependencies: base-x "^4.0.0" -bs58check@2.1.2, bs58check@<3.0.0, bs58check@^2.1.1: +bs58check@2.1.2, bs58check@<3.0.0, bs58check@^2.0.0, bs58check@^2.1.1, bs58check@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== @@ -4746,7 +4857,7 @@ create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: ripemd160 "^2.0.1" sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: +create-hmac@^1.1.0, create-hmac@^1.1.3, create-hmac@^1.1.4, create-hmac@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== @@ -7491,6 +7602,13 @@ intl-messageformat@^7.8.4: intl-format-cache "^4.2.21" intl-messageformat-parser "^3.6.4" +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + ip@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" @@ -9175,6 +9293,11 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +merkle-lib@^2.0.10: + version "2.0.10" + resolved "https://registry.yarnpkg.com/merkle-lib/-/merkle-lib-2.0.10.tgz#82b8dbae75e27a7785388b73f9d7725d0f6f3326" + integrity sha512-XrNQvUbn1DL5hKNe46Ccs+Tu3/PYOlrcZILuGUhb95oKBPjc/nmIC8D462PQkipVDGKRvwhn+QFg2cCdIvmDJA== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -11859,7 +11982,7 @@ rimraf@~2.6.2: dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: +ripemd160@2, ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== @@ -11960,6 +12083,13 @@ runtypes@^6.5.1: resolved "https://registry.yarnpkg.com/runtypes/-/runtypes-6.6.0.tgz#48e353d8b0f641ab5ec5d80fa96dd7bd41ea3281" integrity sha512-ddM7sgB3fyboDlBzEYFQ04L674sKjbs4GyW2W32N/5Ae47NRd/GyMASPC2PFw8drPHYGEcZ0mZ26r5RcB8msfQ== +rxjs@6: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -12191,7 +12321,7 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -sha.js@^2.4.0, sha.js@^2.4.8: +sha.js@2, sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== @@ -12983,7 +13113,7 @@ tiny-invariant@^1.0.2: resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== -tiny-secp256k1@^1.1.6: +tiny-secp256k1@1.1.6, tiny-secp256k1@^1.1.1, tiny-secp256k1@^1.1.3, tiny-secp256k1@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz#7e224d2bee8ab8283f284e40e6b4acb74ffe047c" integrity sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA== @@ -13133,7 +13263,7 @@ tsconfig-paths@^3.10.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1: +tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -13235,7 +13365,7 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typeforce@^1.18.0: +typeforce@^1.11.3, typeforce@^1.11.5, typeforce@^1.18.0: version "1.18.0" resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== @@ -13557,7 +13687,7 @@ value-equal@^1.0.1: resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== -varuint-bitcoin@^1.1.2: +varuint-bitcoin@1.1.2, varuint-bitcoin@^1.0.4, varuint-bitcoin@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz#e76c138249d06138b480d4c5b40ef53693e24e92" integrity sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw== @@ -13898,7 +14028,7 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" -wif@^2.0.6: +wif@^2.0.1, wif@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/wif/-/wif-2.0.6.tgz#08d3f52056c66679299726fade0d432ae74b4704" integrity sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ== From b7313c897228c0c16b9add3a1e8f8622316af354 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:32:45 -0400 Subject: [PATCH 03/15] Bump protobufjs from 6.11.3 to 6.11.4 (#3898) Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 6.11.3 to 6.11.4. - [Release notes](https://github.com/protobufjs/protobuf.js/releases) - [Changelog](https://github.com/protobufjs/protobuf.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/protobufjs/protobuf.js/commits) --- updated-dependencies: - dependency-name: protobufjs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 18f30b2d85..6b86f19498 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11060,9 +11060,9 @@ property-information@^5.3.0: xtend "^4.0.0" protobufjs@^6.11.2, protobufjs@^6.8.0: - version "6.11.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" - integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== + version "6.11.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" + integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -11079,9 +11079,9 @@ protobufjs@^6.11.2, protobufjs@^6.8.0: long "^4.0.0" protobufjs@^7.0.0: - version "7.2.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.3.tgz#01af019e40d9c6133c49acbb3ff9e30f4f0f70b2" - integrity sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg== + version "7.2.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.4.tgz#3fc1ec0cdc89dd91aef9ba6037ba07408485c3ae" + integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" From 305ee28f761efc57f8616b74ec4e5b067064170b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:33:10 -0400 Subject: [PATCH 04/15] Bump word-wrap from 1.2.3 to 1.2.4 (#3894) Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4. - [Release notes](https://github.com/jonschlinkert/word-wrap/releases) - [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4) --- updated-dependencies: - dependency-name: word-wrap dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6b86f19498..c9bf2172ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14041,9 +14041,9 @@ wildcard@^2.0.0: integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + version "1.2.4" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" + integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== worker-loader@^3.0.8: version "3.0.8" From c9a4842bdf01b26b2a8e6c9c8fc8ce1c88d493c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:33:29 -0400 Subject: [PATCH 05/15] Bump semver from 5.7.1 to 5.7.2 (#3891) Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2) --- updated-dependencies: - dependency-name: semver dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index c9bf2172ae..80879feffc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12230,19 +12230,19 @@ semver-diff@^3.1.1: semver "^6.3.0" "semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.8: - version "7.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" - integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" From 152b867ce1ff01f49d3523fa5584fa2b0d502850 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:33:52 -0400 Subject: [PATCH 06/15] Bump tough-cookie from 4.1.2 to 4.1.3 (#3890) Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) from 4.1.2 to 4.1.3. - [Release notes](https://github.com/salesforce/tough-cookie/releases) - [Changelog](https://github.com/salesforce/tough-cookie/blob/master/CHANGELOG.md) - [Commits](https://github.com/salesforce/tough-cookie/compare/v4.1.2...v4.1.3) --- updated-dependencies: - dependency-name: tough-cookie dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 80879feffc..d6edb6d758 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13196,9 +13196,9 @@ toidentifier@1.0.1: integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== tough-cookie@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== dependencies: psl "^1.1.33" punycode "^2.1.1" From 1c7be1688f06a6b927436dd37ded3005a445b716 Mon Sep 17 00:00:00 2001 From: buck54321 Date: Fri, 22 Sep 2023 13:34:40 -0700 Subject: [PATCH 07/15] dex: bump 0.6.3 and enable dexc extension mode (#3905) --- go.mod | 15 +++++++------ go.sum | 39 +++++++++++++++------------------ modules/dex/libdexc/adapter.go | 40 ++++++++++++++++++++++++++++++---- 3 files changed, 61 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index b606733075..c2a3a16ff7 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/decred/decrediton go 1.18 require ( - decred.org/dcrdex v0.6.2 + decred.org/dcrdex v0.6.3 github.com/decred/slog v1.2.0 github.com/jrick/logrotate v1.0.0 ) @@ -18,13 +18,13 @@ require ( github.com/aead/siphash v1.0.1 // indirect github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/btcsuite/btcd v0.23.3 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.2.1 // indirect - github.com/btcsuite/btcd/btcutil v1.1.2 // indirect - github.com/btcsuite/btcd/btcutil/psbt v1.1.5 // indirect + github.com/btcsuite/btcd v0.23.4 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.2.2 // indirect + github.com/btcsuite/btcd/btcutil v1.1.3 // indirect + github.com/btcsuite/btcd/btcutil/psbt v1.1.8 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect - github.com/btcsuite/btcwallet v0.16.1 // indirect + github.com/btcsuite/btcwallet v0.16.10-0.20230706223227-037580c66b74 // indirect github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 // indirect github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 // indirect github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 // indirect @@ -110,7 +110,8 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/lib/pq v1.10.4 // indirect github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect - github.com/lightninglabs/neutrino v0.14.3-0.20221024182812-792af8548c14 // indirect + github.com/lightninglabs/neutrino v0.15.0 // indirect + github.com/lightninglabs/neutrino/cache v1.1.0 // indirect github.com/lightningnetwork/lnd/clock v1.0.1 // indirect github.com/lightningnetwork/lnd/queue v1.0.1 // indirect github.com/lightningnetwork/lnd/ticker v1.0.0 // indirect diff --git a/go.sum b/go.sum index 4209207798..1d5fed9db9 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= decred.org/cspp/v2 v2.1.0 h1:HeHb9+BFqrBaAPc6CsPiUpPFmC1uyBM2mJZUAbUXkRw= decred.org/cspp/v2 v2.1.0/go.mod h1:9nO3bfvCheOPIFZw5f6sRQ42CjBFB5RKSaJ9Iq6G4MA= -decred.org/dcrdex v0.6.2 h1:aEr7/KNUN+CYYGDEqFepIHp8TVpqdwxTh3lUT/tyeXg= -decred.org/dcrdex v0.6.2/go.mod h1:dLAVj5FPmVgyi9ctsyftYpGy6MyNIc0XuPBaf0UzCys= +decred.org/dcrdex v0.6.3 h1:XrqbF5O+CFhQws+eugzqQoPgvjrFKgpYSkfKGAbD3NM= +decred.org/dcrdex v0.6.3/go.mod h1:zzHSNtu94lYxCBUwkuEmWGUL1wlO4pFzMoaPTGoqtBo= decred.org/dcrwallet/v3 v3.0.1 h1:+OLi+u/MvKc3Ubcnf19oyG/a5hJ/qp4OtezdiQZnLIs= decred.org/dcrwallet/v3 v3.0.1/go.mod h1:a+R8BZIOKVpWVPat5VZoBWNh/cnIciwcRkPtrzfS/tw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -121,43 +121,38 @@ github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbz github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= -github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:osu7EoKiL36UThEgzYPqdRaxeo0NU8VoXqgcnwpey0g= github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08= -github.com/btcsuite/btcd v0.22.0-beta.0.20220316175102-8d5c75c28923/go.mod h1:taIcYprAW2g6Z9S0gGUxyR+zDwimyDMK5ePOX+iJ2ds= github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd v0.23.1/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= -github.com/btcsuite/btcd v0.23.3 h1:4KH/JKy9WiCd+iUS9Mu0Zp7Dnj17TGdKrg9xc/FGj24= -github.com/btcsuite/btcd v0.23.3/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd v0.23.4 h1:IzV6qqkfwbItOS/sg/aDfPDsjPP8twrCOE2R93hxMlQ= +github.com/btcsuite/btcd v0.23.4/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.2.1 h1:xP60mv8fvp+0khmrN0zTdPC3cNm24rfeE6lh2R/Yv3E= -github.com/btcsuite/btcd/btcec/v2 v2.2.1/go.mod h1:9/CSmJxmuvqzX9Wh2fXMWToLOHhPd11lSPuIupwTkI8= +github.com/btcsuite/btcd/btcec/v2 v2.2.2 h1:5uxe5YjoCq+JeOpg0gZSNHuFgeogrocBYxvg6w9sAgc= +github.com/btcsuite/btcd/btcec/v2 v2.2.2/go.mod h1:9/CSmJxmuvqzX9Wh2fXMWToLOHhPd11lSPuIupwTkI8= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34= -github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ= -github.com/btcsuite/btcd/btcutil v1.1.2/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= -github.com/btcsuite/btcd/btcutil/psbt v1.1.4/go.mod h1:9AyU6EQVJ9Iw9zPyNT1lcdHd6cnEZdno5wLu5FY74os= -github.com/btcsuite/btcd/btcutil/psbt v1.1.5 h1:x0ZRrYY8j75ThV6xBz86CkYAG82F5bzay4H5D1c8b/U= -github.com/btcsuite/btcd/btcutil/psbt v1.1.5/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8XGBQuuTmuKYUf6q7/U= +github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/btcutil/psbt v1.1.8 h1:4voqtT8UppT7nmKQkXV+T9K8UyQjKOn2z/ycpmJK8wg= +github.com/btcsuite/btcd/btcutil/psbt v1.1.8/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8XGBQuuTmuKYUf6q7/U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.16.1 h1:nD8qXJeAU8c7a0Jlx5jwI2ufbf/9ouy29XGapRLTmos= -github.com/btcsuite/btcwallet v0.16.1/go.mod h1:NCO8+5rIcbUm5CtVNSQM0xrtK4iYprlyuvpGzhkejaM= -github.com/btcsuite/btcwallet/wallet/txauthor v1.2.3/go.mod h1:T2xSiKGpUkSLCh68aF+FMXmKK9mFqNdHl9VaqOr+JjU= +github.com/btcsuite/btcwallet v0.16.10-0.20230706223227-037580c66b74 h1:RanL5kPm5Gi9YPf+3aPfdd6gJuohoJMxcDv8R033RIo= +github.com/btcsuite/btcwallet v0.16.10-0.20230706223227-037580c66b74/go.mod h1:Hl4PP/tSNcgN6himfx/020mYSa19a1qkqTuqQBUU97w= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 h1:etuLgGEojecsDOYTII8rYiGHjGyV5xTqsXi+ZQ715UU= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2/go.mod h1:Zpk/LOb2sKqwP2lmHjaZT9AdaKsHPSbNLm2Uql5IQ/0= github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 h1:BtEN5Empw62/RVnZ0VcJaVtVlBijnLlJY+dwjAye2Bg= github.com/btcsuite/btcwallet/wallet/txrules v1.2.0/go.mod h1:AtkqiL7ccKWxuLYtZm8Bu8G6q82w4yIZdgq6riy60z0= -github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs= github.com/btcsuite/btcwallet/wallet/txsizes v1.2.2/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448= github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 h1:PszOub7iXVYbtGybym5TGCp9Dv1h1iX4rIC3HICZGLg= github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448= @@ -781,9 +776,10 @@ github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= -github.com/lightninglabs/neutrino v0.14.2/go.mod h1:OICUeTCn+4Tu27YRJIpWvvqySxx4oH4vgdP33Sw9RDc= -github.com/lightninglabs/neutrino v0.14.3-0.20221024182812-792af8548c14 h1:ieYiTZabh03Rjn+W8//z7z8xwaEjXE8lWulVWV4Sqns= -github.com/lightninglabs/neutrino v0.14.3-0.20221024182812-792af8548c14/go.mod h1:OICUeTCn+4Tu27YRJIpWvvqySxx4oH4vgdP33Sw9RDc= +github.com/lightninglabs/neutrino v0.15.0 h1:yr3uz36fLAq8hyM0TRUVlef1TRNoWAqpmmNlVtKUDtI= +github.com/lightninglabs/neutrino v0.15.0/go.mod h1:pmjwElN/091TErtSE9Vd5W4hpxoG2/+xlb+HoPm9Gug= +github.com/lightninglabs/neutrino/cache v1.1.0 h1:szZIhVabiQIsGzJjhvo76sj05Au+zVotj2M34EquGME= +github.com/lightninglabs/neutrino/cache v1.1.0/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo= github.com/lightningnetwork/lnd/clock v1.0.1 h1:QQod8+m3KgqHdvVMV+2DRNNZS1GRFir8mHZYA+Z2hFo= github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg= github.com/lightningnetwork/lnd/queue v1.0.1 h1:jzJKcTy3Nj5lQrooJ3aaw9Lau3I0IwvQR5sqtjdv2R0= @@ -1137,6 +1133,7 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1251,7 +1248,6 @@ golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1326,7 +1322,6 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= diff --git a/modules/dex/libdexc/adapter.go b/modules/dex/libdexc/adapter.go index cb06dd9a6f..90daa545f8 100644 --- a/modules/dex/libdexc/adapter.go +++ b/modules/dex/libdexc/adapter.go @@ -108,7 +108,8 @@ func (c *CoreAdapter) startCore(raw json.RawMessage) error { if err := json.Unmarshal(raw, form); err != nil { return err } - err := os.MkdirAll(filepath.Dir(form.DBPath), 0700) + dexDir := filepath.Dir(form.DBPath) + err := os.MkdirAll(dexDir, 0700) if err != nil { return err } @@ -119,6 +120,16 @@ func (c *CoreAdapter) startCore(raw json.RawMessage) error { return err } + extensionModeFile := filepath.Join(dexDir, "dextension.conf") + if _, err := os.Stat(extensionModeFile); err != nil { + if !os.IsNotExist(err) { + return fmt.Errorf("error checking extension file: %w", err) + } + if err := os.WriteFile(extensionModeFile, []byte(extensionModeConfigJSON), 0644); err != nil { + return fmt.Errorf("error writing extension mode configuration: %w", err) + } + } + c.logMaker = initLogging(filepath.Dir(form.LogFilename), form.LogFilename, form.LogLevel.String(), false) logger := c.logMaker.Logger("DEXC") @@ -130,9 +141,10 @@ func (c *CoreAdapter) startCore(raw json.RawMessage) error { // Onion applies ONLY to .onion addresses, unlike TorProxy, which is // used for connections to all servers regardless of hostname. TODO: // expose an option for the user to set this and TorProxy. - Onion: "127.0.0.1:9050", - Language: form.Language, - NoAutoWalletLock: true, // Decrediton user locks it when done + Onion: "127.0.0.1:9050", + Language: form.Language, + NoAutoWalletLock: true, // Decrediton user locks it when done + ExtensionModeFile: extensionModeFile, }) if err != nil { return fmt.Errorf("error creating client core: %v", err) @@ -378,3 +390,23 @@ func (c *CoreAdapter) logout(raw json.RawMessage) (string, error) { } return "", nil } + +const extensionModeConfigJSON = ` +{ + "restrictedWallets": { + "dcr": { + "hiddenFields": [ + "account", + "unmixedaccount", + "tradingaccount", + "username", + "password", + "rpclisten", + "rpccert" + ], + "disableWalletType": true, + "disablePassword": true + } + } +} +` From 2df568aa8e2a4a29654cdcc4e27d4bcc033f1179 Mon Sep 17 00:00:00 2001 From: JoeGruffins <34998433+JoeGruffins@users.noreply.github.com> Date: Mon, 25 Sep 2023 23:22:38 +0900 Subject: [PATCH 08/15] multi: Add ledger ui. (#3874) --- app/actions/DaemonActions.js | 7 +- app/actions/LedgerActions.js | 21 ++---- app/actions/TrezorActions.js | 11 --- app/actions/WalletLoaderActions.js | 12 ++- app/components/SideBar/MenuLinks/Links.jsx | 2 + app/components/SideBar/MenuLinks/hooks.js | 17 ++++- .../SendTransactionButton.jsx | 19 ++++- .../buttons/SendTransactionButton/hooks.js | 8 +- .../shared/SendTransaction/Form.jsx | 7 +- .../SendTransaction/SendTransaction.jsx | 2 + .../shared/SendTransaction/hooks.js | 2 + .../CreateWalletPage/CreateWalletPage.jsx | 27 ++++--- .../GetStartedPage/CreateWalletPage/hooks.js | 11 ++- .../CreateLedgerWalletForm.jsx | 75 +++++++++++++++++++ .../CreateLedgerWalletForm.module.css | 59 +++++++++++++++ .../CreateLedgerWalletForm/index.js | 1 + .../LedgerLoaderBarContainer.jsx | 23 ++++++ .../LedgerLoaderBarContainer.module.css | 63 ++++++++++++++++ .../LedgerLoaderBarContainer/hooks.js | 7 ++ .../LedgerLoaderBarContainer/index.js | 1 + .../PreCreateWallet/PreCreateWallet.jsx | 55 ++++++++++++-- .../GetStartedPage/PreCreateWallet/hooks.js | 57 ++++++++++++-- .../views/GetStartedPage/SetupWallet/hooks.js | 4 +- .../WalletSelection/Form/Form.jsx | 28 +++++++ .../WalletSelection/WalletSelection.jsx | 5 ++ app/components/views/GetStartedPage/hooks.js | 34 ++++++--- .../GetStartedPage/messages/messages.jsx | 9 +++ app/components/views/HomePage/HomePage.jsx | 3 +- .../LedgerPage/NoDevicePage/NoDevicePage.jsx | 19 +++++ .../NoDevicePage/NoDevicePage.module.css | 29 +++++++ .../views/LedgerPage/NoDevicePage/index.js | 1 + .../views/PrivacyPage/PrivacyPage.jsx | 38 ++++++---- .../ReceiveTab/ReceivePage/ReceivePage.jsx | 3 +- app/constants/config.js | 2 + app/helpers/ledger.js | 11 ++- app/hooks/useDaemonStartup.js | 25 +++++++ app/hooks/useLedger.js | 30 ++++++++ app/hooks/useSettings.js | 2 + .../docs/en/Ledger/PreCreateLedgerWallet1.md | 3 + .../docs/en/Ledger/PreCreateLedgerWallet2.md | 1 + app/i18n/docs/en/index.js | 3 + app/index.js | 5 ++ app/main_dev/externalRequests.js | 2 +- app/main_dev/ipc.js | 2 + app/reducers/index.js | 2 + app/reducers/ledger.js | 61 +++++++++++++++ app/reducers/snackbar.js | 16 ++++ app/reducers/walletLoader.js | 3 +- app/selectors.js | 16 +++- app/stateMachines/CreateWalletStateMachine.js | 13 +--- app/stateMachines/GetStartedStateMachine.js | 18 ++++- package.json | 7 +- test/unit/actions/DaemonActions.spec.js | 2 +- .../views/GetStaredPage/CreateWallet.spec.js | 1 + .../GetStaredPage/PreCreateWallet.spec.js | 7 +- test/unit/helpers/ledger.spec.js | 4 +- yarn.lock | 48 ++++++------ 57 files changed, 804 insertions(+), 140 deletions(-) create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.jsx create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.module.css create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/index.js create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.jsx create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.module.css create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/hooks.js create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/index.js create mode 100644 app/components/views/LedgerPage/NoDevicePage/NoDevicePage.jsx create mode 100644 app/components/views/LedgerPage/NoDevicePage/NoDevicePage.module.css create mode 100644 app/components/views/LedgerPage/NoDevicePage/index.js create mode 100644 app/hooks/useLedger.js create mode 100644 app/i18n/docs/en/Ledger/PreCreateLedgerWallet1.md create mode 100644 app/i18n/docs/en/Ledger/PreCreateLedgerWallet2.md create mode 100644 app/reducers/ledger.js diff --git a/app/actions/DaemonActions.js b/app/actions/DaemonActions.js index 7c05e98534..b658b6123c 100644 --- a/app/actions/DaemonActions.js +++ b/app/actions/DaemonActions.js @@ -9,6 +9,7 @@ import { stopNotifcations } from "./NotificationActions"; import { saveSettings, updateStateSettingsChanged } from "./SettingsActions"; import { rescanCancel, showCantCloseModal } from "./ControlActions"; import { enableTrezor } from "./TrezorActions"; +import { enableLedger } from "./LedgerActions"; import { DEX_LOGOUT_ATTEMPT, DEX_LOGOUT_SUCCESS, @@ -309,7 +310,7 @@ export const removeWallet = (selectedWallet) => (dispatch) => { // selectedWallet = { // label: newWalletName, // value: { -// wallet: newWalletName, isWatchingOnly, isTrezor, isNew, +// wallet: newWalletName, isWatchingOnly, isTrezor, isLedger, isNew // network: isTestNet ? "testnet" : "mainnet" // } // } @@ -336,7 +337,8 @@ export const createWallet = (selectedWallet) => (dispatch, getState) => dispatch({ isWatchingOnly: selectedWallet.value.isWatchingOnly, createNewWallet: selectedWallet.value.isNew, - isTrezor: selectedWallet.value.istrezor, + isTrezor: selectedWallet.value.isTrezor, + isLedger: selectedWallet.value.isLedger, type: WALLETCREATED }); dispatch(setSelectedWallet(selectedWallet)); @@ -509,6 +511,7 @@ export const startWallet = confirmDexSeed }); selectedWallet.value.isTrezor && dispatch(enableTrezor()); + selectedWallet.value.isLedger && dispatch(enableLedger()); await dispatch(getVersionServiceAttempt()); await dispatch(openWalletAttempt("", false, selectedWallet)); return discoverAccountsComplete; diff --git a/app/actions/LedgerActions.js b/app/actions/LedgerActions.js index 4c9d7e1b09..7e742d1543 100644 --- a/app/actions/LedgerActions.js +++ b/app/actions/LedgerActions.js @@ -14,7 +14,6 @@ import { const coin = "decred"; import * as selectors from "selectors"; -import * as cfgConstants from "constants/config"; export const LDG_LEDGER_ENABLED = "LDG_LEDGER_ENABLED"; export const LDG_WALLET_CLOSED = "LDG_WALLET_CLOSED"; @@ -26,18 +25,7 @@ export const LDG_WALLET_CLOSED = "LDG_WALLET_CLOSED"; // enableLedger only sets a value in the config. Ledger connections are made // per action then dropped. export const enableLedger = () => (dispatch, getState) => { - const walletName = selectors.getWalletName(getState()); - - if (walletName) { - const config = wallet.getWalletCfg( - selectors.isTestNet(getState()), - walletName - ); - config.set(cfgConstants.LEDGER, true); - } - dispatch({ type: LDG_LEDGER_ENABLED }); - connect()(dispatch, getState); }; @@ -143,13 +131,16 @@ export const LDG_GETWALLETCREATIONMASTERPUBKEY_SUCCESS = "LDG_GETWALLETCREATIONMASTERPUBKEY_SUCCESS"; export const getWalletCreationMasterPubKey = - () => async (dispatch /*, getState*/) => { + () => async (dispatch, getState) => { dispatch({ type: LDG_GETWALLETCREATIONMASTERPUBKEY_ATTEMPT }); // TODO: Enable on mainnet. - const isTestnet = true; + const isTestnet = selectors.isTestNet(getState()); + if (!isTestnet) { + throw "disabled on mainnet"; + } try { const payload = await getPubKey(isTestnet); - const hdpk = ledgerHelpers.fixPubKeyChecksum(payload); + const hdpk = ledgerHelpers.fixPubKeyChecksum(payload, isTestnet); dispatch({ type: LDG_GETWALLETCREATIONMASTERPUBKEY_SUCCESS }); return hdpk; } catch (error) { diff --git a/app/actions/TrezorActions.js b/app/actions/TrezorActions.js index b43a067f00..96e1175897 100644 --- a/app/actions/TrezorActions.js +++ b/app/actions/TrezorActions.js @@ -9,7 +9,6 @@ import { addressPath } from "helpers/trezor"; import { publishTransactionAttempt } from "./ControlActions"; -import * as cfgConstants from "constants/config"; import { MODEL1_DECRED_HOMESCREEN, MODELT_DECRED_HOMESCREEN @@ -49,16 +48,6 @@ export const TRZ_TREZOR_ENABLED = "TRZ_TREZOR_ENABLED"; // enableTrezor attepts to start a connection with connect if none exist and // connect to a trezor device. export const enableTrezor = () => (dispatch, getState) => { - const walletName = selectors.getWalletName(getState()); - - if (walletName) { - const config = wallet.getWalletCfg( - selectors.isTestNet(getState()), - walletName - ); - config.set(cfgConstants.TREZOR, true); - } - dispatch({ type: TRZ_TREZOR_ENABLED }); if (!setListeners) { diff --git a/app/actions/WalletLoaderActions.js b/app/actions/WalletLoaderActions.js index b3b6c77e7d..2a4134b8bb 100644 --- a/app/actions/WalletLoaderActions.js +++ b/app/actions/WalletLoaderActions.js @@ -18,7 +18,7 @@ import { getBestBlockHeightAttempt } from "./ClientActions"; import { WALLETREMOVED_FAILED } from "./DaemonActions"; -import { isTestNet, trezorDevice } from "selectors"; +import { isTestNet, trezorDevice, ledgerDevice } from "selectors"; import { walletrpc as api } from "middleware/walletrpc/api_pb"; import { push as pushHistory } from "connected-react-router"; import { stopNotifcations } from "./NotificationActions"; @@ -28,6 +28,7 @@ import * as cfgConstants from "constants/config"; import { RESCAN_PROGRESS } from "./ControlActions"; import { stopAccountMixer } from "./AccountMixerActions"; import { TRZ_WALLET_CLOSED } from "actions/TrezorActions"; +import { LDG_WALLET_CLOSED } from "actions/LedgerActions"; import { saveSettings, updateStateSettingsChanged } from "./SettingsActions"; const { SyncNotificationType } = api; @@ -156,7 +157,7 @@ export const CREATEWATCHONLYWALLET_FAILED = "CREATEWATCHONLYWALLET_FAILED"; export const CREATEWATCHONLYWALLET_SUCCESS = "CREATEWATCHONLYWALLET_SUCCESS"; export const createWatchOnlyWalletRequest = - (extendedPubKey, pubPass = "") => + (extendedPubKey, isLedger, isTrezor, pubPass = "") => (dispatch, getState) => new Promise((resolve, reject) => { dispatch({ type: CREATEWATCHONLYWALLET_ATTEMPT }); @@ -172,6 +173,12 @@ export const createWatchOnlyWalletRequest = } = getState(); const config = wallet.getWalletCfg(isTestNet(getState()), walletName); config.set(cfgConstants.IS_WATCH_ONLY, true); + if (isTrezor) { + config.set(cfgConstants.TREZOR, true); + } + if (isLedger) { + config.set(cfgConstants.LEDGER, true); + } config.delete(cfgConstants.DISCOVER_ACCOUNTS); wallet.setIsWatchingOnly(true); dispatch({ response: {}, type: CREATEWATCHONLYWALLET_SUCCESS }); @@ -269,6 +276,7 @@ const finalCloseWallet = () => async (dispatch, getState) => { await wallet.stopWallet(); dispatch({ type: CLOSEWALLET_SUCCESS }); if (trezorDevice(getState())) dispatch({ type: TRZ_WALLET_CLOSED }); + if (ledgerDevice(getState())) dispatch({ type: LDG_WALLET_CLOSED }); dispatch(pushHistory("/getstarted/initial")); } catch (error) { dispatch({ error, type: CLOSEWALLET_FAILED }); diff --git a/app/components/SideBar/MenuLinks/Links.jsx b/app/components/SideBar/MenuLinks/Links.jsx index 5e88fa800f..56a2b96a9c 100644 --- a/app/components/SideBar/MenuLinks/Links.jsx +++ b/app/components/SideBar/MenuLinks/Links.jsx @@ -4,6 +4,7 @@ export const LN_KEY = "ln"; export const DEX_KEY = "dex"; export const TICKETS_KEY = "tickets"; export const GOV_KEY = "governance"; +export const PRIV_KEY = "privacy"; export const linkList = [ { @@ -48,6 +49,7 @@ export const linkList = [ path: "/privacy", link: , icon: "securitycntr", + key: PRIV_KEY, ariaLabel: "Privacy" }, { diff --git a/app/components/SideBar/MenuLinks/hooks.js b/app/components/SideBar/MenuLinks/hooks.js index c9ec883b8a..bd2e53768c 100644 --- a/app/components/SideBar/MenuLinks/hooks.js +++ b/app/components/SideBar/MenuLinks/hooks.js @@ -1,7 +1,14 @@ import { useEffect, useState, useMemo } from "react"; import { useSelector } from "react-redux"; import * as sel from "selectors"; -import { linkList, LN_KEY, DEX_KEY, TICKETS_KEY, GOV_KEY } from "./Links"; +import { + linkList, + LN_KEY, + DEX_KEY, + TICKETS_KEY, + GOV_KEY, + PRIV_KEY +} from "./Links"; import { useHistory } from "react-router-dom"; import { cloneDeep } from "fp"; @@ -10,6 +17,7 @@ export function useMenuLinks() { const sidebarOnBottom = useSelector(sel.sidebarOnBottom); const expandSideBar = useSelector(sel.expandSideBar); const isTrezor = useSelector(sel.isTrezor); + const isLedger = useSelector(sel.isLedger); const lnEnabled = useSelector(sel.lnEnabled); const newActiveVoteProposalsCount = useSelector( @@ -47,11 +55,14 @@ export function useMenuLinks() { links = links.filter((l) => l.key !== LN_KEY); } // TODO: Enable ticket purchacing for Trezor. - if (isTrezor) { + if (isTrezor || isLedger) { links = links.filter( (l) => l.key !== DEX_KEY && l.key !== TICKETS_KEY && l.key !== GOV_KEY ); } + if (isLedger) { + links = links.filter((l) => l.key !== PRIV_KEY); + } return links.map((link) => ({ ...link, notifProp: link.notifProp?.reduce( @@ -59,7 +70,7 @@ export function useMenuLinks() { 0 ) })); - }, [notifProps, isTrezor, lnEnabled]); + }, [notifProps, isTrezor, lnEnabled, isLedger]); const [activeTabIndex, setActiveTabIndex] = useState(-1); const history = useHistory(); diff --git a/app/components/buttons/SendTransactionButton/SendTransactionButton.jsx b/app/components/buttons/SendTransactionButton/SendTransactionButton.jsx index ba2cf5b9ba..317241ca79 100644 --- a/app/components/buttons/SendTransactionButton/SendTransactionButton.jsx +++ b/app/components/buttons/SendTransactionButton/SendTransactionButton.jsx @@ -16,8 +16,10 @@ const SendTransactionButton = ({ unsignedTransaction, isSendingTransaction, isTrezor, + isLedger, onAttemptSignTransaction, - onAttemptSignTransactionTrezor + onAttemptSignTransactionTrezor, + onAttemptSignTransactionLedger } = useSendTransactionButton(); const signTransaction = (privpass) => { @@ -31,6 +33,12 @@ const SendTransactionButton = ({ onSubmit?.(); }; + const signTransactionLedger = () => { + if (disabled) return; + onAttemptSignTransactionLedger?.(unsignedTransaction); + onSubmit?.(); + }; + if (isTrezor) { return ( } ); + } else if (isLedger) { + return ( + + {buttonLabel ? buttonLabel : } + + ); } else { return ( { @@ -15,13 +17,17 @@ export function useSendTransactionButton() { }; const onAttemptSignTransactionTrezor = (rawUnsigTx, constructTxResponse) => dispatch(tza.signTransactionAttemptTrezor(rawUnsigTx, constructTxResponse)); + const onAttemptSignTransactionLedger = (rawUnsigTx) => + dispatch(ldgr.signTransactionAttemptLedger(rawUnsigTx)); return { unsignedTransaction, constructTxResponse, isSendingTransaction, isTrezor, + isLedger, onAttemptSignTransaction, - onAttemptSignTransactionTrezor + onAttemptSignTransactionTrezor, + onAttemptSignTransactionLedger }; } diff --git a/app/components/shared/SendTransaction/Form.jsx b/app/components/shared/SendTransaction/Form.jsx index 00e7b4332a..05cba321dd 100644 --- a/app/components/shared/SendTransaction/Form.jsx +++ b/app/components/shared/SendTransaction/Form.jsx @@ -22,6 +22,7 @@ const Form = ({ unsignedRawTx, isWatchingOnly, isTrezor, + isLedger, insuficientFunds, styles, hideDetails, @@ -71,7 +72,9 @@ const Form = ({ )} - {((isTrezor && isWatchingOnly) || !isWatchingOnly) && + {((isTrezor && isWatchingOnly) || + (isLedger && isWatchingOnly) || + !isWatchingOnly) && (getRunningIndicator ? ( - {unsignedRawTx && isWatchingOnly && !isTrezor && ( + {unsignedRawTx && isWatchingOnly && !isTrezor && !isLedger && ( } tx={unsignedRawTx} diff --git a/app/components/shared/SendTransaction/SendTransaction.jsx b/app/components/shared/SendTransaction/SendTransaction.jsx index 8eadf1e11f..faf7a834af 100644 --- a/app/components/shared/SendTransaction/SendTransaction.jsx +++ b/app/components/shared/SendTransaction/SendTransaction.jsx @@ -32,6 +32,7 @@ const SendTransaction = ({ totalSpent, notMixedAccounts, isTrezor, + isLedger, isWatchingOnly, isConstructingTransaction, attemptConstructTransaction, @@ -394,6 +395,7 @@ const SendTransaction = ({ willEnter, isWatchingOnly, isTrezor, + isLedger, insuficientFunds, styles, hideDetails, diff --git a/app/components/shared/SendTransaction/hooks.js b/app/components/shared/SendTransaction/hooks.js index 4a8d45eb3c..faf3c996de 100644 --- a/app/components/shared/SendTransaction/hooks.js +++ b/app/components/shared/SendTransaction/hooks.js @@ -24,6 +24,7 @@ export function useSendTransaction() { shallowEqual ); const isTrezor = useSelector(sel.isTrezor); + const isLedger = useSelector(sel.isLedger); const isWatchingOnly = useSelector(sel.isWatchingOnly); const isConstructingTransaction = useSelector(sel.isConstructingTransaction); const constructTxRequestAttempt = useSelector(sel.constructTxRequestAttempt); @@ -61,6 +62,7 @@ export function useSendTransaction() { totalSpent, notMixedAccounts, isTrezor, + isLedger, isWatchingOnly, isConstructingTransaction, attemptConstructTransaction, diff --git a/app/components/views/GetStartedPage/CreateWalletPage/CreateWalletPage.jsx b/app/components/views/GetStartedPage/CreateWalletPage/CreateWalletPage.jsx index 51f67f6245..5c61fc8b4c 100644 --- a/app/components/views/GetStartedPage/CreateWalletPage/CreateWalletPage.jsx +++ b/app/components/views/GetStartedPage/CreateWalletPage/CreateWalletPage.jsx @@ -103,18 +103,16 @@ const CreateWalletPage = ({ createWalletRef, onSendBack }) => { cancelCreateWallet ]); - const onCreateWatchOnly = useCallback(() => { - createWatchOnlyWalletRequest(walletMasterPubKey) - .then(() => sendEvent({ type: "WALLET_CREATED" })) - .catch((error) => sendEvent({ type: "ERROR", error })); - // we send a continue so we go to loading state - sendContinue(); - }, [ - createWatchOnlyWalletRequest, - sendEvent, - sendContinue, - walletMasterPubKey - ]); + const onCreateWatchOnly = useCallback( + (isLedger, isTrezor) => { + createWatchOnlyWalletRequest(walletMasterPubKey, isLedger, isTrezor) + .then(() => sendEvent({ type: "WALLET_CREATED" })) + .catch((error) => sendEvent({ type: "ERROR", error })); + // we send a continue so we go to loading state + sendContinue(); + }, + [createWatchOnlyWalletRequest, sendEvent, sendContinue, walletMasterPubKey] + ); const getStateComponent = useCallback(() => { const { mnemonic, error } = current.context; @@ -188,7 +186,8 @@ const CreateWalletPage = ({ createWalletRef, onSendBack }) => { ]); useEffect(() => { - const { isNew, walletMasterPubKey, mnemonic } = current.context; + const { isNew, walletMasterPubKey, mnemonic, isLedger, isTrezor } = + current.context; switch (current.value) { case "createWalletInit": setIsNew(isNew); @@ -222,7 +221,7 @@ const CreateWalletPage = ({ createWalletRef, onSendBack }) => { checkIsValid(); break; case "restoreWatchingOnly": - onCreateWatchOnly(); + onCreateWatchOnly(isLedger, isTrezor); break; case "finished": break; diff --git a/app/components/views/GetStartedPage/CreateWalletPage/hooks.js b/app/components/views/GetStartedPage/CreateWalletPage/hooks.js index cdcb279050..65ff6a21a7 100644 --- a/app/components/views/GetStartedPage/CreateWalletPage/hooks.js +++ b/app/components/views/GetStartedPage/CreateWalletPage/hooks.js @@ -21,8 +21,15 @@ export const useCreateWallet = () => { ); // TODO implement pubpass const createWatchOnlyWalletRequest = useCallback( - (extendedPubKey, pubPass = "") => - dispatch(wla.createWatchOnlyWalletRequest(extendedPubKey, pubPass)), + (extendedPubKey, isLedger, isTrezor, pubPass = "") => + dispatch( + wla.createWatchOnlyWalletRequest( + extendedPubKey, + isLedger, + isTrezor, + pubPass + ) + ), [dispatch] ); const createWalletRequest = useCallback( diff --git a/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.jsx b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.jsx new file mode 100644 index 0000000000..69ecddc9f0 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.jsx @@ -0,0 +1,75 @@ +import { FormattedMessage as T, defineMessages } from "react-intl"; +import { TextInput } from "inputs"; +import { KeyBlueButton, InvisibleButton } from "buttons"; +import styles from "./CreateLedgerWalletForm.module.css"; +import { Documentation } from "shared"; + +const messages = defineMessages({ + walletNameInputPlaceholder: { + id: "createLedgerWallet.walletNameInput.placeholder", + defaultMessage: "Choose a name for your Ledger Wallet" + }, + walletNameInputLabel: { + id: "createLedgerWallet.walletNameInput.label", + defaultMessage: "Wallet Name" + }, + messageWalletDupeNameError: { + id: "createLedgerWallet.dupeWalletName.error", + defaultMessage: "Please choose an unused wallet name" + } +}); + +const CreateLedgerWalletForm = ({ + createWallet, + hideCreateWalletForm, + newWalletName, + walletNameError, + onChangeCreateWalletName, + hasFailedAttemptName, + intl +}) => ( +
+
+ +
+
+ + + +
+ +
+ onChangeCreateWalletName(e.target.value)} + label={intl.formatMessage(messages.walletNameInputLabel)} + placeholder={intl.formatMessage(messages.walletNameInputPlaceholder)} + showErrors={hasFailedAttemptName} + className={styles.walletNameInput} + /> + +
+ + + + + + +
+
+
+); + +export default CreateLedgerWalletForm; diff --git a/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.module.css b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.module.css new file mode 100644 index 0000000000..10040539b1 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.module.css @@ -0,0 +1,59 @@ +.container { + width: 764px; +} + +.grid { + display: grid; + grid-template-columns: repeat(2, 30rem); + grid-column-gap: 76px; + font-size: 13px; + line-height: 17px; + color: var(--grey-7); + margin: 38px 0 32px 0; +} + +.title { + font-size: 27px; + line-height: 34px; + color: var(--grey-7); + width: 32rem; +} + +.textToggle { + font-size: 13px; + line-height: 16px; + height: 29px; + grid-column: 1; + margin-top: 20px; +} + +.textToggleChild { + padding: 6px 31px; +} + +.walletNameInput, +.walletNameInput input { + width: 279px !important; +} + +.buttonContrainer { + width: 279px; + display: flex; + justify-content: flex-end; + margin-top: 20px; +} + +.cancelBt, +.createWalletBt { + height: 44px; +} + +@media screen and (max-width: 768px) { + .container { + width: 355px; + } + + .grid { + grid-template-columns: 30rem; + } +} diff --git a/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/index.js b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/index.js new file mode 100644 index 0000000000..b2674514b5 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/index.js @@ -0,0 +1 @@ +export { default } from "./CreateLedgerWalletForm"; diff --git a/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.jsx b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.jsx new file mode 100644 index 0000000000..71b2aece0a --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.jsx @@ -0,0 +1,23 @@ +import { FormattedMessage as T } from "react-intl"; +import styles from "./LedgerLoaderBarContainer.module.css"; +import { useLedgerLoaderBarContainer } from "./hooks"; + +const LedgerLoaderBarContainer = ({ loaderBar }) => { + const { ledgerDevice } = useLedgerLoaderBarContainer(); + return ledgerDevice ? ( +
+
+ + + + | + + + +
+
{loaderBar}
+
+ ) : null; +}; + +export default LedgerLoaderBarContainer; diff --git a/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.module.css b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.module.css new file mode 100644 index 0000000000..db06fe3a11 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.module.css @@ -0,0 +1,63 @@ +.loaderBarContainer { + position: fixed; + bottom: 0; + left: 0; + right: 0; + box-shadow: 0px -4px 8px rgba(0, 0, 0, 0.1); + display: grid; + grid-template-columns: 1fr 1fr; + align-items: center; + background-size: 14px; + background-repeat: no-repeat; + background-position: 25px center; + background-image: var(--tz-create-bt); +} + +.loaderBar div { + border-radius: 0; +} + +.loaderBar, +.loaderBar > div { + height: 60px; + background: linear-gradient( + 270deg, + var(--display-wallet-gradient-selected-right), + var(--input-color) + ); + color: var(--white-button-text); + padding-left: 1rem; + font-size: 1.1rem; +} + +.loaderBar > div > div:nth-of-type(1) { + grid-column: 2; + grid-row: 1; + align-items: center; + display: flex; + padding-left: 1rem; +} +.loaderBar > div > div:nth-of-type(2n) { + grid-column: 1; + grid-row: 1; +} + +.loaderBar > div > div:nth-of-type(2n) > div { + background-size: 2rem; + background-position: center; +} + +.deviceStatus { + font-size: 13px; + line-height: 16px; + padding-left: 55px; +} + +.deviceLabel { + font-weight: 600; + color: var(--main-dark-blue); +} + +.connected { + color: var(--accent-blue); +} diff --git a/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/hooks.js b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/hooks.js new file mode 100644 index 0000000000..e6b7f3a29a --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/hooks.js @@ -0,0 +1,7 @@ +import { useDaemonStartup } from "hooks"; + +export const useLedgerLoaderBarContainer = () => { + const { ledgerDevice } = useDaemonStartup(); + + return { ledgerDevice }; +}; diff --git a/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/index.js b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/index.js new file mode 100644 index 0000000000..0f8e9e04c6 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/index.js @@ -0,0 +1 @@ +export { default } from "./LedgerLoaderBarContainer"; diff --git a/app/components/views/GetStartedPage/PreCreateWallet/PreCreateWallet.jsx b/app/components/views/GetStartedPage/PreCreateWallet/PreCreateWallet.jsx index 545a208529..70261cc88d 100644 --- a/app/components/views/GetStartedPage/PreCreateWallet/PreCreateWallet.jsx +++ b/app/components/views/GetStartedPage/PreCreateWallet/PreCreateWallet.jsx @@ -1,9 +1,11 @@ import { FormattedMessage as T } from "react-intl"; import CreateWalletForm from "./CreateWalletForm"; import CreateTrezorWalletForm from "./CreateTrezorWalletForm"; +import CreateLedgerWalletForm from "./CreateLedgerWalletForm"; import { usePreCreateWallet } from "./hooks"; import { injectIntl } from "react-intl"; import NoDevicePage from "views/TrezorPage/NoDevicePage"; +import { default as NoDevicePageLedger } from "views/LedgerPage/NoDevicePage"; import styles from "./PreCreateWallet.module.css"; import { InvisibleButton } from "buttons"; @@ -15,7 +17,8 @@ const PreCreateWallet = ({ isCreateNewWallet, onShowCreateWallet, isTrezor, - creatingWallet + creatingWallet, + isLedger }) => { const { availableWallets, @@ -29,6 +32,7 @@ const PreCreateWallet = ({ walletNameError, isSPV, trezorDevice, + ledgerDevice, hideCreateWalletForm, onChangeCreateWalletName, createWallet, @@ -38,14 +42,16 @@ const PreCreateWallet = ({ toggleDisableCoinTypeUpgrades, gapLimit, setGapLimit, - connectTrezor + connectTrezor, + connectLedger } = usePreCreateWallet({ onSendContinue, onSendBack, onSendError, isCreateNewWallet, onShowCreateWallet, - isTrezor + isTrezor, + isLedger }); return isTrezor && !trezorDevice ? ( @@ -81,7 +87,45 @@ const PreCreateWallet = ({ disableCoinTypeUpgrades, toggleDisableCoinTypeUpgrades, gapLimit, - setGapLimit + setGapLimit, + isLedger + }} + /> + ) : isLedger && !ledgerDevice ? ( +
+ + + + +
+ ) : isLedger ? ( + ) : ( @@ -110,7 +154,8 @@ const PreCreateWallet = ({ disableCoinTypeUpgrades, toggleDisableCoinTypeUpgrades, gapLimit, - setGapLimit + setGapLimit, + isLedger }} /> ); diff --git a/app/components/views/GetStartedPage/PreCreateWallet/hooks.js b/app/components/views/GetStartedPage/PreCreateWallet/hooks.js index 41824abf41..30cd8fa93b 100644 --- a/app/components/views/GetStartedPage/PreCreateWallet/hooks.js +++ b/app/components/views/GetStartedPage/PreCreateWallet/hooks.js @@ -2,6 +2,7 @@ import { useState, useCallback, useEffect } from "react"; import { useDispatch } from "react-redux"; import { useDaemonStartup } from "hooks"; import * as trza from "actions/TrezorActions"; +import * as ldgr from "actions/LedgerActions"; export const usePreCreateWallet = ({ onSendContinue, @@ -9,7 +10,8 @@ export const usePreCreateWallet = ({ onSendError, isCreateNewWallet, onShowCreateWallet, - isTrezor + isTrezor, + isLedger }) => { const { isSPV, @@ -22,7 +24,12 @@ export const usePreCreateWallet = ({ trezorGetWalletCreationMasterPubKey, onCreateWallet, trezorEnable, - validateMasterPubKey + validateMasterPubKey, + ledgerDevice, + ledgerEnable, + ledgerDisable, + ledgerAlertNoConnectedDevice, + ledgerGetWalletCreationMasterPubKey } = useDaemonStartup(); const [newWalletName, setNewWalletName] = useState(""); const [isWatchingOnly, setIsWatchingOnly] = useState(false); @@ -38,8 +45,11 @@ export const usePreCreateWallet = ({ if (isTrezor) { trezorDisable(); } + if (isLedger) { + ledgerDisable(); + } onSendBack(); - }, [isTrezor, trezorDisable, onSendBack]); + }, [isTrezor, trezorDisable, onSendBack, isLedger, ledgerDisable]); const onChangeCreateWalletName = useCallback( (newWalletName) => { @@ -83,7 +93,18 @@ export const usePreCreateWallet = ({ } else { trezorDisable(); } - }, [isTrezor, trezorEnable, trezorDisable]); + if (isLedger) { + return ledgerEnable(); + } + return ledgerDisable(); + }, [ + isTrezor, + trezorEnable, + trezorDisable, + isLedger, + ledgerEnable, + ledgerDisable + ]); const toggleDisableCoinTypeUpgrades = () => setDisableCoinTypeUpgrades((value) => !value); @@ -96,6 +117,7 @@ export const usePreCreateWallet = ({ wallet: newWalletName, isWatchingOnly, isTrezor: !!isTrezor, + isLedger: !!isLedger, isNew, network: isTestNet ? "testnet" : "mainnet", gapLimit, @@ -112,8 +134,10 @@ export const usePreCreateWallet = ({ return; } if (isTrezor && !trezorDevice) { - trezorAlertNoConnectedDevice(); - return; + return trezorAlertNoConnectedDevice(); + } + if (isLedger && !ledgerDevice) { + return ledgerAlertNoConnectedDevice(); } // onSendContinue action so getStartedStateMachine can go to // creatingWallet state. @@ -130,6 +154,18 @@ export const usePreCreateWallet = ({ ) ); } + if (isLedger) { + walletSelected.isWatchingOnly = true; + return ledgerGetWalletCreationMasterPubKey().then((walletMasterPubKey) => + onCreateWallet(walletSelected).then(() => + onShowCreateWallet({ + isNew, + walletMasterPubKey, + isLedger: true + }) + ) + ); + } return onCreateWallet(walletSelected) .then(() => onShowCreateWallet({ isNew, walletMasterPubKey })) @@ -138,6 +174,7 @@ export const usePreCreateWallet = ({ isCreateNewWallet, isTestNet, isTrezor, + isLedger, isWatchingOnly, masterPubKeyError, newWalletName, @@ -147,6 +184,9 @@ export const usePreCreateWallet = ({ trezorAlertNoConnectedDevice, trezorDevice, trezorGetWalletCreationMasterPubKey, + ledgerAlertNoConnectedDevice, + ledgerDevice, + ledgerGetWalletCreationMasterPubKey, walletMasterPubKey, walletNameError, gapLimit, @@ -172,6 +212,7 @@ export const usePreCreateWallet = ({ const dispatch = useDispatch(); const connectTrezor = useCallback(() => dispatch(trza.connect()), [dispatch]); + const connectLedger = useCallback(() => dispatch(ldgr.connect()), [dispatch]); return { availableWallets, @@ -194,6 +235,8 @@ export const usePreCreateWallet = ({ toggleDisableCoinTypeUpgrades, gapLimit, setGapLimit, - connectTrezor + ledgerDevice, + connectTrezor, + connectLedger }; }; diff --git a/app/components/views/GetStartedPage/SetupWallet/hooks.js b/app/components/views/GetStartedPage/SetupWallet/hooks.js index d5b77e04a9..e346850c78 100644 --- a/app/components/views/GetStartedPage/SetupWallet/hooks.js +++ b/app/components/views/GetStartedPage/SetupWallet/hooks.js @@ -87,7 +87,7 @@ export const useWalletSetup = (settingUpWalletRef) => { isCreateNewWallet, isRestoreNewWallet } = ctx; - const { isWatchingOnly, isTrezor } = selectedWallet.value; + const { isWatchingOnly, isTrezor, isLedger } = selectedWallet.value; let component, hasSoloTickets; @@ -115,7 +115,7 @@ export const useWalletSetup = (settingUpWalletRef) => { switch (current.value) { case "settingAccountsPass": { // step not needed with trezor or watching only wallets. - if (isWatchingOnly || isTrezor) { + if (isWatchingOnly || isTrezor || isLedger) { sendContinue(); return; } diff --git a/app/components/views/GetStartedPage/WalletSelection/Form/Form.jsx b/app/components/views/GetStartedPage/WalletSelection/Form/Form.jsx index b8610894f6..b3bc54cf92 100644 --- a/app/components/views/GetStartedPage/WalletSelection/Form/Form.jsx +++ b/app/components/views/GetStartedPage/WalletSelection/Form/Form.jsx @@ -26,6 +26,7 @@ const WalletSelectionForm = ({ availableWallets, showCreateWalletForm, showCreateTrezorBackedWalletForm, + showCreateLedgerBackedWalletForm, onRemoveWallet, onToggleEditWallet, editWallets, @@ -39,6 +40,7 @@ const WalletSelectionForm = ({ const green = getThemeProperty(theme, "green-2"); const lightGreen = getThemeProperty(theme, "light-green"); const closeButtonColor = getThemeProperty(theme, "background-back-color"); + const ledgerIconColor = getThemeProperty(theme, "input-color-default"); const CreateButton = (props) => ( ); + const LedgerButton = (props) => ( + showCreateLedgerBackedWalletForm()} + text={intl.formatMessage(messages.ledgerTabMsg)} + iconColor={ledgerIconColor} + {...props} + /> + ); + return (
@@ -100,6 +112,7 @@ const WalletSelectionForm = ({ +
)} + {wallet.value.isLedger && ( +
+ + }> + + +
+ )} {wallet.isWatchingOnly && (
+ )}
diff --git a/app/components/views/GetStartedPage/WalletSelection/WalletSelection.jsx b/app/components/views/GetStartedPage/WalletSelection/WalletSelection.jsx index e4861af67a..a1a2751109 100644 --- a/app/components/views/GetStartedPage/WalletSelection/WalletSelection.jsx +++ b/app/components/views/GetStartedPage/WalletSelection/WalletSelection.jsx @@ -30,6 +30,10 @@ const WalletSelectionBody = ({ onSendCreateWallet(false, true); }, [onSendCreateWallet]); + const showCreateLedgerBackedWalletForm = useCallback(() => { + onSendCreateWallet(false, false, true); + }, [onSendCreateWallet]); + return ( diff --git a/app/components/views/GetStartedPage/hooks.js b/app/components/views/GetStartedPage/hooks.js index 6d6f6bfbc5..00bcd4dbc6 100644 --- a/app/components/views/GetStartedPage/hooks.js +++ b/app/components/views/GetStartedPage/hooks.js @@ -26,6 +26,7 @@ import styles from "./GetStarted.module.css"; import { isObject } from "lodash"; import { wallet } from "wallet-preload-shim"; import TrezorLoaderBarContainer from "views/GetStartedPage/PreCreateWallet/TrezorLoaderBarContainer"; +import LedgerLoaderBarContainer from "views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer"; import { LoaderBarContainer } from "./helpers"; export const useGetStarted = () => { @@ -164,7 +165,7 @@ export const useGetStarted = () => { isAtStartWallet: (context) => { const { selectedWallet } = context; const { passPhrase } = context; - const { isWatchingOnly, isTrezor } = selectedWallet.value; + const { isWatchingOnly, isTrezor, isLedger } = selectedWallet.value; const hasPassPhrase = !!passPhrase; onStartWallet(selectedWallet, hasPassPhrase) .then((discoverAccountsComplete) => { @@ -174,7 +175,8 @@ export const useGetStarted = () => { !discoverAccountsComplete && !passPhrase && !isWatchingOnly && - !isTrezor + !isTrezor && + !isLedger ) { // Need to discover accounts and the passphrase isn't stored in // context, so ask for the private passphrase before continuing. @@ -373,7 +375,8 @@ export const useGetStarted = () => { ); const onSendCreateWallet = useCallback( - (isNew, isTrezor) => send({ type: "CREATE_WALLET", isNew, isTrezor }), + (isNew, isTrezor, isLedger) => + send({ type: "CREATE_WALLET", isNew, isTrezor, isLedger }), [send] ); @@ -409,12 +412,13 @@ export const useGetStarted = () => { ); const onShowCreateWallet = useCallback( - ({ isNew, walletMasterPubKey, isTrezor }) => + ({ isNew, walletMasterPubKey, isTrezor, isLedger }) => send({ type: "SHOW_CREATE_WALLET", isNew, walletMasterPubKey, - isTrezor + isTrezor, + isLedger }), [send] ); @@ -489,7 +493,8 @@ export const useGetStarted = () => { isTrezor, isSPV, createWalletRef, - settingUpWalletRef + settingUpWalletRef, + isLedger } = state.context; let component, text, animationType, PageComponent; @@ -560,9 +565,19 @@ export const useGetStarted = () => { m="Create a trezor wallet..." /> ); - hideHeader = isTrezor; - showLoaderBar = isTrezor; - loaderBarContainer = isTrezor ? TrezorLoaderBarContainer : null; + text = isLedger && ( + + ); + hideHeader = isTrezor || isLedger; + showLoaderBar = isTrezor || isLedger; + loaderBarContainer = isTrezor + ? TrezorLoaderBarContainer + : isLedger + ? LedgerLoaderBarContainer + : null; component = h(PreCreateWalletForm, { onShowCreateWallet, onSendContinue, @@ -570,6 +585,7 @@ export const useGetStarted = () => { onSendError, isCreateNewWallet, isTrezor, + isLedger, error }); break; diff --git a/app/components/views/GetStartedPage/messages/messages.jsx b/app/components/views/GetStartedPage/messages/messages.jsx index df9a250106..1fe4f2a005 100644 --- a/app/components/views/GetStartedPage/messages/messages.jsx +++ b/app/components/views/GetStartedPage/messages/messages.jsx @@ -81,6 +81,10 @@ export const messages = defineMessages({ id: "getStarted.trezor", defaultMessage: "Setup a Trezor Wallet" }, + ledgerTabMsg: { + id: "getStarted.ledger", + defaultMessage: "Setup a Ledger Wallet" + }, closeEditWallets: { id: "getStarted.closeEditWallets", defaultMessage: "Close" @@ -108,6 +112,11 @@ export const messages = defineMessages({ defaultMessage: "Trezor is a hardware wallet. For more information, visit {link}" }, + messageWalletLedgerDescription: { + id: "createwallet.ledger.description", + defaultMessage: + "Ledger is a hardware wallet. For more information, visit {link}" + }, messageWalletMasterPubKey: { id: "createwallet.walletpubkey.placeholder", defaultMessage: "Master Pub Key" diff --git a/app/components/views/HomePage/HomePage.jsx b/app/components/views/HomePage/HomePage.jsx index 0406db5189..2bfb2ad465 100644 --- a/app/components/views/HomePage/HomePage.jsx +++ b/app/components/views/HomePage/HomePage.jsx @@ -56,8 +56,9 @@ export default () => { // TODO: Enable ticket purchacing for Trezor. const isTrezor = useSelector(sel.isTrezor); + const isLedger = useSelector(sel.isLedger); let recentTickets, tabs; - if (isTrezor) { + if (isTrezor || isLedger) { tabs = [balanceTab, transactionsTab]; } else { recentTickets = ( diff --git a/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.jsx b/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.jsx new file mode 100644 index 0000000000..194ce9a8f4 --- /dev/null +++ b/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.jsx @@ -0,0 +1,19 @@ +import styles from "./NoDevicePage.module.css"; +import { FormattedMessage as T } from "react-intl"; +import { KeyBlueButton } from "buttons"; + +const NoDevicePage = ({ onConnect }) => ( +
+
+ +
+ + + +
+); + +export default NoDevicePage; diff --git a/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.module.css b/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.module.css new file mode 100644 index 0000000000..ad8d45215e --- /dev/null +++ b/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.module.css @@ -0,0 +1,29 @@ +.container { + display: flex; + flex-direction: column; + align-items: center; +} + +.desc { + font-size: 27px; + line-height: 34px; + color: var(--grey-7); + width: 522px; + text-align: center; + margin-bottom: 40px; +} + +.button { + height: 29px; + font-size: 13px; + line-height: 16.34px; + padding: 6px 10px; +} + +@media screen and (max-width: 768px) { + .desc { + width: 80%; + font-size: 22px; + line-height: 23px; + } +} diff --git a/app/components/views/LedgerPage/NoDevicePage/index.js b/app/components/views/LedgerPage/NoDevicePage/index.js new file mode 100644 index 0000000000..ef97d62181 --- /dev/null +++ b/app/components/views/LedgerPage/NoDevicePage/index.js @@ -0,0 +1 @@ +export { default } from "./NoDevicePage"; diff --git a/app/components/views/PrivacyPage/PrivacyPage.jsx b/app/components/views/PrivacyPage/PrivacyPage.jsx index 78739f7463..bd66a5ad1b 100644 --- a/app/components/views/PrivacyPage/PrivacyPage.jsx +++ b/app/components/views/PrivacyPage/PrivacyPage.jsx @@ -1,10 +1,12 @@ import { TabbedPage, TitleHeader, DescriptionHeader } from "layout"; import { FormattedMessage as T } from "react-intl"; +import { useSelector } from "react-redux"; import SecurityTab from "./SecurityTab"; import PrivacyTab from "./PrivacyTab"; import { usePrivacyPage } from "./hooks"; import styles from "./PrivacyPage.module.css"; import { SECURITY_ICON } from "constants"; +import * as sel from "selectors"; export const PrivacyTabHeader = () => { const { mixedAccountName, changeAccountName } = usePrivacyPage(); @@ -46,21 +48,27 @@ const PrivacyPageHeader = () => ( const PrivacyPage = () => { const { privacyEnabled } = usePrivacyPage(); - const tabs = [ - { - path: "/privacy/mixing", - content: PrivacyTab, - header: PrivacyTabHeader, - label: , - disabled: !privacyEnabled - }, - { - path: "/privacy/security", - content: SecurityTab, - header: PrivacyTabHeader, - label: - } - ]; + const isTrezor = useSelector(sel.isTrezor); + const mixingTab = { + path: "/privacy/mixing", + content: PrivacyTab, + header: PrivacyTabHeader, + label: , + disabled: !privacyEnabled + }; + const securityTab = { + path: "/privacy/security", + content: SecurityTab, + header: PrivacyTabHeader, + label: + }; + let tabs; + // Cannot currently mix with trezor and ledger hides this tab altogether. + if (isTrezor) { + tabs = [securityTab]; + } else { + tabs = [mixingTab, securityTab]; + } return } tabs={tabs} />; }; diff --git a/app/components/views/TransactionsPage/ReceiveTab/ReceivePage/ReceivePage.jsx b/app/components/views/TransactionsPage/ReceiveTab/ReceivePage/ReceivePage.jsx index 48d28f37d5..045fe9e2f7 100644 --- a/app/components/views/TransactionsPage/ReceiveTab/ReceivePage/ReceivePage.jsx +++ b/app/components/views/TransactionsPage/ReceiveTab/ReceivePage/ReceivePage.jsx @@ -54,8 +54,9 @@ const ReceivePage = ({ // TODO: Enable ticket purchacing for Trezor. const isTrezor = useSelector(sel.isTrezor); + const isLedger = useSelector(sel.isLedger); let hardwareWalletWarning; - if (isTrezor) { + if (isTrezor || isLedger) { const warningStr = "Caution! Hardware wallets cannot spend from special/staking inputs. " + "Only use this address for receiving funds from normal transacitons. Do not " + diff --git a/app/constants/config.js b/app/constants/config.js index d700935986..4f1df9200c 100644 --- a/app/constants/config.js +++ b/app/constants/config.js @@ -59,6 +59,7 @@ export const IS_WATCH_ONLY = "iswatchonly"; export const POLITEIA_LAST_ACCESS_TIME = "politeia_last_access_time"; export const POLITEIA_LAST_ACCESS_BLOCK = "politeia_last_access_block"; export const TREZOR = "trezor"; +export const LEDGER = "ledger"; export const ENABLE_PRIVACY = "enableprivacy"; export const LN_ACCOUNT = "ln_account"; export const LN_ADDRESS = "ln_address"; @@ -98,6 +99,7 @@ export const WALLET_INITIAL_VALUE = { [POLITEIA_LAST_ACCESS_TIME]: 0, [POLITEIA_LAST_ACCESS_BLOCK]: 0, [TREZOR]: false, + [LEDGER]: false, // enable_privacy only shows the privacy menu on the wallet [ENABLE_PRIVACY]: true, [LN_ACCOUNT]: null, diff --git a/app/helpers/ledger.js b/app/helpers/ledger.js index 3948b27e89..c04452f17a 100644 --- a/app/helpers/ledger.js +++ b/app/helpers/ledger.js @@ -14,7 +14,16 @@ export function addressPath(branch, index) { // fixPubKeyChecksum replaces the sha256 checksum, or last four bytes, of a // pubkey with a blake256 checksum. -export function fixPubKeyChecksum(pubKey) { +export function fixPubKeyChecksum(pubKey, isTestnet) { + const mainnetPubPrefix = "dpub"; + const testnetPubPrefix = "tpub"; + if (isTestnet) { + if (pubKey.slice(0, 4) !== testnetPubPrefix) { + throw "pubkey is not for testnet"; + } + } else if (pubKey.slice(0, 4) !== mainnetPubPrefix) { + throw "pubkey is not for mainnet"; + } const buff = bs58.decode(pubKey).slice(0, -4); const firstPass = blake("blake256").update(Buffer.from(buff)).digest(); const secondPass = blake("blake256").update(firstPass).digest(); diff --git a/app/hooks/useDaemonStartup.js b/app/hooks/useDaemonStartup.js index 676aea1f0f..20f2588321 100644 --- a/app/hooks/useDaemonStartup.js +++ b/app/hooks/useDaemonStartup.js @@ -6,6 +6,7 @@ import * as da from "actions/DaemonActions"; import * as ca from "actions/ClientActions"; import * as ctrla from "actions/ControlActions"; import * as trza from "actions/TrezorActions"; +import * as ldgr from "actions/LedgerActions"; import * as ama from "actions/AccountMixerActions"; import * as va from "actions/VSPActions"; @@ -38,7 +39,9 @@ const useDaemonStartup = () => { const getDaemonStarted = useSelector(sel.getDaemonStarted); const getEstimatedTimeLeft = useSelector(sel.getEstimatedTimeLeft); const trezorDevice = useSelector(sel.trezorDevice); + const ledgerDevice = useSelector(sel.ledgerDevice); const isTrezor = useSelector(sel.isTrezor); + const isLedger = useSelector(sel.isLedger); const syncAttemptRequest = useSelector(sel.getSyncAttemptRequest); const daemonWarning = useSelector(sel.daemonWarning); const isSettingAccountsPassphrase = useSelector( @@ -230,6 +233,22 @@ const useDaemonStartup = () => { () => dispatch(trza.getWalletCreationMasterPubKey()), [dispatch] ); + const ledgerEnable = useCallback( + () => dispatch(ldgr.enableLedger()), + [dispatch] + ); + const ledgerDisable = useCallback( + () => dispatch(ldgr.disableLedger()), + [dispatch] + ); + const ledgerAlertNoConnectedDevice = useCallback( + () => dispatch(ldgr.alertNoConnectedDevice()), + [dispatch] + ); + const ledgerGetWalletCreationMasterPubKey = useCallback( + () => dispatch(ldgr.getWalletCreationMasterPubKey()), + [dispatch] + ); const validateMasterPubKey = useCallback( (masterPubKey) => dispatch(ctrla.validateMasterPubKey(masterPubKey)), [dispatch] @@ -279,6 +298,11 @@ const useDaemonStartup = () => { trezorDisable, trezorEnable, trezorLoadDeviceList, + ledgerGetWalletCreationMasterPubKey, + ledgerAlertNoConnectedDevice, + ledgerDisable, + ledgerEnable, + ledgerDevice, getDcrwalletLogs, onCreateWallet, goToErrorPage, @@ -325,6 +349,7 @@ const useDaemonStartup = () => { getEstimatedTimeLeft, trezorDevice, isTrezor, + isLedger, peerCount, synced, syncFetchMissingCfiltersAttempt, diff --git a/app/hooks/useLedger.js b/app/hooks/useLedger.js new file mode 100644 index 0000000000..0f5abeb950 --- /dev/null +++ b/app/hooks/useLedger.js @@ -0,0 +1,30 @@ +import { useSelector, useDispatch } from "react-redux"; +import { useCallback } from "react"; +import * as sel from "selectors"; +import * as ldgr from "actions/LedgerActions"; + +const useLedger = () => { + const isLedger = useSelector(sel.isLedger); + const device = useSelector(sel.ledgerDevice); + const walletCreationMasterPubkeyAttempt = useSelector( + sel.ledgerWalletCreationMasterPubkeyAttempt + ); + + const dispatch = useDispatch(); + + const onConnect = useCallback(() => dispatch(ldgr.connect()), [dispatch]); + const onEnableLedger = useCallback( + () => dispatch(ldgr.enableLedger()), + [dispatch] + ); + + return { + isLedger, + device, + walletCreationMasterPubkeyAttempt, + onConnect, + onEnableLedger + }; +}; + +export default useLedger; diff --git a/app/hooks/useSettings.js b/app/hooks/useSettings.js index 7a09c2b75e..4167becfc1 100644 --- a/app/hooks/useSettings.js +++ b/app/hooks/useSettings.js @@ -24,6 +24,7 @@ const useSettings = () => { const walletName = useSelector(sel.getWalletName); const walletReady = useSelector(sel.getWalletReady); const isTrezor = useSelector(sel.isTrezor); + const isLedger = useSelector(sel.isLedger); const onAttemptChangePassphrase = useCallback( (oldPass, args) => { @@ -124,6 +125,7 @@ const useSettings = () => { walletName, walletReady, isTrezor, + isLedger, onAttemptChangePassphrase, onChangeTempSettings, onSaveSettings, diff --git a/app/i18n/docs/en/Ledger/PreCreateLedgerWallet1.md b/app/i18n/docs/en/Ledger/PreCreateLedgerWallet1.md new file mode 100644 index 0000000000..e2719b812c --- /dev/null +++ b/app/i18n/docs/en/Ledger/PreCreateLedgerWallet1.md @@ -0,0 +1,3 @@ +You must use an already setup Ledger “Device Wallet”, a device already initialized with a seed and the decred app installed. New devices should be initialized via [ledger live](https://www.ledger.com/ledger-live/) and the decred app downloaded and installed there. + +A Ledger-backed wallet can be used with multiple cryptocurrencies without conflict. In other words, if you already use your device wallet with other coins, you can keep using it and use the same seed for your Decrediton wallet. diff --git a/app/i18n/docs/en/Ledger/PreCreateLedgerWallet2.md b/app/i18n/docs/en/Ledger/PreCreateLedgerWallet2.md new file mode 100644 index 0000000000..a65e8d55df --- /dev/null +++ b/app/i18n/docs/en/Ledger/PreCreateLedgerWallet2.md @@ -0,0 +1 @@ +Ledger wallets used within Decrediton do not currently support staking operations (purchasing, voting and revoking tickets) or signing messages. diff --git a/app/i18n/docs/en/index.js b/app/i18n/docs/en/index.js index d203c295dd..1fc827f2da 100644 --- a/app/i18n/docs/en/index.js +++ b/app/i18n/docs/en/index.js @@ -94,3 +94,6 @@ export { default as TrezorRecoverDevice } from "./Trezor/RecoverDevice.md"; export { default as TrezorWipeDevice } from "./Trezor/WipeDevice.md"; export { default as PreCreateTrezorWallet1 } from "./Trezor/PreCreateTrezorWallet1.md"; export { default as PreCreateTrezorWallet2 } from "./Trezor/PreCreateTrezorWallet2.md"; + +export { default as PreCreateLedgerWallet1 } from "./Ledger/PreCreateLedgerWallet1.md"; +export { default as PreCreateLedgerWallet2 } from "./Ledger/PreCreateLedgerWallet2.md"; diff --git a/app/index.js b/app/index.js index 48abab17fc..20493999a4 100644 --- a/app/index.js +++ b/app/index.js @@ -406,6 +406,11 @@ const initialState = { performingTogglePassphraseOnDeviceProtection: false, deviceLabel: undefined }, + ledger: { + enabled: false, + device: false, + walletCreationMasterPubkeyAttempt: false + }, ln: { enabled: globalCfg.get(cfgConstants.LN_ENABLED), active: false, diff --git a/app/main_dev/externalRequests.js b/app/main_dev/externalRequests.js index 0eef75a5b9..a87463a7d5 100644 --- a/app/main_dev/externalRequests.js +++ b/app/main_dev/externalRequests.js @@ -78,7 +78,7 @@ export const installSessionHandlers = (mainLogger) => { ); callback({ cancel: true, requestHeaders: details.requestHeaders }); } else { - //logger.log("verbose", details.method + " " + details.url); + // logger.log("verbose", details.method + " " + details.url); if ( allowedExternalRequests[EXTERNALREQUEST_TREZOR_BRIDGE] && /^http:\/\/127.0.0.1:21325\//.test(details.url) diff --git a/app/main_dev/ipc.js b/app/main_dev/ipc.js index 204968eb5e..7f0c5f1b5b 100644 --- a/app/main_dev/ipc.js +++ b/app/main_dev/ipc.js @@ -56,6 +56,7 @@ export const getAvailableWallets = (network) => { const lastAccess = cfg.get(cfgConstants.LAST_ACCESS); const isWatchingOnly = cfg.get(cfgConstants.IS_WATCH_ONLY); const isTrezor = cfg.get(cfgConstants.TREZOR); + const isLedger = cfg.get(cfgConstants.LEDGER); const isPrivacy = cfg.get(cfgConstants.MIXED_ACCOUNT_CFG); const walletDbFilePath = getWalletDb(isTestNet, wallet); const finished = fs.existsSync(walletDbFilePath); @@ -68,6 +69,7 @@ export const getAvailableWallets = (network) => { lastAccess, isWatchingOnly, isTrezor, + isLedger, isPrivacy, isLN, displayWalletGradient diff --git a/app/reducers/index.js b/app/reducers/index.js index e0f50f4f35..51c57de385 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -12,6 +12,7 @@ import snackbar from "./snackbar"; import statistics from "./statistics"; import governance from "./governance"; import trezor from "./trezor"; +import ledger from "./ledger"; import ln from "./ln"; import vsp from "./vsp"; import dex from "./dex"; @@ -30,6 +31,7 @@ export default { statistics, governance, trezor, + ledger, ln, vsp, dex diff --git a/app/reducers/ledger.js b/app/reducers/ledger.js new file mode 100644 index 0000000000..db47290a5b --- /dev/null +++ b/app/reducers/ledger.js @@ -0,0 +1,61 @@ +import { + LDG_WALLET_CLOSED, + LDG_LEDGER_ENABLED, + LDG_LEDGER_DISABLED, + LDG_CONNECT_ATTEMPT, + LDG_CONNECT_FAILED, + LDG_CONNECT_SUCCESS, + LDG_NOCONNECTEDDEVICE, + LDG_GETWALLETCREATIONMASTERPUBKEY_ATTEMPT, + LDG_GETWALLETCREATIONMASTERPUBKEY_FAILED, + LDG_GETWALLETCREATIONMASTERPUBKEY_SUCCESS +} from "actions/LedgerActions"; +import { CLOSEWALLET_SUCCESS } from "actions/WalletLoaderActions"; + +export default function ledger(state = {}, action) { + switch (action.type) { + case LDG_LEDGER_ENABLED: + return { ...state, enabled: true }; + case LDG_LEDGER_DISABLED: + return { ...state, enabled: false }; + case LDG_CONNECT_ATTEMPT: + return { + ...state, + connectAttempt: true + }; + case LDG_CONNECT_SUCCESS: + return { + ...state, + // Ledger does not keep a constant connection. Device is set to true on + // the first successful attempt and left true until the wallet is closed. + device: true, + connectAttempt: false + }; + case LDG_CONNECT_FAILED: + return { + ...state, + connectError: action.error, + connectAttempt: false + }; + case LDG_NOCONNECTEDDEVICE: + // We don't currently listen for reconnect so not deleting the device. + return { + ...state + }; + case LDG_WALLET_CLOSED: + return { + ...state, + device: false, + connected: false + }; + case LDG_GETWALLETCREATIONMASTERPUBKEY_ATTEMPT: + return { ...state, walletCreationMasterPubkeyAttempt: true }; + case LDG_GETWALLETCREATIONMASTERPUBKEY_SUCCESS: + case LDG_GETWALLETCREATIONMASTERPUBKEY_FAILED: + return { ...state, walletCreationMasterPubkeyAttempt: false }; + case CLOSEWALLET_SUCCESS: + return { ...state, enabled: false }; + default: + return state; + } +} diff --git a/app/reducers/snackbar.js b/app/reducers/snackbar.js index fb0f74682e..287a318786 100644 --- a/app/reducers/snackbar.js +++ b/app/reducers/snackbar.js @@ -105,6 +105,10 @@ import { TRZ_GETWALLETCREATIONMASTERPUBKEY_FAILED, TRZ_NOTBACKEDUP } from "actions/TrezorActions"; +import { + LDG_NOCONNECTEDDEVICE, + LDG_GETWALLETCREATIONMASTERPUBKEY_FAILED +} from "actions/LedgerActions"; import { NEW_TRANSACTIONS_RECEIVED, TRANSACTION_TYPES, @@ -362,6 +366,16 @@ const messages = defineMessages({ defaultMessage: "Trezor must be backed up in order to perform this operation." }, + LDG_NOCONNECTEDDEVICE: { + id: "ledger.noConnectedDevice", + defaultMessage: + "No Ledger device connected. Check the device connection and Ledger bridge." + }, + LDG_GETWALLETCREATIONMASTERPUBKEY_FAILED: { + id: "ledger.getWalletCreationMasterPubKey.failed", + defaultMessage: + "Failed to obtain master extended pubkey from Ledger device: {originalError}" + }, ERROR_IS_OBJECT: { id: "snackbar.errorObject", defaultMessage: "The following error happened: {error}" @@ -839,6 +853,8 @@ export default function snackbar(state = {}, action) { case TRZ_NOTBACKEDUP: case TRZ_BACKUPDEVICE_FAILED: case TRZ_GETWALLETCREATIONMASTERPUBKEY_FAILED: + case LDG_NOCONNECTEDDEVICE: + case LDG_GETWALLETCREATIONMASTERPUBKEY_FAILED: case LNWALLET_CREATEACCOUNT_FAILED: case LNWALLET_STARTDCRLND_FAILED: case LNWALLET_CONNECT_FAILED: diff --git a/app/reducers/walletLoader.js b/app/reducers/walletLoader.js index b566505ee6..304f3fee1c 100644 --- a/app/reducers/walletLoader.js +++ b/app/reducers/walletLoader.js @@ -71,7 +71,8 @@ export default function walletLoader(state = {}, action) { return { ...state, isWatchingOnly: action.isWatchingOnly, - isTrezor: action.isTrezor + isTrezor: action.isTrezor, + isLedger: action.isLedger }; case WALLET_SELECTED: return { ...state, selectedWallet: action.selectedWallet }; diff --git a/app/selectors.js b/app/selectors.js index 8a287b5591..6c1b1e0aca 100644 --- a/app/selectors.js +++ b/app/selectors.js @@ -1104,9 +1104,13 @@ export const confirmationDialogModalVisible = bool( export const isTrezor = get(["trezor", "enabled"]); export const isPerformingTrezorUpdate = get(["trezor", "performingUpdate"]); +export const isLedger = get(["ledger", "enabled"]); + export const isSignMessageDisabled = and(isWatchingOnly, not(isTrezor)); export const isChangePassPhraseDisabled = isWatchingOnly; -export const isTransactionsSendTabDisabled = not(isTrezor); +export const isTransactionsSendTabDisabled = bool( + and(not(isTrezor), not(isLedger)) +); export const politeiaURL = createSelector([isTestNet], (isTestNet) => isTestNet ? POLITEIA_URL_TESTNET : POLITEIA_URL_MAINNET @@ -1313,6 +1317,12 @@ export const trezorWalletCreationMasterPubkeyAttempt = get([ "walletCreationMasterPubkeyAttempt" ]); +export const ledgerDevice = get(["ledger", "device"]); +export const ledgerWalletCreationMasterPubkeyAttempt = get([ + "ledger", + "walletCreationMasterPubkeyAttempt" +]); + // selectors for checking if decrediton can be closed. // getRunningIndicator is a indicator for indicate something is runnning on // decrediton, like the ticket auto buyer or the mixer. @@ -1329,7 +1339,9 @@ export const loggedInDex = bool(get(["dex", "loggedIn"])); // ln selectors -export const lnEnabled = bool(and(not(isWatchingOnly), not(isTrezor))); +export const lnEnabled = bool( + and(not(isWatchingOnly), not(isTrezor), not(isLedger)) +); export const lnActive = bool(get(["ln", "active"])); export const lnStartupStage = get(["ln", "startupStage"]); export const lnStartAttempt = bool(get(["ln", "startAttempt"])); diff --git a/app/stateMachines/CreateWalletStateMachine.js b/app/stateMachines/CreateWalletStateMachine.js index 380b2423f9..cd73379106 100644 --- a/app/stateMachines/CreateWalletStateMachine.js +++ b/app/stateMachines/CreateWalletStateMachine.js @@ -13,7 +13,9 @@ export const CreateWalletMachine = Machine({ mnemonic: "", seed: "", passPhrase: "", - walletMasterPubKey: "" + walletMasterPubKey: "", + isTrezor: false, + isLedger: false }, states: { createWalletInit: { @@ -26,10 +28,6 @@ export const CreateWalletMachine = Machine({ target: "restoreWatchingOnly", cond: (c, event) => event.isWatchingOnly }, - RESTORE_TREZOR_WALLET: { - target: "restoreTrezor", - cond: (c, event) => event.isTrezor - }, RESTORE_WALLET: { target: "writeSeed", cond: (c, event) => event.isRestore @@ -124,11 +122,6 @@ export const CreateWalletMachine = Machine({ CONTINUE: "loading" } }, - restoreTrezor: { - on: { - CONTINUE: "loading" - } - }, loading: { on: { ERROR: { diff --git a/app/stateMachines/GetStartedStateMachine.js b/app/stateMachines/GetStartedStateMachine.js index 075c7c268e..0c100aae19 100644 --- a/app/stateMachines/GetStartedStateMachine.js +++ b/app/stateMachines/GetStartedStateMachine.js @@ -26,6 +26,7 @@ export const getStartedMachine = Machine({ initial: "preStart", on: { SHOW_TREZOR_CONFIG: "trezorConfig", + SHOW_LEDGER_CONFIG: "ledgerConfig", SHOW_CREATE_WALLET: "creatingWallet", SHOW_SETTING_UP_WALLET: "settingUpWallet" }, @@ -213,7 +214,8 @@ export const getStartedMachine = Machine({ !isUndefined(event.isNew) ? !event.isNew : context.isRestoreNewWallet, - isTrezor: (context, event) => event.isTrezor + isTrezor: (context, event) => event.isTrezor, + isLedger: (context, event) => event.isLedger }) }, ERROR: { @@ -319,7 +321,8 @@ export const getStartedMachine = Machine({ CreateWalletMachine.withContext({ isNew: e.isNew, walletMasterPubKey: e.walletMasterPubKey, - isTrezor: e.isTrezor + isTrezor: e.isTrezor, + isLedger: e.isLedger }) ); } catch (e) { @@ -373,6 +376,7 @@ export const getStartedMachine = Machine({ isCreateNewWallet: ctx.isCreateNewWallet, isWatchingOnly: ctx.selectedWallet.isWatchingOnly, isTrezor: ctx.selectedWallet.isTrezor, + isLedger: ctx.selectedWallet.isLedger, passPhrase: ctx.passPhrase }) ); @@ -394,6 +398,16 @@ export const getStartedMachine = Machine({ BACK: "startMachine.hist", SHOW_TREZOR_CONFIG: "trezorConfig" } + }, + ledgerConfig: { + initial: "ledgerConfig", + states: { + ledgerConfig: {} + }, + on: { + BACK: "startMachine.hist", + SHOW_LEDGER_CONFIG: "ledgerConfig" + } } } }); diff --git a/package.json b/package.json index 7a92245444..2a0b26da71 100644 --- a/package.json +++ b/package.json @@ -274,7 +274,7 @@ "@grpc/grpc-js": "1.7.3", "@hot-loader/react-dom": "16.14.0", "@ledgerhq/hw-app-btc": "^10.0.5", - "@ledgerhq/hw-transport-webusb": "^6.27.16", + "@ledgerhq/hw-transport-webusb": "6.27.1", "@peculiar/webcrypto": "1.4.3", "@xstate/react": "^0.8.1", "blake-hash": "^2.0.0", @@ -297,7 +297,7 @@ "minimist": "1.2.8", "mv": "^2.1.1", "node-polyfill-webpack-plugin": "1.1.4", - "pi-ui": "https://github.com/decred/pi-ui/", + "pi-ui": "https://github.com/decred/pi-ui#4daf214b901aa9f50547cf5645594cb76e7c9e51", "postcss": "8.4.24", "postcss-loader": "^7.0.1", "postcss-preset-env": "7.8.3", @@ -343,5 +343,8 @@ "engines": { "node": ">=14.16", "yarn": "^1.22" + }, + "resolutions": { + "@ledgerhq/hw-transport": "6.27.1" } } diff --git a/test/unit/actions/DaemonActions.spec.js b/test/unit/actions/DaemonActions.spec.js index 971a8ca48f..5b416fb893 100644 --- a/test/unit/actions/DaemonActions.spec.js +++ b/test/unit/actions/DaemonActions.spec.js @@ -883,7 +883,7 @@ test("test createWallet", async () => { selectedWallet.value.isWatchingOnly ); expect(store.getState().walletLoader.isTrezor).toBe( - selectedWallet.value.istrezor + selectedWallet.value.isTrezor ); expect(mockWalletCfgSet).toHaveBeenCalledWith( diff --git a/test/unit/components/views/GetStaredPage/CreateWallet.spec.js b/test/unit/components/views/GetStaredPage/CreateWallet.spec.js index ba88768eda..0135854aba 100644 --- a/test/unit/components/views/GetStaredPage/CreateWallet.spec.js +++ b/test/unit/components/views/GetStaredPage/CreateWallet.spec.js @@ -24,6 +24,7 @@ const testSelectedWallet = { value: { isNew: true, isTrezor: false, + isLedger: false, isWatchingOnly: false, network: "mainnet", wallet: testWalletName, diff --git a/test/unit/components/views/GetStaredPage/PreCreateWallet.spec.js b/test/unit/components/views/GetStaredPage/PreCreateWallet.spec.js index 47df248fac..3a68a3cb69 100644 --- a/test/unit/components/views/GetStaredPage/PreCreateWallet.spec.js +++ b/test/unit/components/views/GetStaredPage/PreCreateWallet.spec.js @@ -19,6 +19,7 @@ const testSelectedWallet = { value: { isNew: true, isTrezor: false, + isLedger: false, isWatchingOnly: false, network: "mainnet", wallet: testWalletName, @@ -258,6 +259,8 @@ test("test watch only control on restore wallet", async () => { await wait(() => expect(mockCreateWatchOnlyWalletRequest).toHaveBeenCalledWith( testValidMasterPubKey, + undefined, + undefined, "" ) ); @@ -286,7 +289,7 @@ test("test create trezor-backed wallet page (trezor device is connected)", async }); const testRestoreSelectedWallet = { ...testSelectedWallet, - value: { ...testSelectedWallet.value, isNew: false, isTrezor: true }, + value: { ...testSelectedWallet.value, isNew: false, isTrezor: true, isLedger: false }, isWatchingOnly: true }; @@ -338,6 +341,8 @@ test("test create trezor-backed wallet page (trezor device is connected)", async await wait(() => expect(mockCreateWatchOnlyWalletRequest).toHaveBeenCalledWith( testWalletCreationMasterPubKey, + undefined, + true, "" ) ); diff --git a/test/unit/helpers/ledger.spec.js b/test/unit/helpers/ledger.spec.js index 4a9d2ff655..0b49b6312f 100644 --- a/test/unit/helpers/ledger.spec.js +++ b/test/unit/helpers/ledger.spec.js @@ -6,9 +6,9 @@ test("test ledger address path", () => { test("test ledger pubkey to hd key", () => { // testnet - expect(fixPubKeyChecksum("tpubVpeRVBDM14ydoJ7jCzTayDHiP6CU4NcbKEW6bWQGSghmi14BfNx3omc6GjV3AxbtkTYcPAedw48XywVgSyY8H9ef73zWWtoZ6MCMLLJiUyq")).toStrictEqual("tpubVpeRVBDM14ydoJ7jCzTayDHiP6CU4NcbKEW6bWQGSghmi14BfNx3omc6GjV3AxbtkTYcPAedw48XywVgSyY8H9ef73zWWtoZ6MCMLGM6GV3"); + expect(fixPubKeyChecksum("tpubVpeRVBDM14ydoJ7jCzTayDHiP6CU4NcbKEW6bWQGSghmi14BfNx3omc6GjV3AxbtkTYcPAedw48XywVgSyY8H9ef73zWWtoZ6MCMLLJiUyq", true)).toStrictEqual("tpubVpeRVBDM14ydoJ7jCzTayDHiP6CU4NcbKEW6bWQGSghmi14BfNx3omc6GjV3AxbtkTYcPAedw48XywVgSyY8H9ef73zWWtoZ6MCMLGM6GV3"); // mainet - expect(fixPubKeyChecksum("dpubZFs9f5ex4qxoiqCHVEnotAaYpZhCgwDAndMyLicfcXJysBmNxMyx7X3QHW2NiUN14KnekWcMQt4XwyF5wAqDPzbLcv59mQTRhL6foyvvqLK")).toStrictEqual("dpubZFs9f5ex4qxoiqCHVEnotAaYpZhCgwDAndMyLicfcXJysBmNxMyx7X3QHW2NiUN14KnekWcMQt4XwyF5wAqDPzbLcv59mQTRhL6fp1csmnS"); + expect(fixPubKeyChecksum("dpubZFs9f5ex4qxoiqCHVEnotAaYpZhCgwDAndMyLicfcXJysBmNxMyx7X3QHW2NiUN14KnekWcMQt4XwyF5wAqDPzbLcv59mQTRhL6foyvvqLK", false)).toStrictEqual("dpubZFs9f5ex4qxoiqCHVEnotAaYpZhCgwDAndMyLicfcXJysBmNxMyx7X3QHW2NiUN14KnekWcMQt4XwyF5wAqDPzbLcv59mQTRhL6fp1csmnS"); }); function dispatchFn (inputs) { diff --git a/yarn.lock b/yarn.lock index d6edb6d758..dfd57ecc84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1795,17 +1795,17 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@ledgerhq/devices@^8.0.4": - version "8.0.4" - resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.0.4.tgz#ebc7779adbbec2d046424603a481623eb3fbe306" - integrity sha512-dxOiWZmtEv1tgw70+rW8gviCRZUeGDUnxY6HUPiRqTAc0Ts2AXxiJChgAsPvIywWTGW+S67Nxq1oTZdpRbdt+A== +"@ledgerhq/devices@^6.27.1": + version "6.27.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-6.27.1.tgz#3b13ab1d1ba8201e9e74a08f390560483978c962" + integrity sha512-jX++oy89jtv7Dp2X6gwt3MMkoajel80JFWcdc0HCouwDsV1mVJ3SQdwl/bQU0zd8HI6KebvUP95QTwbQLLK/RQ== dependencies: - "@ledgerhq/errors" "^6.12.7" - "@ledgerhq/logs" "^6.10.1" + "@ledgerhq/errors" "^6.10.0" + "@ledgerhq/logs" "^6.10.0" rxjs "6" semver "^7.3.5" -"@ledgerhq/errors@^6.12.7": +"@ledgerhq/errors@^6.10.0": version "6.12.7" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.12.7.tgz#c7b630488d5713bc7b1e1682d6ab5d08918c69f1" integrity sha512-1BpjzFErPK7qPFx0oItcX0mNLJMplVAm2Dpl5urZlubewnTyyw5sahIBjU+8LLCWJ2eGEh/0wyvh0jMtR0n2Mg== @@ -1828,26 +1828,26 @@ tiny-secp256k1 "1.1.6" varuint-bitcoin "1.1.2" -"@ledgerhq/hw-transport-webusb@^6.27.16": - version "6.27.16" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webusb/-/hw-transport-webusb-6.27.16.tgz#b8e20e772f78c312fc7f2ce3a469c99ecf59dc67" - integrity sha512-A3S2p5Rh9Ot402pWNZw8v5EpO3wOHP8ch/Dcz0AjInmwNouQ9nIYd1+eLSL7QiyG9X7+tuHxFF1IjrEgvAzQuQ== +"@ledgerhq/hw-transport-webusb@6.27.1": + version "6.27.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webusb/-/hw-transport-webusb-6.27.1.tgz#62be3b2e5f0d54ae06d066571e4408a807d0f01f" + integrity sha512-n0ygJSeRpznrUfwtbDCLQOM5mA23YT/ngYY8HU46dzsVJHrHQ4jwBNJU48iKB+a9GhHyPAUpPNlWGTogvoVUxg== dependencies: - "@ledgerhq/devices" "^8.0.4" - "@ledgerhq/errors" "^6.12.7" - "@ledgerhq/hw-transport" "^6.28.5" - "@ledgerhq/logs" "^6.10.1" + "@ledgerhq/devices" "^6.27.1" + "@ledgerhq/errors" "^6.10.0" + "@ledgerhq/hw-transport" "^6.27.1" + "@ledgerhq/logs" "^6.10.0" -"@ledgerhq/hw-transport@^6.28.5": - version "6.28.5" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.28.5.tgz#675193be2f695a596068145351da598316c25831" - integrity sha512-xmw5RhYbqExBBqTvOnOjN/RYNIGMBxFJ+zcYNfkfw/E+uEY3L7xq8Z7sC/n7URTT6xtEctElqduBJnBQE4OQtw== +"@ledgerhq/hw-transport@6.27.1", "@ledgerhq/hw-transport@^6.27.1", "@ledgerhq/hw-transport@^6.28.5": + version "6.27.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.27.1.tgz#88072278f69c279cb6569352acd4ae2fec33ace3" + integrity sha512-hnE4/Fq1YzQI4PA1W0H8tCkI99R3UWDb3pJeZd6/Xs4Qw/q1uiQO+vNLC6KIPPhK0IajUfuI/P2jk0qWcMsuAQ== dependencies: - "@ledgerhq/devices" "^8.0.4" - "@ledgerhq/errors" "^6.12.7" + "@ledgerhq/devices" "^6.27.1" + "@ledgerhq/errors" "^6.10.0" events "^3.3.0" -"@ledgerhq/logs@^6.10.1": +"@ledgerhq/logs@^6.10.0", "@ledgerhq/logs@^6.10.1": version "6.10.1" resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.10.1.tgz#5bd16082261d7364eabb511c788f00937dac588d" integrity sha512-z+ILK8Q3y+nfUl43ctCPuR4Y2bIxk/ooCQFwZxhtci1EhAtMDzMAx2W25qx8G1PPL9UUOdnUax19+F0OjXoj4w== @@ -10405,9 +10405,9 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -"pi-ui@https://github.com/decred/pi-ui/": +"pi-ui@https://github.com/decred/pi-ui#4daf214b901aa9f50547cf5645594cb76e7c9e51": version "1.1.0" - resolved "https://github.com/decred/pi-ui/#d7ca43e8bc65a45bbebd93b0101e58f3a6a170ab" + resolved "https://github.com/decred/pi-ui#4daf214b901aa9f50547cf5645594cb76e7c9e51" dependencies: clamp-js-main "^0.11.5" lodash "^4.17.15" From 74c3fdf76249423c3208472e9df6999ece956e1d Mon Sep 17 00:00:00 2001 From: Matheus Degiovani Date: Mon, 25 Sep 2023 11:24:25 -0300 Subject: [PATCH 09/15] [actions] Make GetWalletService syncable (#3900) This fixes a bug when wallet operations (for example GetBestBlock) are attempted before the GetWalletServiceAttempt fully completes, causing the wallet not to load. --- app/actions/ClientActions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/actions/ClientActions.js b/app/actions/ClientActions.js index 6ee969c349..a8bf9c015c 100644 --- a/app/actions/ClientActions.js +++ b/app/actions/ClientActions.js @@ -184,7 +184,7 @@ export const getWalletServiceAttempt = () => (dispatch, getState) => { } = getState(); dispatch({ type: GETWALLETSERVICE_ATTEMPT }); const grpcCertAndKey = wallet.getDcrwalletGrpcKeyCert(); - wallet + return wallet .getWalletService( sel.isTestNet(getState()), walletName, From b30cbafdb4b3b226ade0465b717e9d76cb004a15 Mon Sep 17 00:00:00 2001 From: Matt Hawkins <47730539+matthawkins90@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:25:07 -0500 Subject: [PATCH 10/15] [multi] Minor updates to tutorial text (#3892) --- .../SettingsPage/TutorialsTab/helpers/tutorials.js | 14 +++++++------- app/i18n/docs/en/Tutorials/PowPosPage03.md | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/components/views/SettingsPage/TutorialsTab/helpers/tutorials.js b/app/components/views/SettingsPage/TutorialsTab/helpers/tutorials.js index 9f64f99730..1fd9ab3101 100644 --- a/app/components/views/SettingsPage/TutorialsTab/helpers/tutorials.js +++ b/app/components/views/SettingsPage/TutorialsTab/helpers/tutorials.js @@ -33,7 +33,7 @@ tutorials.decredIntro = { tutorials.ln = { title: "What is Lightning Network?", - desc: "Learn how we've the basics of Lightning Network, our 2nd layer implementation for super fast transactions.", // TODO + desc: "Learn the basics of Lightning Network, our 2nd layer implementation for super fast transactions.", // TODO thumbnailImage: "lnThumb", slides: [ { @@ -42,7 +42,7 @@ tutorials.ln = { title: ( ), images: ["LNSlideImage1"] @@ -109,7 +109,7 @@ tutorials.ln = { tutorials.consensusCode = { title: "Consensus Code", - desc: "For most coins, consensus code does not allow for orderly upgrades. Decred's voting system allows for smooth upgrades that are easily understood by everyone.", // TODO + desc: "Most other coins don't upgrade consensus code in a fair or decentralized way. Decred's voting system allows for smooth upgrades that are easily understood.", // TODO thumbnailImage: "consensusCodeThumb", slides: [ { @@ -125,7 +125,7 @@ tutorials.consensusCode = { tutorials.powPos = { title: "Hybrid PoW/PoS", - desc: "With a hybrid system, we've taken the best of both PoW and PoS to obtain fair and secure block creation.", // TODO + desc: "With a hybrid system, we've taken the best features of both PoW and PoS to obtain fair and secure block creation.", // TODO thumbnailImage: "powPosThumb", slides: [ { @@ -156,7 +156,7 @@ tutorials.powPos = { tutorials.tickets = { title: "Staking and Tickets", - desc: "Typically staking can be complicated, with tickets and votes it's easy to understand and see how your hard-earned DCR is being put to use.", // TODO + desc: "Staking doesn't have to be complicated. Decred's system of tickets and votes makes it easy to understand how your hard-earned DCR can be put to use.", // TODO thumbnailImage: "ticketsThumb", slides: [ { @@ -187,7 +187,7 @@ tutorials.tickets = { tutorials.staking = { title: "Core Functions of Staking", - desc: "Learn about the core functions of staking and how your votes lead to real changes.", // TODO + desc: "Learn about the core functions of staking, and how your votes lead to real changes.", // TODO thumbnailImage: "stakingThumb", slides: [ { @@ -222,7 +222,7 @@ tutorials.staking = { tutorials.lifecycle = { title: "Ticket Lifecycle", - desc: "Learn all about how a ticket is created, consumed and the rewards for voting.", // TODO + desc: "Learn all about how a ticket is created, consumed, and the rewards for voting.", // TODO thumbnailImage: "lifecycleThumb", slides: [ { diff --git a/app/i18n/docs/en/Tutorials/PowPosPage03.md b/app/i18n/docs/en/Tutorials/PowPosPage03.md index cb5abc3a46..42aa583bbf 100644 --- a/app/i18n/docs/en/Tutorials/PowPosPage03.md +++ b/app/i18n/docs/en/Tutorials/PowPosPage03.md @@ -1 +1 @@ -To avoid these shortcomings each, Decred employs a hybrid model which allows for production of blocks by PoW, but the validation of those blocks are done so by the PoS. This allows us to have the best of both systems and create a system of checks and balances that should avoid problems of centralization that has been seen across the space for years. +To avoid each of these shortcomings, Decred employs a hybrid model which allows for production of blocks by PoW, but the validation of those blocks are done so by the PoS. This allows us to have the best of both systems and create a system of checks and balances that should avoid problems of centralization that has been seen across the space for years. From 8f8a663dbe770762a2bc3c7bea30e556cb2fe4c4 Mon Sep 17 00:00:00 2001 From: xaur <24484727+xaur@users.noreply.github.com> Date: Mon, 25 Sep 2023 14:29:03 +0000 Subject: [PATCH 11/15] Remove executable bit from assets (#3887) I used this script to find files with odd perms: find -ipath "*.git" -prune -false -o -type f \! -perm 644 Assets with mode 100755 introduced in: - a9ec524 2017-12-11 - a2822be 2018-08-28 - ffcbe05 2018-09-18 - ba68b23 2018-11-12 - bd63dd9 2019-08-08 - bfaa9e8 2019-09-12 - 783f9b7 2022-11-22 --- app/i18n/docs/el/DexNotes/DexNotes.md | 0 app/i18n/docs/el/GetStarted/TutorialPage01.md | 0 app/i18n/docs/el/GetStarted/TutorialPage02.md | 0 app/i18n/docs/el/GetStarted/TutorialPage03.md | 0 app/i18n/docs/el/GetStarted/TutorialPage04.md | 0 app/i18n/docs/el/InfoModals/BalanceOverview.md | 0 app/i18n/docs/el/InfoModals/DecredConstitution.md | 0 app/i18n/docs/el/InfoModals/GapLimit.md | 0 app/i18n/docs/el/InfoModals/Passphrase.md | 0 .../docs/el/InfoModals/PurchasePrivacyTickets.md | 0 app/i18n/docs/el/InfoModals/PurchaseTickets.md | 0 app/i18n/docs/el/InfoModals/ScriptNotRedeemable.md | 0 app/i18n/docs/el/InfoModals/Seed.md | 0 app/i18n/docs/el/InfoModals/SendFromUnmixed.md | 0 app/i18n/docs/el/InfoModals/SignMessage.md | 0 app/i18n/docs/el/InfoModals/TicketAutoBuyer.md | 0 app/i18n/docs/el/InfoModals/UIAnimations.md | 0 app/i18n/docs/el/InfoModals/VerifyMessage.md | 0 app/i18n/docs/el/LN/BackupInfo.md | 0 app/i18n/docs/el/ReleaseNotes/v1.3.0.md | 0 app/i18n/docs/el/ReleaseNotes/v1.3.1.md | 0 app/i18n/docs/el/ReleaseNotes/v1.4.0.md | 0 app/i18n/docs/el/ReleaseNotes/v1.5.0.md | 0 app/i18n/docs/el/ReleaseNotes/v1.6.0.md | 0 app/i18n/docs/el/StakingTutorial/Page01.md | 0 app/i18n/docs/el/StakingTutorial/Page02.md | 0 app/i18n/docs/el/TicketLifecycleTutorial/Page01.md | 0 app/i18n/docs/el/TicketLifecycleTutorial/Page02.md | 0 app/i18n/docs/el/TicketLifecycleTutorial/Page03.md | 0 app/i18n/docs/el/TicketLifecycleTutorial/Page04.md | 0 app/i18n/docs/el/TicketLifecycleTutorial/Page05.md | 0 app/i18n/docs/el/TicketLifecycleTutorial/Page06.md | 0 app/i18n/docs/el/Warnings/LNForceCloseChannel.md | 0 app/i18n/docs/el/Warnings/LNWalletCreation.md | 0 app/i18n/docs/el/Warnings/MixerIntroduction.md | 0 app/i18n/docs/el/Warnings/SeedCopy.md | 0 app/i18n/docs/el/Warnings/TrezorFirmwareUpdate.md | 0 .../el/Warnings/TrezorWalletCreationPassPhrase.md | 0 app/i18n/docs/el/Warnings/TrezorWipe.md | 0 app/i18n/docs/el/Warnings/WalletCreation.md | 0 app/i18n/docs/el/index.js | 0 app/i18n/translations/gr.json | 0 app/style/animations/blockChainLoading.json | 0 app/style/animations/daemonWaiting.json | 0 app/style/animations/discoveringAddresses.json | 0 app/style/animations/establishingRpc.json | 0 app/style/animations/fetchingHeaders.json | 0 app/style/animations/finalizingSetup.json | 0 app/style/animations/scanningBlocks.json | 0 app/style/icons/3D_Loading.gif | Bin app/style/icons/accounts-active.png | Bin app/style/icons/blockchainExplorerGreyDark.svg | 0 app/style/icons/constitutionGreyDark.svg | 0 app/style/icons/createnewwalletDark.svg | 0 app/style/icons/docsGreyDark.svg | 0 app/style/icons/forumGreyDark.svg | 0 app/style/icons/githubGreyDark.svg | 0 app/style/icons/governance-active.png | Bin app/style/icons/help-active.png | Bin app/style/icons/matrixGreyDark.svg | 0 app/style/icons/overview-active.png | Bin app/style/icons/pending_dots.gif | Bin app/style/icons/privacyCustomDark.svg | 0 app/style/icons/privacyDisabledDark.svg | 0 app/style/icons/privacyStandardDark.svg | 0 app/style/icons/sendMaxDefaultDark.svg | 0 app/style/icons/sendSelfDefaultDark.svg | 0 app/style/icons/stakepoolsGreyDark.svg | 0 app/style/icons/starGreyDark.svg | 0 .../icons/ticket_live_loop_decrediton_grey.gif | Bin app/style/icons/tickets-active.png | Bin app/style/icons/tickets-default.png | Bin app/style/icons/ticketsCogsOpenedDark.svg | 0 app/style/icons/transactions-active.png | Bin app/style/icons/transactions-default.png | Bin app/style/icons/walletBlueDark.svg | 0 app/style/icons/walletGrayDark.svg | 0 app/style/icons/xGreyDark.svg | 0 78 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 app/i18n/docs/el/DexNotes/DexNotes.md mode change 100755 => 100644 app/i18n/docs/el/GetStarted/TutorialPage01.md mode change 100755 => 100644 app/i18n/docs/el/GetStarted/TutorialPage02.md mode change 100755 => 100644 app/i18n/docs/el/GetStarted/TutorialPage03.md mode change 100755 => 100644 app/i18n/docs/el/GetStarted/TutorialPage04.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/BalanceOverview.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/DecredConstitution.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/GapLimit.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/Passphrase.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/PurchasePrivacyTickets.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/PurchaseTickets.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/ScriptNotRedeemable.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/Seed.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/SendFromUnmixed.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/SignMessage.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/TicketAutoBuyer.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/UIAnimations.md mode change 100755 => 100644 app/i18n/docs/el/InfoModals/VerifyMessage.md mode change 100755 => 100644 app/i18n/docs/el/LN/BackupInfo.md mode change 100755 => 100644 app/i18n/docs/el/ReleaseNotes/v1.3.0.md mode change 100755 => 100644 app/i18n/docs/el/ReleaseNotes/v1.3.1.md mode change 100755 => 100644 app/i18n/docs/el/ReleaseNotes/v1.4.0.md mode change 100755 => 100644 app/i18n/docs/el/ReleaseNotes/v1.5.0.md mode change 100755 => 100644 app/i18n/docs/el/ReleaseNotes/v1.6.0.md mode change 100755 => 100644 app/i18n/docs/el/StakingTutorial/Page01.md mode change 100755 => 100644 app/i18n/docs/el/StakingTutorial/Page02.md mode change 100755 => 100644 app/i18n/docs/el/TicketLifecycleTutorial/Page01.md mode change 100755 => 100644 app/i18n/docs/el/TicketLifecycleTutorial/Page02.md mode change 100755 => 100644 app/i18n/docs/el/TicketLifecycleTutorial/Page03.md mode change 100755 => 100644 app/i18n/docs/el/TicketLifecycleTutorial/Page04.md mode change 100755 => 100644 app/i18n/docs/el/TicketLifecycleTutorial/Page05.md mode change 100755 => 100644 app/i18n/docs/el/TicketLifecycleTutorial/Page06.md mode change 100755 => 100644 app/i18n/docs/el/Warnings/LNForceCloseChannel.md mode change 100755 => 100644 app/i18n/docs/el/Warnings/LNWalletCreation.md mode change 100755 => 100644 app/i18n/docs/el/Warnings/MixerIntroduction.md mode change 100755 => 100644 app/i18n/docs/el/Warnings/SeedCopy.md mode change 100755 => 100644 app/i18n/docs/el/Warnings/TrezorFirmwareUpdate.md mode change 100755 => 100644 app/i18n/docs/el/Warnings/TrezorWalletCreationPassPhrase.md mode change 100755 => 100644 app/i18n/docs/el/Warnings/TrezorWipe.md mode change 100755 => 100644 app/i18n/docs/el/Warnings/WalletCreation.md mode change 100755 => 100644 app/i18n/docs/el/index.js mode change 100755 => 100644 app/i18n/translations/gr.json mode change 100755 => 100644 app/style/animations/blockChainLoading.json mode change 100755 => 100644 app/style/animations/daemonWaiting.json mode change 100755 => 100644 app/style/animations/discoveringAddresses.json mode change 100755 => 100644 app/style/animations/establishingRpc.json mode change 100755 => 100644 app/style/animations/fetchingHeaders.json mode change 100755 => 100644 app/style/animations/finalizingSetup.json mode change 100755 => 100644 app/style/animations/scanningBlocks.json mode change 100755 => 100644 app/style/icons/3D_Loading.gif mode change 100755 => 100644 app/style/icons/accounts-active.png mode change 100755 => 100644 app/style/icons/blockchainExplorerGreyDark.svg mode change 100755 => 100644 app/style/icons/constitutionGreyDark.svg mode change 100755 => 100644 app/style/icons/createnewwalletDark.svg mode change 100755 => 100644 app/style/icons/docsGreyDark.svg mode change 100755 => 100644 app/style/icons/forumGreyDark.svg mode change 100755 => 100644 app/style/icons/githubGreyDark.svg mode change 100755 => 100644 app/style/icons/governance-active.png mode change 100755 => 100644 app/style/icons/help-active.png mode change 100755 => 100644 app/style/icons/matrixGreyDark.svg mode change 100755 => 100644 app/style/icons/overview-active.png mode change 100755 => 100644 app/style/icons/pending_dots.gif mode change 100755 => 100644 app/style/icons/privacyCustomDark.svg mode change 100755 => 100644 app/style/icons/privacyDisabledDark.svg mode change 100755 => 100644 app/style/icons/privacyStandardDark.svg mode change 100755 => 100644 app/style/icons/sendMaxDefaultDark.svg mode change 100755 => 100644 app/style/icons/sendSelfDefaultDark.svg mode change 100755 => 100644 app/style/icons/stakepoolsGreyDark.svg mode change 100755 => 100644 app/style/icons/starGreyDark.svg mode change 100755 => 100644 app/style/icons/ticket_live_loop_decrediton_grey.gif mode change 100755 => 100644 app/style/icons/tickets-active.png mode change 100755 => 100644 app/style/icons/tickets-default.png mode change 100755 => 100644 app/style/icons/ticketsCogsOpenedDark.svg mode change 100755 => 100644 app/style/icons/transactions-active.png mode change 100755 => 100644 app/style/icons/transactions-default.png mode change 100755 => 100644 app/style/icons/walletBlueDark.svg mode change 100755 => 100644 app/style/icons/walletGrayDark.svg mode change 100755 => 100644 app/style/icons/xGreyDark.svg diff --git a/app/i18n/docs/el/DexNotes/DexNotes.md b/app/i18n/docs/el/DexNotes/DexNotes.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/GetStarted/TutorialPage01.md b/app/i18n/docs/el/GetStarted/TutorialPage01.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/GetStarted/TutorialPage02.md b/app/i18n/docs/el/GetStarted/TutorialPage02.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/GetStarted/TutorialPage03.md b/app/i18n/docs/el/GetStarted/TutorialPage03.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/GetStarted/TutorialPage04.md b/app/i18n/docs/el/GetStarted/TutorialPage04.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/BalanceOverview.md b/app/i18n/docs/el/InfoModals/BalanceOverview.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/DecredConstitution.md b/app/i18n/docs/el/InfoModals/DecredConstitution.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/GapLimit.md b/app/i18n/docs/el/InfoModals/GapLimit.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/Passphrase.md b/app/i18n/docs/el/InfoModals/Passphrase.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/PurchasePrivacyTickets.md b/app/i18n/docs/el/InfoModals/PurchasePrivacyTickets.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/PurchaseTickets.md b/app/i18n/docs/el/InfoModals/PurchaseTickets.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/ScriptNotRedeemable.md b/app/i18n/docs/el/InfoModals/ScriptNotRedeemable.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/Seed.md b/app/i18n/docs/el/InfoModals/Seed.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/SendFromUnmixed.md b/app/i18n/docs/el/InfoModals/SendFromUnmixed.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/SignMessage.md b/app/i18n/docs/el/InfoModals/SignMessage.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/TicketAutoBuyer.md b/app/i18n/docs/el/InfoModals/TicketAutoBuyer.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/UIAnimations.md b/app/i18n/docs/el/InfoModals/UIAnimations.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/InfoModals/VerifyMessage.md b/app/i18n/docs/el/InfoModals/VerifyMessage.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/LN/BackupInfo.md b/app/i18n/docs/el/LN/BackupInfo.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/ReleaseNotes/v1.3.0.md b/app/i18n/docs/el/ReleaseNotes/v1.3.0.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/ReleaseNotes/v1.3.1.md b/app/i18n/docs/el/ReleaseNotes/v1.3.1.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/ReleaseNotes/v1.4.0.md b/app/i18n/docs/el/ReleaseNotes/v1.4.0.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/ReleaseNotes/v1.5.0.md b/app/i18n/docs/el/ReleaseNotes/v1.5.0.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/ReleaseNotes/v1.6.0.md b/app/i18n/docs/el/ReleaseNotes/v1.6.0.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/StakingTutorial/Page01.md b/app/i18n/docs/el/StakingTutorial/Page01.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/StakingTutorial/Page02.md b/app/i18n/docs/el/StakingTutorial/Page02.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/TicketLifecycleTutorial/Page01.md b/app/i18n/docs/el/TicketLifecycleTutorial/Page01.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/TicketLifecycleTutorial/Page02.md b/app/i18n/docs/el/TicketLifecycleTutorial/Page02.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/TicketLifecycleTutorial/Page03.md b/app/i18n/docs/el/TicketLifecycleTutorial/Page03.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/TicketLifecycleTutorial/Page04.md b/app/i18n/docs/el/TicketLifecycleTutorial/Page04.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/TicketLifecycleTutorial/Page05.md b/app/i18n/docs/el/TicketLifecycleTutorial/Page05.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/TicketLifecycleTutorial/Page06.md b/app/i18n/docs/el/TicketLifecycleTutorial/Page06.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/Warnings/LNForceCloseChannel.md b/app/i18n/docs/el/Warnings/LNForceCloseChannel.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/Warnings/LNWalletCreation.md b/app/i18n/docs/el/Warnings/LNWalletCreation.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/Warnings/MixerIntroduction.md b/app/i18n/docs/el/Warnings/MixerIntroduction.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/Warnings/SeedCopy.md b/app/i18n/docs/el/Warnings/SeedCopy.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/Warnings/TrezorFirmwareUpdate.md b/app/i18n/docs/el/Warnings/TrezorFirmwareUpdate.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/Warnings/TrezorWalletCreationPassPhrase.md b/app/i18n/docs/el/Warnings/TrezorWalletCreationPassPhrase.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/Warnings/TrezorWipe.md b/app/i18n/docs/el/Warnings/TrezorWipe.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/Warnings/WalletCreation.md b/app/i18n/docs/el/Warnings/WalletCreation.md old mode 100755 new mode 100644 diff --git a/app/i18n/docs/el/index.js b/app/i18n/docs/el/index.js old mode 100755 new mode 100644 diff --git a/app/i18n/translations/gr.json b/app/i18n/translations/gr.json old mode 100755 new mode 100644 diff --git a/app/style/animations/blockChainLoading.json b/app/style/animations/blockChainLoading.json old mode 100755 new mode 100644 diff --git a/app/style/animations/daemonWaiting.json b/app/style/animations/daemonWaiting.json old mode 100755 new mode 100644 diff --git a/app/style/animations/discoveringAddresses.json b/app/style/animations/discoveringAddresses.json old mode 100755 new mode 100644 diff --git a/app/style/animations/establishingRpc.json b/app/style/animations/establishingRpc.json old mode 100755 new mode 100644 diff --git a/app/style/animations/fetchingHeaders.json b/app/style/animations/fetchingHeaders.json old mode 100755 new mode 100644 diff --git a/app/style/animations/finalizingSetup.json b/app/style/animations/finalizingSetup.json old mode 100755 new mode 100644 diff --git a/app/style/animations/scanningBlocks.json b/app/style/animations/scanningBlocks.json old mode 100755 new mode 100644 diff --git a/app/style/icons/3D_Loading.gif b/app/style/icons/3D_Loading.gif old mode 100755 new mode 100644 diff --git a/app/style/icons/accounts-active.png b/app/style/icons/accounts-active.png old mode 100755 new mode 100644 diff --git a/app/style/icons/blockchainExplorerGreyDark.svg b/app/style/icons/blockchainExplorerGreyDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/constitutionGreyDark.svg b/app/style/icons/constitutionGreyDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/createnewwalletDark.svg b/app/style/icons/createnewwalletDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/docsGreyDark.svg b/app/style/icons/docsGreyDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/forumGreyDark.svg b/app/style/icons/forumGreyDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/githubGreyDark.svg b/app/style/icons/githubGreyDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/governance-active.png b/app/style/icons/governance-active.png old mode 100755 new mode 100644 diff --git a/app/style/icons/help-active.png b/app/style/icons/help-active.png old mode 100755 new mode 100644 diff --git a/app/style/icons/matrixGreyDark.svg b/app/style/icons/matrixGreyDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/overview-active.png b/app/style/icons/overview-active.png old mode 100755 new mode 100644 diff --git a/app/style/icons/pending_dots.gif b/app/style/icons/pending_dots.gif old mode 100755 new mode 100644 diff --git a/app/style/icons/privacyCustomDark.svg b/app/style/icons/privacyCustomDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/privacyDisabledDark.svg b/app/style/icons/privacyDisabledDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/privacyStandardDark.svg b/app/style/icons/privacyStandardDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/sendMaxDefaultDark.svg b/app/style/icons/sendMaxDefaultDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/sendSelfDefaultDark.svg b/app/style/icons/sendSelfDefaultDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/stakepoolsGreyDark.svg b/app/style/icons/stakepoolsGreyDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/starGreyDark.svg b/app/style/icons/starGreyDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/ticket_live_loop_decrediton_grey.gif b/app/style/icons/ticket_live_loop_decrediton_grey.gif old mode 100755 new mode 100644 diff --git a/app/style/icons/tickets-active.png b/app/style/icons/tickets-active.png old mode 100755 new mode 100644 diff --git a/app/style/icons/tickets-default.png b/app/style/icons/tickets-default.png old mode 100755 new mode 100644 diff --git a/app/style/icons/ticketsCogsOpenedDark.svg b/app/style/icons/ticketsCogsOpenedDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/transactions-active.png b/app/style/icons/transactions-active.png old mode 100755 new mode 100644 diff --git a/app/style/icons/transactions-default.png b/app/style/icons/transactions-default.png old mode 100755 new mode 100644 diff --git a/app/style/icons/walletBlueDark.svg b/app/style/icons/walletBlueDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/walletGrayDark.svg b/app/style/icons/walletGrayDark.svg old mode 100755 new mode 100644 diff --git a/app/style/icons/xGreyDark.svg b/app/style/icons/xGreyDark.svg old mode 100755 new mode 100644 From a6c322bb87159978ca9ef92e8746c285c70c6ec5 Mon Sep 17 00:00:00 2001 From: DominicTing <47611497+DominicTing@users.noreply.github.com> Date: Mon, 25 Sep 2023 22:29:43 +0800 Subject: [PATCH 12/15] [i18n] Update ZH translations (#3893) --- app/i18n/translations/zh.json | 741 +++++++++++++++++++++++++++++++++- 1 file changed, 720 insertions(+), 21 deletions(-) diff --git a/app/i18n/translations/zh.json b/app/i18n/translations/zh.json index b017a6872b..c3d62a8a04 100644 --- a/app/i18n/translations/zh.json +++ b/app/i18n/translations/zh.json @@ -19,6 +19,7 @@ "account.mixer.running.title": "混币工具正在运行", "account.pubKey": "扩展的公钥", "accountExtendedKey.failed": "获取账户扩展密钥时出错:{originalError}", + "accountMixer.insufficientUnmixedAccountBalance": "未混币账户余额不足", "accounts.addNewButton": "添加", "accounts.balances": "余额", "accounts.cancelRenameBtn": "取消", @@ -34,8 +35,12 @@ "accounts.hide.tip": "隐藏", "accounts.immatureRewards": "未成熟的奖励", "accounts.immatureStake": "生成未成熟票", + "accounts.immatureStakingRewards": "不成熟的质押奖励", "accounts.keys": "密钥", "accounts.keys.counts": "{external} 外部、 {internal}内部、 {imported} 已导出", + "accounts.lockFailed": "锁定帐户失败: {originalError}", + "accounts.lockStatus": "锁定状态", + "accounts.locked": "锁定", "accounts.lockedByTickets": "选票锁定", "accounts.name.default": "主账户", "accounts.newAccountConfirmations": "创建新账户", @@ -54,10 +59,14 @@ "accounts.subtitle": "账户列表", "accounts.title": "账户", "accounts.total": "总共", + "accounts.unconfirmed": "未确认", + "accounts.unlocked": "解锁", "accounts.votingAuthority": "投票权利", "accountsButton.tip": "账户", "accountsSelect.placeholder": "选择账户", + "addAccountModal.label": "新帐户名称", "addAccountModal.newAccountName": "新账户名称", + "addAccountModal.placeholder": "输入新的账户名称", "addCustomStakePool.failed": "尝试添加自定义选票矿池时出错: {originalError}", "addCustomStakePool.success": "已成功添加选票池 {host}。", "addMixerAccountModal.changeAccountName": "未混币帐户名称", @@ -76,11 +85,17 @@ "agenda.card.idLabel": "议程 ID", "agenda.card.inProgressIndicator": "进行中", "agenda.card.inProgressTooltip": "投票仍在进行中。", + "agenda.card.preference": "偏好: {selectedChoice}", "agenda.overview.idLabel": "议程 ID", "agenda.overviewDescription": "如果大多数 PoW 矿工已升级(最近100个区块中的75%是最新版本),大多数 PoS 选民也已经升级(在2016个区块间隔中,75%的选民已更新),则投票程序开始。", "agenda.updatePreference": "更新偏好", "agenda.votedFor": "正在投票", "agenda.votingFor": "正在投票", + "agendas.details.title": "治理", + "agendas.sortby.newest": "最新", + "agendas.sortby.oldest": "最早的", + "agendas.sortby.tooltip": "排序方式", + "agendas.updateVoteChoiceModal.title": "确认您的投票", "appMenu.about": "关于", "appMenu.aboutDecrediton": "关于 Decrediton", "appMenu.advanced": "高级设置", @@ -91,6 +106,7 @@ "appMenu.cut": "剪切", "appMenu.decrediton": "Decrediton", "appMenu.developerTools": "切换开发工具", + "appMenu.developerToolsConfWindow": "Toggle 开发人员工具确认窗口", "appMenu.documentation": "文档", "appMenu.edit": "编辑", "appMenu.file": "&文件", @@ -98,6 +114,7 @@ "appMenu.hideDecrediton": "隐藏Decrediton", "appMenu.hideOthers": "隐藏其它", "appMenu.learnMore": "了解更多", + "appMenu.loadCustomTranslation": "加载自定义翻译", "appMenu.minimize": "最小化", "appMenu.paste": "粘贴", "appMenu.quit": "退出", @@ -113,10 +130,21 @@ "appMenu.undo": "取消", "appMenu.view": "&查看", "appMenu.window": "窗口", + "appPassphrase.newPassphrase": "DEX 密码", + "appPassphrase.newPassphrasePlaceholder": "输入你的 DEX 密码", + "autoBuyerSettings.cancel": "取消", + "autoBuyerSettings.header": "自动购票", + "autoBuyerSettings.save": "保存", + "autoWalletLaunching.askMeLater": "稍后告诉我", + "autoWalletLaunching.checkboxDesc": "您稍后可以在“设置”中对此进行更改。", + "autoWalletLaunching.checkboxLabel": "加载完成后立即启动钱包", + "autoWalletLaunching.save": "保存", + "autoWalletLaunching.title": "自动启动钱包", "autobuyer.accountFrom": "来自", "autobuyer.balanceToMaintain": "保留余额", "autobuyer.balanceToMaintainError": "您要保留的余额无效", "autobuyer.disabled": "打开自动购票器", + "autobuyer.disabledText": "自动购票功能已禁用", "autobuyer.enabled": "关闭自动购票器", "autobuyer.modal.balanceToMaintain": "保留余额", "autobuyer.modal.stakepool": "VSP", @@ -127,9 +155,17 @@ "backup.redeem.script.modal.title": "备份兑换脚本", "backup.redeem.script.title": "脚本", "backup.redeem.script.warn": "你的资金可能会永远丢失。", + "blockchain.filterByNamePlaceholder": "按名称过滤", "blocksInput.blocks": "区块", + "buttons.listutxos": "列出 UTXOs", "changePassModal.confirm": "确认", + "changePassModal.confirmPassphrasePlaceholder": "确认您的私人密码", "changePassModal.newPassphrase": "新钱包密码", + "changePassModal.newPassphrasePlaceholder": "输入您的新私人密码", + "channelFilter.type.all": "全部", + "channelFilter.type.closed": "关闭", + "channelFilter.type.open": "打开", + "channelFilter.type.pending": "等待中", "charts.dayMonthDisplay": "{value, date, day-short-month}", "charts.fullDayDisplay": "{value, date}", "charts.keys.available": "可用", @@ -150,10 +186,22 @@ "charts.voteTime.noVotesDay": "没有在此范围内投票的选票", "clipboard.copied": "已复制", "confirmModal.btnCancel": "取消", + "confirmSeed.desc": "每个字段只包含一个正确的单词。请通过在每一行中选择正确的单词来确认您的种子。", "confirmSeed.errors.hexNot32Bytes": "错误:种子不是32字节,该种子来自一个不支持的软件,可能会产生未知的后果。", + "confirmSeed.errors.invalidHexSeed": "十六进制种子无效。十六进制种子的长度需要在 32 到 128 个字符之间。", "confirmSeed.label": "确认种子密钥", + "confirmSeed.title": "种子短语验证", "confirmSeed.warnings.pasteExistingError": "*请粘贴一个有效的33个单词的种子。", "confirmSeed.warnings.pasteExistingSeed": "*请确保您已在物理媒介上记录此种子。", + "confirmSeed.word.label": "字符 #{index}", + "createTrezorWallet.cancel": "取消", + "createTrezorWallet.createAWallet": "创建钱包", + "createTrezorWallet.createWallet": "创建钱包", + "createTrezorWallet.deviceSetup": "设备设置", + "createTrezorWallet.dupeWalletName.error": "请选择一个未使用的钱包名称", + "createTrezorWallet.title": "创建Trezor支持的Decrediton钱包", + "createTrezorWallet.walletNameInput.label": "钱包名称", + "createTrezorWallet.walletNameInput.placeholder": "为Trezor钱包选择一个名称", "createWallet.continueBtn": "继续", "createWallet.copy": "将种子单词复制到剪贴板", "createWallet.createWalletBtn": "创建钱包", @@ -163,6 +211,7 @@ "createWallet.passhraseInput.label": "私人密码", "createWallet.passphraseInput.errors.noMatch": "*密码不匹配", "createWallet.passphraseInput.errors.noPassPhrase": "*请输入您的钱包密码", + "createWallet.passphraseInput.match": "正确重复", "createWallet.passphraseInput.verifyLabel": "再次输入私人密码", "createWallet.passphrasePlaceholder": "私人密码", "createWallet.restore.title": "恢复现有钱包", @@ -171,20 +220,138 @@ "createWallet.seedCopiedToClipboard": "种子文件已复制到剪贴板", "createWallet.title": "创建新钱包", "createWallet.verifyPassphrasePlaceholder": "确认钱包密码", + "createwallet.advancedOptions.label": "高级选项", + "createwallet.disableCoinTypeUpgrades.label": "禁用硬币类型升级", + "createwallet.disablecointypeupgrades.description": "切勿从旧版密钥升级到 SLIP0044 型密钥", "createwallet.dupeWalletName.error": "请选择一个未使用过的钱包名", "createwallet.errors.walletRemoveFailed": "{originalError}", + "createwallet.gapLimit.label": "间隔限制", + "createwallet.gaplimit.description": "帐户允许未使用的地址间隙", "createwallet.isTrezor.label": "Trezor", + "createwallet.trezor.description": "Trezor 是一个硬件钱包。 欲了解更多信息,请访问 {link}", "createwallet.walletOnly.label": "仅观看", "createwallet.walletWatchOnly.error": "无效的主公钥", "createwallet.walletmasterpubkey.label": "主公钥", "createwallet.walletname.label": "钱包名称", "createwallet.walletname.placehlder": "选择名称", + "createwallet.walletname.tooltip": "该名称用于识别您的钱包 恢复钱包不需要将新名称与之前的钱包名称匹配。", "createwallet.walletpubkey.placeholder": "主公钥", + "createwallet.watchOnly.label": "仅观察", + "createwallet.watchonly.description": "仅观察钱包的功能有限。它只能用于查看余额和监控交易历史。您将无法转移此钱包中的DCR。", "daemon.connect.error": "连接到守护进程出错", "daemonSyncingTimeout.errors": "守护进程连接超时\n请检查提供的参数后再尝试", "decryptWalletForm.openBtn": "打开钱包", + "dex.btcWalletConnected": "BTC钱包已成功连接!", + "dex.btcWalletLocation.description": "如果您有一个非默认比特币位置,请选中该框并确定。", + "dex.btcWalletLocation.label": "你有一个非默认的比特币目录", + "dex.checkBTCConfig": "您必须确认您的 bitcoin.conf 已正确设置以连接到 DEX。 如果您尚未安装比特币钱包,请访问 bitcoin.org 获取更多说明。", + "dex.checkBTCConfig.failed": "无法识别现有的 BTC 配置文件: {originalError}", + "dex.checkBTCConfigButtonTryAgain": "重试", + "dex.checkBTCConfigInstalled": "如果您已经安装了 bitcoin.conf,但尚未创建 bitcoin.conf 文件,我们可以使用下面的按钮为您创建一个。", + "dex.checkInit.failed": "无法检查 DEX 是否已初始化: {originalError}", + "dex.confirmDexSeed.description": "请先确认您的DEX帐户种子,然后再继续。", + "dex.confirmDexSeed.title": "确认DEX帐户种子", + "dex.confirmDexSeedButton": "我已经复制了DEX帐户种子", + "dex.connectBTCWallet": "请输入您的 BTC 钱包的名称,然后尝试连接到钱包。", + "dex.connectBTCWallet.failed": "连接 BTC 钱包失败: {originalError}", + "dex.connectBTCWalletNote": "请注意:我们在默认位置找到了一个 bitcoin.conf,它将用于与您的 BTC 钱包进行通信。", + "dex.connectBTCWalletNote2": "在尝试连接之前,请确保您的 BTC 钱包当前正在运行。", + "dex.connectWallet.failed": "连接 DCR 钱包失败: {originalError}", + "dex.createBTCWallet": "连接比特币钱包", + "dex.createBTCWalletPassphrase": "BTC 密码(如果设置)", + "dex.createDCRWallet": "连接 DCR 钱包", + "dex.createDEXAccount.failed": "创建 DEX 账户失败: {originalError}", + "dex.createDexAccount": "创建 DEX 账户", + "dex.createDexAccount.description": "需要创建一个新帐户以提高钱包的整体安全性。", + "dex.createDexAccount.or": "或", + "dex.createDexAccount.selectAnExistingAccount": "选择现有帐户", + "dex.createDexAccount.title": "创建 DEX 账户", + "dex.createDexAccountButton": "创建 DEX 账户", + "dex.createWallet.description": "完成以下步骤,将您的 DCR 和 BTC 钱包连接到 DEX。", + "dex.createWallet.title": "将钱包连接到 Dex", + "dex.createWalletBTCPassphraseButton": "连接比特币钱包", + "dex.createWalletDCRPassphraseButton": "连接 DCR 钱包", + "dex.dcrWalletConnected": "DCR钱包已成功连接!", + "dex.dexServer": "DEX 服务器", + "dex.enable.failed": "启动 DEX 失败: {originalError}", + "dex.enableButton": "启动 DEX", + "dex.enableInformation": "欢迎来到 Decred 的去中心化交易所!单击以下按钮,您将在此钱包上启用 DEX。 出于安全考虑,我们建议使用单独的 DEX 钱包,然后将资金转移到您的普通钱包。", + "dex.enablePage.description": "您必须启用 DEX 功能才能使用它们。", + "dex.enablePage.title": "启用 DEX", + "dex.error.description": "DEX 没有运行", + "dex.error.page": "发生错误!DEX 没有运行。 如果问题仍然存在,请重新启动并检查日志。", + "dex.error.title": "DEX 错误", + "dex.export.seed.failed": "", + "dex.findBTCConfigButton": "查找bitcoin conf", + "dex.getConfig.failed": "获取 DEX 配置失败: {originalError}", + "dex.getFeeButton": "获取费用", + "dex.hasDexSeed.description": "DEX 种子允许您在特定服务器上恢复帐户。", + "dex.hasDexSeed.label": "我已经有一个 DEX 种子需要恢复。", + "dex.hide.seed": "点击隐藏DEX帐户种子", + "dex.init.failed": "设置 DEX 密码失败: {originalError}", + "dex.initPage.description": "您必须创建一个新密码,用于登录该钱包的 DEX。", + "dex.initPage.title": "设置 DEX 密码", + "dex.initPassphrase": "设置 DEX 密码", + "dex.initPassphraseButton": "设置 DEX 密码", + "dex.instructions.seed": "", + "dex.launchDexWIndow.description": "启动窗口以访问 DEX", + "dex.launchDexWindow": "启动 DEX 窗口", + "dex.launchDexWindow.title": "启动 Dex 窗口", + "dex.launchWindow.failed": "DEX 窗口启动失败: {originalError}", + "dex.login.failed": "DEX 登录失败: {originalError}", + "dex.loginDexPassphrase": "DEX 密码", + "dex.loginPage.description": "登录并连接钱包至 Dex", + "dex.loginPage.title": "DEX登录", + "dex.loginPassphrase": "输入 DEX 密码", + "dex.loginPassphraseButton": "登陆", + "dex.logout.failed": "无法从 DEX 注销: {originalError}", + "dex.logs": "日志", + "dex.neededFieldsInConfig": "bitcoin.conf rpcuser、rpcpassword、rpcbind、rpcport 中需要以下字段。 您还必须设置“server=1”来启动钱包监听连接。 如果您对这些说明有任何疑问,请前往 chat.decred.org 上的支持频道寻求进一步帮助。", + "dex.newAccount": "请创建一个新帐户或选择一个现有账户连接到 DEX 。 将资金转入和转出该账户,以便从 DEX 中存入/提取资金。", + "dex.newPassphrase": "请为 DEX 设置一个新的密码。 您可以使用与您的钱包相同的密码,或选择一个新密码。", + "dex.newPassphraseNote": "注意:如果您丢失了 DEX 密码,您将被迫创建一个新的 DEX 帐户并重新支付注册费。", + "dex.openorders.confirmModal.goback": "返回", + "dex.openorders.message": "目前 DEX 仍在管理未结订单。 请等待所有订单执行完毕后再关闭。 如果您在订单执行前关闭,您将无法完成交易并可能受到处罚。", + "dex.openorders.title": "DEX 未结订单", + "dex.paDexFeeModalNote2": "所有交易都是手数的倍数。 手数是 DCR 中最小交易金额。", + "dex.payDexFeeAppPassphrase": "DEX 密码", + "dex.payDexFeeButton": "注册", + "dex.payDexFeeModalDescription": "输入您的 DEX 密码支付 {fee} 的注册费,以便在 {address} 的 DEX 注册。", + "dex.payDexFeeModalNote": "注意:该 DEX 的 DCR 手数为:", + "dex.payDexFeeModalTitle": "确认注册", + "dex.payRegistration.Address": "DEX 服务器注册到:", + "dex.payRegistration.Fee": "请输入您的 DEX 密码以支付以下费用:", + "dex.preRegisterButton": "检查注册", + "dex.preRegisterModalDescription": "由于您已经从种子恢复了您的 DEX 帐户,现在可以检查您是否已经在 DEX 服务器上支付了费用。", + "dex.preRegisterModalTitle": "确认注册检查", + "dex.register.failed": "支付 DEX 费用失败: {originalError}", + "dex.registerPage.description": "在 DEX 服务器上注册您的钱包", + "dex.registerPage.title": "DEX 服务器支付", + "dex.resetWalletRequired": "需要重置钱包", + "dex.resetWalletRequiredContent": "必须重启钱包才能与 DEX 客户端通信。 重新加载后返回 DEX 页面,您可以继续操作。", + "dex.seed": "DEX帐户种子", + "dex.seed.Hidden": "点击显示DEX种子", + "dex.sendToDexAccount.title": "发送到 Dex 账户", + "dex.startup.failed": "DEX 客户端无法启动: {originalError}", + "dex.subtitle.dcrWallet": "DCR钱包", + "dex.updateBTCConfig": "您必须更新您的 bitcoin.conf 才能与 DEX 正确通信。", + "dex.updateBTCConfigButton": "创建 BTC 配置", + "dex.user.failed": "获取用户信息失败: {originalError}", + "dexPassModal.confirm": "DEX应用密码", + "dexPassModal.dexAppPasswordDesc": "提供DEX应用密码也会自动传播到dexc", + "dexPassModal.dexPasswordPlaceholder": "输入您的DEX应用密码", + "dialogs.cancelButton": "取消", + "dialogs.confirmFileOverwrite": "确定覆盖文件 {filename}?", + "dialogs.yesButton": "是的", + "discoverAddress.failed": "{原始错误}", + "discoverAddress.success": "您已成功发现地址使用情况。 现在开始重新扫描。", + "discoverUsage.cancel": "取消", + "discoverUsage.gapLimitLbl": "间隔限制", + "discoverUsage.save": "发现地址使用情况", + "discoverUsage.startErr": "填写所有字段。", "docs.unavailable": "文档‘{name}’在当前语言中不可用。请向社区频道申请。", "error.empty.fields": "填充所有字段", + "error.empty.fields.app.pass": "填写所有字段。", "error.not.same.pass": "密码不匹配", "errors.general": "发生错误,请返回", "errors.goHome": "返回主页", @@ -217,7 +384,10 @@ "export.dailyBalances.fields.voted": "已投票总DCR数额", "export.dailyBalances.name": "每日余额", "export.destination": "目标", + "export.destination.label": "目的地", + "export.destination.placeholder": "选择目的地...", "export.infoFieldsHeader": "导出的字段", + "export.modalTitle": "导出交易", "export.select": "导出类型", "export.subtitle": "导出交易", "export.tickets.descr": "导出选票和投票信息。", @@ -265,6 +435,9 @@ "getStarted.chainLoading.headerTime": "最后获取的区块头显示时间为:", "getStarted.chainLoading.syncEstimation": "区块链预计同步时间:", "getStarted.chainLoading.syncEstimation.small": "正在加载Decred区块数据,预计完成时间", + "getStarted.closeEditWallets": "关闭", + "getStarted.confirmSeed.enterAllWords": "*请输入所有单词", + "getStarted.confirmSeed.wrongWordError": "输入种子中的是不正确的。请正确输入以继续。", "getStarted.decrypt.error": "键入了错误的公共密码短语。", "getStarted.decrypt.info": "这个钱包已加密,请输入公共密码来解密它。", "getStarted.decrypt.label": "解密钱包", @@ -275,6 +448,7 @@ "getStarted.discoverAccountsInfo": "\n输入您刚才创建的密码以扫描区块链,查找您以前用钱包创建的其他账户。\n\n您的账户名称没有存储在区块链上,所以您必须在设置 Decrediton 后重新命名它们。", "getStarted.discoverAddresses.passphrasePlaceholder": "私人密码", "getStarted.discoverAddresses.scanBtn": "扫描", + "getStarted.editWallets": "编辑钱包", "getStarted.header.discoveringAddresses.meta": "正在探索地址", "getStarted.header.fetchingBlockHeaders.meta": "正在获取区块头", "getStarted.header.fetchingMissing.meta": "获取丢失的提交过滤器", @@ -283,15 +457,28 @@ "getStarted.learnBasics": "学习基础知识", "getStarted.newSeedTab": "创建新钱包", "getStarted.passphrase.info": "钱包账户处于隐藏状态。请输入私人密码执行帐户发现。", + "getStarted.releaseInfo": "版本信息", "getStarted.releaseNotes.NewerVersion": "新版本", "getStarted.releaseNotes.OlderVersion": "旧版本", "getStarted.releaseNotesTitle": "Decrediton v{version} 已发布", "getStarted.restore": "恢复现有钱包", "getStarted.settings.save": "保存", + "getStarted.startwalletservice": "启动钱包服务", + "getStarted.trezor": "设置 Trezor 钱包", "getStarted.updateAvailable": "有可用更新", "getStarted.updateAvailableTooltip": "新版本{version}可用", "getStarted.walletRescan.progress": "重新扫描进度({rescanCurrentBlock} / {rescanEndBlock})", "getStarted.whatsNew": "v{version} 中有何更新", + "getstarted.cancelLoading": "取消加载\n", + "getstarted.loadingpage.learnAboutDecred": "学习了解 decred", + "getstarted.openWallet": "打开钱包\n", + "getstarted.processManagedTickets.description": "您的钱包似乎有现场选票。 请与 VSP 确认您的所有选票当前都是由已知的 VSPs 支付。 如果您已经确认您的选票,那么您可以跳过此步骤。", + "getstarted.processManagedTickets.title": "处理托管选票", + "getstarted.processUnmangedTickets.description": "看起来您有未处理费用的 vsp 选票。 如果选票被选中且没有与 vsp 链接,选票可能会被错过。", + "getstarted.processUnmangedTickets.title": "处理非托管选票", + "getstarted.resendVotesToRecentlyUpdatedVSPs.title": "向最近更新的 VSP 重新发送投票\n", + "getstarted.resendVotesToRecentlyUpdatedVSPs.votesHeader": "", + "getstarted.resendVotesToRecentlyUpdatedVSPs.vspsHeader": "", "getstarted.setAccount.acctIdxRow": "帐户 {acctIdx}", "getstarted.setAccount.change": "设置未混币帐户", "getstarted.setAccount.continue": "继续", @@ -300,12 +487,21 @@ "getstarted.setAccount.mix": "设置混币帐户", "getstarted.setAccount.sumCoinjoin": "混币合计输出:{coinjoinSum}", "getstarted.setAccount.title": "设置混币帐户", + "getstarted.setAccountsPass.description": "Decrediton 现在锁定所有帐户,这需要一次性迁移。 输入您当前的密码以执行此升级。 访问 {link} 了解更多信息。", + "getstarted.setAccountsPass.docs": "Decred 文档", + "getstarted.setAccountsPass.title": "迁移到所有帐户的密码", "getstarted.setAcct.changAcct": "未混币帐户: {value}", "getstarted.setAcct.mixedAcct": "混币帐户:{value}", + "getstarted.settings.tab.connectivity": "连接", + "getstarted.settings.tab.general": "通用", + "getstarted.settings.tab.privacyandsecurity": "隐私和安全", + "getstarted.spvMode": "SPV 模式", + "getstarted.tutorials.learnAboutDecred": "学习了解 decred", "governance.description": "Decred治理方面。", "governance.ntf.updateVoteChoiceSuccess": "你的投票已完成!\n感谢你参与Decred的治理", "governance.tab.consensusChanges": "共识更改", "governance.tab.proposals": "提案", + "governance.tab.treasurySpending": "国库支出", "governance.title": "治理", "governance.treasury_balance": "国库余额:{treasuryBalance}", "heatmap.month": "{date, date, short-month}", @@ -320,7 +516,7 @@ "help.blockchain": "区块浏览器", "help.blockchain.subtitle": "dcrdata.decred.org", "help.constitution": "宪法", - "help.constitution.subtitle": "Decred 项目宪法", + "help.constitution.subtitle": "Decred 宪法", "help.description.links": "", "help.description.logs": "", "help.description.tutorials": "", @@ -353,10 +549,17 @@ "history.ticket.rewardLabel": "选票奖励", "history.ticket.rewardMesage": "{rewardLabel}: {reward}", "home.activeTicketsCount": "{count, plural, one {{fmtCount} active ticket} other {{fmtCount} active tickets}}", - "home.currentTotalBalanceLabel": "当前总余额", + "home.currentTotalBalanceLabel": "总余额", "home.currentTotalLockedBalanceLabel": "已锁定", "home.currentTotalSpendableBalanceLabel": "可用", + "home.currentTotalUnconfirmedBalanceLabel": "未确认", "home.earned": "赢得{value}投票奖励", + "home.immatureRewardBalanceLabel": "未成熟的奖励", + "home.immatureStakingRewardsBalanceLabel": "未成熟的质押奖励", + "home.lockedByTicketsTotalBalanceLabel": "被选票锁定", + "home.newNotYetVotedActiveProposalsCount": "", + "home.newNotYetVotedAgendasCount": "", + "home.newNotYetVotedTSpendCount": "", "home.noTickets.lifecycle": "了解选票的生命周期", "home.noTickets.staking": "什么是POS(权益证明)?", "home.noTickets.title": "还没有选票", @@ -382,27 +585,79 @@ "infoModal.btnClose": "明白", "infoModal.btnConfirm": "确认", "input.invalidInput": "字段错误", - "input.requiredInput": "需要字段", + "input.requiredInput": "输入金额", "invalidRPCVersion.info": "当前运行的钱包({walletRPCVersion})的API与Decrediton(必需的版本{requiredWalletRPCVersion})不兼容。\n\n请将守护程序(dcrd)和钱包(dcrwallet)更新到最新版本,然后重试。", + "invoiceFilter.type.all": "所有", + "invoiceFilter.type.canceled": "取消", + "invoiceFilter.type.expired": "已到期", + "invoiceFilter.type.open": "尚未付款", + "invoiceFilter.type.settled": "已收到", + "launcherSettings.checkboxDesc": "自动打开钱包\n", + "launcherSettings.checkboxLabel": "加载完成后立即启动钱包\n", + "listutxo.header.utxo": "UTXO", + "listutxo.header.value": "价值", + "listutxos.listUnspentUTXOs": "列出未使用的 UTXOs", + "ln.LNChannelStatus.closed": "关闭", + "ln.LNChannelStatus.open": "打开", + "ln.LNChannelStatus.pending": "待处理", + "ln.LNInvoiceStatus.canceled": "取消", + "ln.LNInvoiceStatus.expired": "已到期", + "ln.LNInvoiceStatus.notPaidYet": "尚未付款", + "ln.LNInvoiceStatus.received": "已收到", + "ln.LNPaymentStatus.confirmed": "已确认", + "ln.LNPaymentStatus.failed": "失败的", + "ln.LNPaymentStatus.pending": "待处理", + "ln.advancedTab.backup": "备份", + "ln.advancedTab.description.header": "闪电网络钱包的链上余额和操作", + "ln.advancedTab.infos": "信息", + "ln.advancedTab.network": "网络", + "ln.advancedTab.nodeInfos.ID": "节点 ID", + "ln.advancedTab.nodeInfos.alias": "节点昵称", "ln.backup.backupBtn": "现在备份", "ln.backup.verifyBtn": "验证备份", "ln.backupInfo.lastUpdated": "最近更新: {lastUpdate, date} {lastUpdate, time, short}", "ln.backupInfo.location": "SCB备份文件位置:{path}", + "ln.channelDetails.closeChannelButton": "关闭通道", + "ln.channelDetails.closeChannelModalTitle": "关闭通道", + "ln.channelDetails.props": "特性", + "ln.channelModal.closeChannelButton": "关闭通道", + "ln.channelModal.closeChannelModalTitle": "关闭通道", + "ln.channelModal.props": "特性", + "ln.channelModal.title": "通道已创建", + "ln.channeldetails.description.channels": "打开及待处理闪电网络钱包通道", + "ln.channeldetails.title": "闪电网络", + "ln.channels.description.channels": "打开及待处理闪电网络钱包通道", "ln.channelsTab.balance": "余额", "ln.channelsTab.balance.channelsCapacity": "总通道容量", "ln.channelsTab.balance.onChain": "确认链上余额", + "ln.channelsTab.channelCard.capacity": "容量", "ln.channelsTab.channelList": "打开通道列表", + "ln.channelsTab.channelTypes.tooltip": "通道类型", "ln.channelsTab.closeChannelBtn": "关闭此通道", "ln.channelsTab.closeChannelModalTitle": "关闭通道", "ln.channelsTab.closedChannel.settledBalance": "已完成", "ln.channelsTab.closedChannel.timeLockedBalance": "时间锁", "ln.channelsTab.closedList": "已关闭通道", + "ln.channelsTab.counterpartyNode": "交易对手节点", + "ln.channelsTab.counterpartyNodePlaceholder": "节点公钥@ip:port", + "ln.channelsTab.createAChannel": "创建通道", + "ln.channelsTab.emptyChannelList": "未发现通道", + "ln.channelsTab.filterByChannelPointPlaceholder": "按通道点过滤", + "ln.channelsTab.invalidNodeAddressFormat": "多于一个:节点地址", + "ln.channelsTab.invalidNodeFormat": "节点中有多个@地址", + "ln.channelsTab.invalidNodeId": "无效的节点 ID", + "ln.channelsTab.localAmt": "承诺金额", + "ln.channelsTab.localAmtPlaceholder": "提交给通道的 DCR 数量", + "ln.channelsTab.manageChannels": "管理通道", + "ln.channelsTab.nodeSuccessMsg": "有效的公钥", "ln.channelsTab.openChannel": "打开通道", "ln.channelsTab.openChannel.localBalance": "本地", "ln.channelsTab.openChannel.remoteBalance": "远程", "ln.channelsTab.pendingChannel.localBalance": "本地", "ln.channelsTab.pendingChannel.remoteBalance": "远程", "ln.channelsTab.pendingList": "待处理通道", + "ln.channelsTab.pushAmount": "推送金额(可选)", + "ln.channelsTab.pushAmountPlaceholder": "推送到通道的 DCR 数量", "ln.closeChannelModa.descrForce": "是否尝试强制关闭通道?", "ln.closeChannelModal.capacity": "容量", "ln.closeChannelModal.channelPoint": "通道点", @@ -423,25 +678,67 @@ "ln.confirmBackupOverwrite.content": "确定要覆盖备份文件{file}?现有备份数据将丢失。", "ln.confirmBackupOverwrite.title": "确认覆盖备份", "ln.connectPage.account": "要使用的钱包帐户", + "ln.connectPage.automaticChannelCreation": "自动创建通道", + "ln.connectPage.autopilot.enabled": "禁用自动创建通道", + "ln.connectPage.autopilot.not.enabled": "启用自动创建通道", "ln.connectPage.backupFile": "还原SCB备份", + "ln.connectPage.backupFilePlaceholder": "选择路径...", "ln.connectPage.createAccount": "创建新的", "ln.connectPage.description": "启动、解锁并连接到闪电网络钱包。", + "ln.connectPage.disabled.unlockWalletModal": "混币器、自动购票 或 购票尝试 正在运行,请在解锁闪电网络钱包之前将其关闭。", "ln.connectPage.enableAutopilot": "自动开启通道", "ln.connectPage.enableAutopilotDescr": "启用了“自动”功能,此功能将尝试使用账户可使用资金的60%自动打开通道。", "ln.connectPage.launchBtn": "启动并打开闪电网络钱包", + "ln.connectPage.running": "解锁闪电网络钱包", "ln.connectPage.title": "启动闪电网络钱包", "ln.connectPage.unlockWalletModal": "打开闪电网络钱包", "ln.connectPage.useAccount": "使用现有的", "ln.connectPage.useExistingAccountWarning": "注意:正在运行的闪电网络钱包会\n会将未加密的密钥保存在内存中,并控制帐户内的所有资金\n建议使用专用的闪电网络帐户操作\n,建议仅将您打算长期使用闪电网络的资金转进去。", + "ln.connectPage.useExistingAccountWarningAttention": "注意:", "ln.createLNWalletPage.description": "创建一个由Decrediton支持的新闪电网络钱包。", "ln.createLNWalletPage.title": "创建闪电网络钱包", "ln.createWalletWarning.okBtn": "我理解并接受风险", + "ln.creationWarning.desc": "注意,闪电网络仍在开发中,请谨慎使用。", + "ln.creationWarning.title": "在您继续前", + "ln.decodedPayRequestDetails.cltvExpiry": "CLTV 到期", + "ln.decodedPayRequestDetails.emptyFallbackAddr": "(空的备用地址)", + "ln.decodedPayRequestDetails.fallbackAddr": "备用地址", + "ln.decodedPayRequestDetails.paymentAddr": "付款地址", "ln.description.channels": "当前闪电网络钱包的打开及待定通道", "ln.description.invoices": "当前闪电网络钱包创建的发票(付款请求)。", "ln.description.network": "Decred闪电网络的当前状态和一般信息。", "ln.description.payments": "从当前闪电网络钱包付款", + "ln.description.receive": "生成闪电发票以通过闪电网络接收 DCR 资金。", + "ln.description.send": "粘贴支付请求代码以通过闪电网络发送资金。", "ln.description.wallet": "链上余额及闪电网络钱包详情", "ln.description.watchtowers": "管理瞭望塔的连接。", + "ln.header.balances": "您可以发送总计 {maxOutboundAmount} 和接收总计 {maxInboundAmount}", + "ln.invoice.sortby.newest": "最新", + "ln.invoice.sortby.oldest": "最早", + "ln.invoiceModal.date": "日期", + "ln.invoiceModal.expirationTime": "到期时间", + "ln.invoiceModal.requestedAmount": "要求的金额", + "ln.invoiceModal.settleDateLabel": "结算日期", + "ln.invoiceModal.status": "状态", + "ln.invoiceModal.title": "闪电支付请求", + "ln.invoicesModal.cancelInvoice": "取消发票", + "ln.invoicesModal.creationDate": "{creationDate, date, medium} {creationDate, time, short}", + "ln.invoicesModal.desc": "描述", + "ln.invoicesModal.details": "细节", + "ln.invoicesModal.expired": "已到期", + "ln.invoicesModal.expires": "过期", + "ln.invoicesModal.hash": "哈希", + "ln.invoicesModal.htlc": "HTLC", + "ln.invoicesModal.htlc.acceptHeight": "接受高度", + "ln.invoicesModal.htlc.acceptTime": "{acceptTime, date, medium} {acceptTime, time, short}", + "ln.invoicesModal.htlc.acceptTimeLabel": "接受时间", + "ln.invoicesModal.htlc.chanId": "通道 id", + "ln.invoicesModal.htlc.expiryHeight": "到期高度", + "ln.invoicesModal.htlc.resolveTime": "{resolveTime, date, medium} {resolveTime, time, short}", + "ln.invoicesModal.htlc.resolveTimeLabel": "处理时间", + "ln.invoicesModal.htlc.state": "状态", + "ln.invoicesModal.requestCodeLabel": "闪电支付请求代码(将此发送给付款人)", + "ln.invoicesModal.settleDate": "{settleDate, date, medium} {settleDate, time, short}", "ln.invoicesTab.addInvoice.memo": "描述", "ln.invoicesTab.addInvoice.value": "金额", "ln.invoicesTab.addInvoiceHeader": "添加发票", @@ -449,6 +746,7 @@ "ln.invoicesTab.balanceHeader": "余额", "ln.invoicesTab.invoice.creationDate": "{creationDate, date, medium} {creationDate, time, short}", "ln.invoicesTab.invoice.settleDate": "{settleDate, date, medium} {settleDate, time, short}", + "ln.invoicesTab.invoice.value": "{balance} 的发票", "ln.invoicesTab.invoicesHeader": "最新发票", "ln.networkTab.numChannels": "通道列表", "ln.networkTab.numNodes": "节点", @@ -481,8 +779,14 @@ "ln.nodeInfo.pubkey": "公钥", "ln.nodeInfo.totalCapacity": "总容量", "ln.nodeInfo.updateDate": "{lastUpdate, date, medium} {lastUpdate, time, medium}", + "ln.ntf.addInvoiceFailed": "创建发票失败: {originalError}", + "ln.ntf.addInvoiceSuccess": "已成功添加发票!", "ln.ntf.addWatchtowerFailed": "添加瞭望塔失败:{originalError}", "ln.ntf.addWatchtowerSuccess": "成功添加瞭望台!", + "ln.ntf.autopilotStatusModifyFailed": "修改自动运行状态失败: {originalError}", + "ln.ntf.autopilotStatusModifySuccess": "自动运行修改成功!", + "ln.ntf.cancelInvoiceFailed": "无法取消发票: {originalError}", + "ln.ntf.cancelInvoiceSuccess": "发票成功取消!", "ln.ntf.closechannelChanclose": "通道已关闭", "ln.ntf.closechannelClosepending": "通道关闭程序已启动", "ln.ntf.closechannelFailed": "无法关闭通道:{originalerror}", @@ -511,10 +815,14 @@ "ln.ntf.verifyBackupSuccess": "SCB备份文件对此钱包有效!", "ln.ntf.withdrawWalletFailed": "从闪电网络钱包提取失败:{originalerror}", "ln.ntf.withdrawWalletSuccess": "已发送闪电网络钱包提取交易", + "ln.openChannel.connectTo": "连接到交易对手节点并创建通道,开始使用闪电网络。", + "ln.openChannel.createChannelBt": "创建通道", + "ln.openChannel.emptyRecentNodes": "未发现节点", "ln.openChannel.localAmt": "总额", "ln.openChannel.node": "交易对方(node@ip:port)", "ln.openChannel.openBtn": "打开", "ln.openChannel.pushAmt": "推送金额(可选)", + "ln.openChannel.recentNodes": "最近的节点", "ln.openChannelDetails.chanId": "通道ID", "ln.openChannelDetails.channelPoint": "通道点", "ln.openChannelDetails.commitFee": "提交费用", @@ -527,21 +835,74 @@ "ln.openChannelDetails.totalReceived": "总接收", "ln.openChannelDetails.totalSent": "总发送", "ln.openChannelDetails.unsettledBalance": "未结算余额", + "ln.overview.description": "使用闪电网络的链下交易。", + "ln.overviewTab.LNAccountOverview": "闪电网络帐户概览", + "ln.overviewTab.accountOverview.capacity": "容量", + "ln.overviewTab.accountOverview.confirmedBalance": "确认余额", + "ln.overviewTab.accountOverview.openChannels": "打开通道", + "ln.overviewTab.accountOverview.totalAccountBalance": "账户总余额", + "ln.overviewTab.accountOverview.unconfirmedBalance": "未确认余额", + "ln.overviewTab.channelModal.closeChannelButton": "关闭通道", + "ln.overviewTab.channelModal.closeChannelModalTitle": "关闭通道", + "ln.overviewTab.channelModal.props": "属性", + "ln.overviewTab.emptyInvoiceList": "没有活动", + "ln.overviewTab.networkStats": "网络统计", + "ln.overviewTab.networkStats.capacity": "容量", + "ln.overviewTab.networkStats.channels": "通道", + "ln.overviewTab.networkStats.nodes": "节点", + "ln.overviewTab.recentActivity": "近期活动", + "ln.overviewTab.recentChannel.closed": "通道关闭 {balance}", + "ln.overviewTab.recentChannel.ts": "{ts, date, medium} {ts, time, short}", + "ln.overviewTab.recentChannel.value": "通道资金 {balance}", + "ln.paymentModal.creationDate": "{creationDate, date, medium} {creationDate, time, short}", + "ln.paymentModal.date": "日期", + "ln.paymentModal.desc": "描述", + "ln.paymentModal.destination": "目的地", + "ln.paymentModal.details": "细节", + "ln.paymentModal.fee": "费用", + "ln.paymentModal.hash": "哈希", + "ln.paymentModal.htlc": "HTLC", + "ln.paymentModal.htlc.hop.fee": "费用", + "ln.paymentModal.htlc.hop.pubkey": "公钥", + "ln.paymentModal.htlc.status": "状态", + "ln.paymentModal.htlc.totalAmt": "总金额", + "ln.paymentModal.htlc.totalFees": "总费用", + "ln.paymentModal.requestCodeLabel": "闪电支付请求代码", + "ln.paymentModal.routeHop": "跳转", + "ln.paymentModal.sentAmount": "发送金额", + "ln.paymentModal.status": "状态", + "ln.paymentModal.title": "闪电支付", + "ln.payments.sortby.newest": "最新", + "ln.payments.sortby.oldest": "最早", + "ln.paymentsTab.amountLabel": "数量", "ln.paymentsTab.balance.maxPayable": "最高应付金额", "ln.paymentsTab.balanceHeader": "余额", + "ln.paymentsTab.decodedPayreq.details": "细节", + "ln.paymentsTab.descLabel": "描述", "ln.paymentsTab.destLabel": "描述", "ln.paymentsTab.emptyDescr": "(无说明)", + "ln.paymentsTab.emptyPaymentList": "未找到付款", "ln.paymentsTab.expired": "过期 {relTime}", + "ln.paymentsTab.expiredErrorMsg": "发票已过期", "ln.paymentsTab.expires": "过期 {relTime}", + "ln.paymentsTab.expiryLabel": "到期时间", "ln.paymentsTab.failed.creationDate": "{creationDate, date, medium} {creationDate, time, short}", + "ln.paymentsTab.filterByHashPlaceholder": "按付款哈希过滤", "ln.paymentsTab.hashLabel": "付款哈希", "ln.paymentsTab.latestPayments": "最新付款", + "ln.paymentsTab.lightingPayments": "闪电支付", "ln.paymentsTab.outstanding": "持续付款", "ln.paymentsTab.outstanding.creationDate": "{creationDate, date, medium} {creationDate, time, short}", "ln.paymentsTab.payReq": "付款请求", + "ln.paymentsTab.payReqDecodeSuccessMsg": "有效的闪电请求", + "ln.paymentsTab.payReqPlaceholder": "从发票请求代码", "ln.paymentsTab.payment.creationDate": "{creationDate, date, medium} {creationDate, time, short}", + "ln.paymentsTab.paymentHashLabel": "支付哈希", + "ln.paymentsTab.paymentTypes.tooltip": "支付方式", + "ln.paymentsTab.send": "发送", "ln.paymentsTab.sendBtn": "发送", "ln.paymentsTab.sendPayment": "发送付款", + "ln.paymentsTab.sortby.tooltip": "排序方式", "ln.paymentsTag.failed": "失败的交易", "ln.pendingChannelDetails.channelPoint": "通道节点", "ln.pendingChannelDetails.remotePubKey": "远程公钥", @@ -554,6 +915,18 @@ "ln.pendingForceCloseDetails.limboBalance": "Limbo Balance", "ln.pendingForceCloseDetails.recoveredBalance": "已恢复余额", "ln.pendingOpenDetails.commitFee": "提交费用", + "ln.receiveTab.capacityError": "超过总接收容量不能请求", + "ln.receiveTab.createInvoice": "创建发票", + "ln.receiveTab.descriptionLabel": "描述", + "ln.receiveTab.descriptionPlaceholder": "接收人留言", + "ln.receiveTab.emptyInvoiceList": "未找到发票", + "ln.receiveTab.filterByHashPlaceholder": "按付款哈希过滤", + "ln.receiveTab.invoiceTypes.tooltip": "发票类型", + "ln.receiveTab.lightingInvoices": "闪电发票", + "ln.receiveTab.receiveHeader": "收到", + "ln.receiveTab.requestedAmountLabel": "要求的金额", + "ln.receiveTab.requestedAmountPlaceholder": "0.00000000", + "ln.receiveTab.sortby.tooltip": "排序方式", "ln.routesInfo.amount": "金额", "ln.routesInfo.hop": "跳", "ln.routesInfo.hopFee": "费用", @@ -563,15 +936,28 @@ "ln.routesInfo.routeAmount": "总金额", "ln.routesInfo.routeFees": "总费用", "ln.routesInfo.routesList": "路线", + "ln.searchForNodesModal.emptySearchResult": "未找到匹配节点", + "ln.searchForNodesModal.paste": "粘贴节点公钥@ip:port", + "ln.searchForNodesModal.recentNodes": "最近的节点", + "ln.searchForNodesModal.searchInputLabel": "搜索网络或粘贴公钥", + "ln.searchForNodesModal.searchInputPlaceholder": "按名称搜索网络或粘贴公钥", + "ln.searchForNodesModal.searchResults": "搜索结果 ({count})", + "ln.searchForNodesModal.title": "搜索节点", + "ln.sendTab.invoice.creationDate": "{creationDate, date, medium} {creationDate, time, short}", + "ln.sendTab.payment.value": "已付款 {balance}", "ln.startupStage.connect": "连接到闪电网络守护进程dcrlnd", "ln.startupStage.scbRestore": "恢复备份", "ln.startupStage.startDcrlnd": "正在启动闪电网络dcrlnd", "ln.startupStage.startupSync": "将闪电网络钱包同步到网络", "ln.startupStage.unlock": "解锁闪电网络钱包", + "ln.tab.advanced": "高级", "ln.tab.channels": "通道列表", "ln.tab.invoices": "发票列表", "ln.tab.network": "网络", + "ln.tab.overview": "概述", "ln.tab.payments": "付款", + "ln.tab.receive": "收到", + "ln.tab.send": "发送", "ln.tab.wallet": "钱包", "ln.tab.watchtowers": "瞭望塔", "ln.title": "闪电网络", @@ -592,12 +978,14 @@ "ln.watchtowersTab.removeTowerBtn": "删除瞭望塔", "ln.watchtowersTab.sessions": "会话:{Sessions}", "loader.title": "欢迎使用 Decrediton 钱包", + "loader.title.chooseTheWallet": "请选择要访问的钱包", "loaderBar.StartingDaemon": "正在启动守护程序。。。", "loaderBar.WaitingConnection": "正在连接到守护程序...", "loaderBar.WaitingDaemon": "正在等待守护程序连接...", "loaderBar.checkingNetwork": "正在检查网络是否匹配。。。", "loaderBar.choosingWallet": "选择要开启的钱包", "loaderBar.choosingWalletSPV": "选择要在SPV模式下开启的钱包", + "loaderBar.preCreateTrezorWalletCreate": "创建一个 Trezor 钱包...\n", "loaderBar.preCreateWalletCreate": "创建一个钱包", "loaderBar.preCreateWalletRestore": "恢复钱包...", "loaderBar.startingWallet": "正在启动钱包...", @@ -619,14 +1007,18 @@ "login.form.rpcpassword.placeholder": "RPC 密码", "login.form.rpcport.placeholder.": "RPC 端口", "login.form.rpcuser.placeholder": "RPC用户名", + "logs.back": "返回", "logs.goBack": "返回", "logs.subtitle": "系统日志", + "meta": "[object Object]", "mixer.disabled": "", "mixer.enabled": "", "mixer.ntf.createdAcct": "已成功创建混币帐户", "mixer.ntf.startMixerFailed": "{originalError}", + "mixer.start.running": "混币器 或 自动购票 正在运行,请在购票前将其关闭。", "mixerSetting.header": "混币工具设置", "mixerSettings.gotIt": "我已知晓", + "modal.complete.registration.process": "完成注册流程", "modal.go.to.tickets.status": "转到选票状态页面", "myTicket.syncVSP": "同步失败的VSP选票", "myTickets.loadingMoreTickets": "正在加载更多选票……", @@ -656,6 +1048,8 @@ "mytickets.statistics.votetime.ninetyfifthpercentile.value": "{value, plural, =0 {in the same day} one { within one day } other {within # days} }", "mytickets.statistics.votetime.title": "投票时间", "mytickets.subtitle": "我的选票", + "newBTCConfig.failed": "{originalError}", + "newBTCConfig.success": "您已成功创建默认比特币配置。 请重新启动您的比特币核心钱包,以便按预期使用此配置。", "noMoreTickets.description": "没有更多选票", "noProposals.description": "没有可用的提案", "noStats.description": "没有统计数据", @@ -668,18 +1062,30 @@ "notifications.type.revocation": "已撤销", "notifications.type.send": "已发送", "notifications.type.ticket": "选票", + "notifications.type.ticketfee": "转移", "notifications.type.transfer": "已转账", "notifications.type.vote": "已投票", "numTicketInput.unit": "选票", + "numTicketsInput.ticket": "选票", + "numTicketsInput.tickets": "选票", "numTicketsInput.unit": "选票", "passphraseForm.continueBtn": "继续", "passphraseModal.btnCancel": "取消", "passphraseModal.confirmationRequired": "需要确认密码", "passphraseModal.continue": "继续", + "passphraseModal.ifRequired": "(如果需要)", + "passphraseModal.placeholder": "输入你的私人密码 {ifRequired}", "passphraseModal.privatePassphrase": "私人密码", + "paymentFilter.type.all": "全部", + "paymentFilter.type.confirmed": "已确认", + "paymentFilter.type.failed": "失败", + "paymentFilter.type.pending": "待处理", + "pkeys.title": "Politeia 密钥", "politeia.details.openLinkModal": "在外部浏览器中打开链接", "politeia.details.openLinkModalDesc": "单击“确认”在外部浏览器中打开链接:{link}。", + "preCreateWallet.noDevice.back": "后退", "privacy.allowSendingFromUnmixedAccount": "允许从未混币帐户发送", + "privacy.autobuyerRunning": "自动购买程序正在运行", "privacy.change.account": "未混币帐户", "privacy.coinMixer": "混币工具", "privacy.config.subtitle": "隐私设定", @@ -713,6 +1119,7 @@ "privacy.sendToUnmixedAccount.title": "发送到未混币帐户", "privacy.start.mixer": "启动混币工具", "privacy.start.mixer.confirmation": "启动混币工具", + "privacy.stop.autobuyer": "停止自动购买", "privacy.stop.mixer": "停止混币工具", "privacy.tab.privacy": "隐私", "privacy.tab.security.center": "安全中心", @@ -721,12 +1128,31 @@ "privacy.title": "隐私选项", "privacy.titleSub": "选择 Decrediton 连接至外部服务的方式。您可以稍后在应用设置中更改此设置。", "privacypage.title": "隐私与安全", + "process.mangedTickets.button": "继续", + "process.mangedTickets.button.skip": "跳过", + "process.mangedTickets.title": "密码", + "process.resendVotesToRecentlyUpdatedVSPs.button": "继续", + "process.resendVotesToRecentlyUpdatedVSPs.button.skip": "跳过\n", + "process.resendVotesToRecentlyUpdatedVSPs.title": "密码", + "process.settingPassAccts.button": "继续", + "process.settingPassAccts.title": "密码", + "process.unmanagedTickets.button.skip": "跳过", + "process.unmangedTickets.button": "继续", + "process.unmangedTickets.title": "密码", + "processmanaged.failed": "{originalError}", "proposal.details.description": "你的投票权:{votingPower}", "proposal.details.title": "治理", + "proposal.event.fullTime": "{timestamp, date, medium} {timestamp, time, short}", + "proposal.event.updated": "更新", + "proposal.event.voteended": "投票结束", + "proposal.event.voteends": "投票结束", + "proposal.overview.blocksLeft": "剩余区块", "proposal.overview.created.label": "创建者", "proposal.overview.deadline.label": "投票截止日期", "proposal.overview.fullTime": "{timestamp, date, medium} {timestamp, time, short}", "proposal.overview.lastUpdated.label": "最近更新时间", + "proposal.overview.proposedToRfp.label": "为 {linkedProposal} 提议", + "proposal.overview.title": "在 Politeia 上查看", "proposal.overview.version.label": "版本", "proposalDetails.loadingError": "加载提案时出错:{error}", "proposalDetails.votingInfo.abandoned": "提案已被废弃", @@ -743,6 +1169,7 @@ "proposals.community.createLink": "创建提案", "proposals.community.descr": "通过对社区提案进行投票,你可以对国库资金的使用方向发表意见。\n 参与投票需要购买选票(PoS)。 提案的创建,讨论和其它功能可在这里获得{link}。", "proposals.community.goToProposal": "查看更多Politeia提案评论", + "proposals.community.refreshProposals": "刷新提案", "proposals.community.title": "提案", "proposals.detail.wallet.eligible.header": "有参投资格的选票:", "proposals.detail.wallet.eligible.header.preference": "投票偏好", @@ -763,6 +1190,7 @@ "proposals.statusLinks.voted": "已完成投票", "proposals.updateVoteChoiceModal.btnLabel": "投票", "proposals.updateVoteChoiceModal.title": "确认您的投票", + "proposals.votedCheckmark.label": "投票", "purchas.legacypurchaseTickets.purchaseBtn": "购买", "purchase.isLegacy": "使用旧版VSP", "purchase.isLegacy.legacy": "使用旧版VSP", @@ -818,11 +1246,12 @@ "purchaseTickets.validMsg.legacy": "总计: {amount} 剩余的: {remaining}", "purchaseTickets.vspFee": "VSP费用", "purchaseTickets.vspFrom": "VSP", - "receive.accountLabel": "本地址为", + "receive.accountLabel": "接收地址为", "receive.amountPlaceholder": "金额", "receive.copyHash": "复制", "receive.errors.invalidAmount": "请输入有效金额", "receive.errors.negativeAmount": "请输入有效金额(> 0)", + "receive.hardwareWalletWarning": "警告!硬件钱包不能购买选票。此地址仅支持接收正常交易的资金。请勿用于选票相关的接收。\n", "receive.modalClose": "关闭", "receive.modalLabel": "这是我的Decred(DCR) 地址", "receive.newAddressBtn": "生成新地址", @@ -831,11 +1260,14 @@ "receive.tooltipCopied": "✓已复制到剪贴板", "receive.tooltipGenerated": "+生成新地址", "receive.viewQR": "查看二维码", + "receiveTab.QRCode": "二维码", + "receiveTab.copy": "复制", "releaseNotes.goBack": "返回", "rescan.progressPercent": "{progress, number, percent}", "rescan.rescanning": "重新扫描中 {blockProgress} ({progressPercent})", "runTicketBuyer.Failed": "密码错误,请再一次尝试", "runTicketBuyer.Success": "购票器成功开启。", + "runTicketBuyerNew.Success": "自动购票已成功启动。", "security.sign.failed": "{originalError}", "security.signTitle": "签名消息", "security.signature": "签名", @@ -880,6 +1312,15 @@ "send.confirmAmountAddresses": "目标地址", "send.confirmAmountLabelFor": "总花费", "send.destinationAddrPlaceholder": "地址", + "send.dex.amount": "数量", + "send.dex.amountPlaceholder": "数量", + "send.dex.cancelSendAllTitle": "取消发送所有资金", + "send.dex.destinationAddrPlaceholder": "地址", + "send.dex.from": "来自", + "send.dex.sendAllTitle": "发送所有资金", + "send.dex.sendAllTitle.disabled": "发送所有资金 - 已禁用", + "send.dex.sendToSelfBtn": "发送给自己", + "send.dex.to": "至", "send.errors.abandonTxFailed": "{originalError}", "send.errors.abandonTxSuccess": "中止交易成功!", "send.errors.constructTxFailed": "{originalError}", @@ -889,9 +1330,10 @@ "send.errors.publishTxFailed": "{originalError}", "send.errors.signTxFailed": "{originalError}", "send.errors.validateAddressFailed": "{originalError}", - "send.feeEstimation": "预计费用", + "send.feeEstimation": "预计网络费用", "send.from": "来自", - "send.insuficient.funds": "", + "send.indicator.running": "混币器、自动购票 或 购票请求 正在运行,请在发送交易前关闭它们。", + "send.insuficient.funds": "资金不足", "send.publishUnminedTransactions.success": "将未打包的交易重新发布到decred网络。", "send.sendAllTitle": "发送所有资金", "send.sendAllTitle.disabled": "发送所有资金-已禁用", @@ -900,18 +1342,50 @@ "send.sendOthersTitle": "将资金发送到另一个账户", "send.sendSelfTitle": "将资金发送到另一个账户", "send.sendToSelfBtn": "发送给自己", - "send.sizeEstimation": "预计大小", + "send.sizeEstimation": "预计数据大小", "send.subtitle": "发送DCR", "send.to": "至", - "send.totalAmountEstimation": "发送到钱包外地址的总金额", + "send.totalAmountEstimation": "预计发送的总金额", "send.unsignedRawTxTite": "未签名的原始交易", "sendFromUnmixedModal.copyConfirmationPhrase": "我了解其风险", + "sendtab.addOutput": "添加输出", + "sendtab.addressLabel": "发送至", + "sendtab.amountLabel": "数量", + "sendtab.amountPlaceholder": "数量", + "sendtab.cancelSendAllTitle": "取消发送所有资金", + "sendtab.deleteOutput": "删除输出", + "sendtab.percentOfBalance": "{percent}% 的账户余额", + "sendtab.recipientsAddrPlaceholder": "接收DCR的地址", + "sendtab.sendAllAmountLabel": "金额", + "sendtab.sendAllTitle": "发送所有资金", + "sendtab.sendAllTitle.disabled": "发送所有资金 - 已禁用", + "sendtab.sendOthersTitle": "发送至另一个钱包", + "sendtab.sendSelfTitle": "发送至另一个账户", + "sendtab.sendTo": "发送至", + "set.getvspticketstatus.failed": "获取 vsp 选票状态失败:{originalError}", + "set.vote.failed": "设置钱包投票选项失败: {originalError}", + "set.vote.partial.success": "您已经在您可能设置的任何VSP上部分更新了您的钱包投票选择。", + "set.vote.success": "您已成功设置钱包投票选项。", + "set.vspdvote.failed": "设置 vspd 投票选项失败: {originalError}", + "set.vspdvote.single.failed": "在 {host} 上设置 vspd 投票选择失败。{originalError}\n", + "set.vspdvote.timeout.failed": "在 {host} 上设置 vspd 投票选择超时\n", + "setNewPassphraseModal.confirm": "确认", + "setNewPassphraseModal.confirmPassphrasePlaceholder": "确认您输入的密码", + "setNewPassphraseModal.confirmationRequired": "需要确认", + "setNewPassphraseModal.newPassphrase": "新密码", + "setNewPassphraseModal.newPassphrasePlaceholder": "输入您设置的新密码", + "setTSpendPolicy.updateVoteChoiceFailed": "设置 tspend 策略失败:{originalError}\n", + "setTSpendPolicy.updateVoteChoiceSuccess": "您的 tspend 策略已成功更新!感谢您参与 Decred 的治理。\n", + "setTreasuryPolicy.updateVoteChoiceSuccess": "您的国库支出投票偏好已经更新!感谢您参与 Decred 的治理!", + "settings.DiscoverAddressBtn": "发现地址使用情况", + "settings.GapLimit.placeholder": "间隔限制", "settings.SPV": "SPV", - "settings.SPVConnect": "链接SPV", - "settings.advancedDaemon.false": "已禁用", + "settings.SPVConnect": "连接SPV", + "settings.addressUsage": "地址使用", + "settings.advancedDaemon.false": "禁用", "settings.advancedDaemon.label": "启动高级守护进程", - "settings.advancedDaemon.true": "已启用", - "settings.alreadySetFromCli": "这已在启动decrediton是被设置为命令行", + "settings.advancedDaemon.true": "启用", + "settings.alreadySetFromCli": "开启 decrediton 时被设置为命令行启动", "settings.changeConfirmation": "更改密码", "settings.changePassphrase": "已成功更改私人密码。", "settings.closeWalletModalContent": "您是否确定要关闭{walletName}回到首页?", @@ -919,12 +1393,22 @@ "settings.closeWalletModalTitle": "请确认", "settings.closeWalletModalWithAutobuyerModal": "是否选择继续关闭{walletName}并返回主页面?自动购票器仍在运行中。继续将关闭自动购票器并停止购票。", "settings.description": "更改网络设置需要重启", + "settings.discoverUsage": "发现地址使用情况", + "settings.discoverUsageContent": "在极少数情况下,默认间隔限制为20,可能会导致无法发现地址。建议在尝试其它选项并与支持人员讨论后使用此功能。 请注意,将间隔限制提高到100以上会导致加载时间过长。", "settings.displayedUnits": "显示的单位", - "settings.errors.changePassphraseFailed": "更新密码失败。错误密码,请重试。", + "settings.errors.changePassphraseFailed": "更新密码失败。私人密码错误,请重试。", + "settings.errors.setPassphraseAcctFailed": "更新密码失败。密码错误,请重试。", "settings.gapLimit.label": "间隙限制", "settings.getstartpage.group-title.connectivity": "连接性", "settings.getstartpage.group-title.general": "通用", - "settings.getstartpage.group-title.privacy-and-security": "隐私权与安全性", + "settings.getstartpage.group-title.launcher": "启动器", + "settings.getstartpage.group-title.misc": "杂项", + "settings.getstartpage.group-title.network": "网络", + "settings.getstartpage.group-title.privacy-and-security": "隐私与安全", + "settings.getstartpage.group-title.privatePassphrase": "私人密码", + "settings.getstartpage.group-title.proxy": "代理", + "settings.getstartpage.group-title.timezone": "时区", + "settings.getstartpage.group-title.ui": "用户界面", "settings.group-title.connectivity": "连接性", "settings.group-title.general": "通用", "settings.group-title.privacy-and-security": "隐私权与安全性", @@ -944,19 +1428,26 @@ "settings.privacy.stakepoolListing.label": "VSP列表", "settings.privacy.updateCheck.description": "从github.org获取最新发布的版本", "settings.privacy.updateCheck.label": "更新检查", + "settings.privatePassphrase.buttonLabel": "更新私人密码", "settings.proxy.location": "代理地址", - "settings.proxy.title": "代理", + "settings.proxy.save": "保存代理设置", + "settings.proxy.title": "设置代理", "settings.proxy.type": "代理类型", "settings.proxy.type.none": "无代理", "settings.resetNetworkContent": "您选择更改的设置需要重新启动Decrediton,请在继续之前确认此操作。", "settings.resetNetworkTitle": "请重置", "settings.save": "保存", + "settings.saved": "设置保存成功", "settings.spv.false": "关闭", "settings.spv.true": "开启", "settings.subtitle": "设置", + "settings.tab.connectivity": "连接", + "settings.tab.general": "通用", "settings.tab.logs": "日志", + "settings.tab.privacyandsecurity": "隐私和安全", "settings.tab.settings": "设置", - "settings.tab.sources": "来源", + "settings.tab.sources": "资源", + "settings.tab.trezor": "Trezor", "settings.tab.tutorials": "指南", "settings.timezone.local.description": "使用本地时区", "settings.timezone.title": "时区", @@ -964,17 +1455,25 @@ "settings.title": "设置", "settings.trezorConfig": "配置Trezor", "settings.ui.title": "UI", + "settings.uiAnimations.disabled": "已禁用", + "settings.uiAnimations.enabled": "启用", + "settings.uiAnimations.label": "用户界面动画", + "settings.uiAnimations.whatsthis": "这是什么?", "settings.uitheme.type": "色调", "settings.uitheme.type.dark": "夜晚模式", "settings.uitheme.type.light": "白天模式", "settings.updatePrivatePassphrase": "更新私人密码", + "setupWallet.processAccounts.failed": "{originalError}", + "setupwallet.progressLabel": "设置钱包", "shutdown.header.title": "正在关闭Decrediton", "sidebar.accounts.name.default": "主账户", "sidebar.connectedTo": "连接到", "sidebar.isWatchingOnlyTooltip": "这是只读模式钱包功能有限", "sidebar.lastBlockIsRecent": "几秒钟前", - "sidebar.latestBlock": "块", + "sidebar.latestBlock": "区块", "sidebar.link.accounts": "账户", + "sidebar.link.dex": "DEX", + "sidebar.link.disabledDexTooltip": "使用 SPV 时 DEX 不可用。 请转到设置并禁用 SPV 以访问 DEX。", "sidebar.link.governance": "治理", "sidebar.link.home": "概览", "sidebar.link.ln": "闪电交易", @@ -984,6 +1483,7 @@ "sidebar.link.trezor": "Trezor", "sidebar.menuLinkTooltip": "{value}", "sidebar.mixer.running": "在后台运行一个或多个decrediton功能:隐私混币工具、自动购票工具、购票询问", + "sidebar.peerCount": "点", "sidebar.peersCount": "节点", "sidebar.rescanBtn.tip": "启动事务重新扫描。\n\n重新扫描可能帮忙解决一些余额错误。\n\n注意:这会扫描整个区块链,\n但不会重新下载它。", "sidebar.rescanCancelBtn.tip": "取消重新扫描", @@ -1014,6 +1514,7 @@ "stake.discoverStakeOoolsAPILink": "此链接", "stake.enableStakePoolListing.button": "启用VSP列表", "stake.enableStakePoolListing.description": "当前禁用了来自外部API端口的VSP列表。 请启用第三方服务访问或手动配置VSP。", + "stake.isLegacy": "使用旧版 VSP", "stake.lastTicketLink": "{shortHash}... View →", "stake.lastVotedTicket": "上一张投出的选票", "stake.lastVotedTicket.none": "无", @@ -1049,10 +1550,16 @@ "stakepools.list.form.cancel": "取消", "stakepools.list.form.submit": "添加VSP", "stakepools.removedStakePoolConfig": "已成功删除选票矿池配置", + "stakingTabWarning.desc": "请检查选票和质押的基础知识:", + "stakingTabWarning.okBtn": "我理解并接受风险", + "stakingTabWarning.title": "在您继续之前...", "startup.dataDir.tip": "选择一个路径", "statistics.subtitle": "统计数据", "statusSmall.daysToVotePlural": "{days, plural, one {# 天} other {# 天}}", "stopTicketBuyer.Success": "购票器成功停止。", + "stopTicketBuyerNew.Success": "自动购票成功停止。", + "sync.vsp.failed": "{originalError}", + "sync.vsp.success": "所有错误的选票都已成功尝试支付。 请确保所有选票现在显示已付款。 否则,请重试或使用不同的 VSP。", "syncVsp.account": "帐户", "syncVsp.vsp": "VSP选项", "ticket.daysToVoteLabel": "投票日期", @@ -1067,34 +1574,52 @@ "ticket.status.multiple.unknown": "未知", "ticket.status.multiple.unmined": "未打包", "ticket.status.multiple.voted": "已投票", + "ticket.type.liveTicketLabel": "选票,{typeMsg}", + "ticket.type.ticketLabel": "选票,{typeMsg}", + "tickets.autobuyer.running": "混币器 或 购票进程 正在运行,请在启动 自动购票 之前将其关闭。", "tickets.autobuyerRunning.confirmModal.btnCancel": "取消", "tickets.autobuyerRunning.confirmModal.btnConfirm": "确认", + "tickets.autobuyerRunning.confirmModal.closeAnyway": "关闭全部", "tickets.autobuyerRunning.message": "如果继续,将停止购买选票", "tickets.autobuyerRunning.title": "自动购票仍在运行中", "tickets.description": "当前价格: {ticketPrice}", "tickets.errors.importScriptFailed": "{originalError}", "tickets.errors.purchaseTicketsFailed": "{originalError}", "tickets.errors.refreshStakePoolInfo": "从{host}刷新选票矿池错误{originalError}", + "tickets.errors.revokeTicketFailed": "{originalError}", "tickets.errors.revokeTicketsFailed": "{originalError}", "tickets.errors.setStakePoolVoteChoicesFailed": "{originalError}", "tickets.errors.startAutoBuyerFailed": "{originalError}", "tickets.errors.updateStakePoolConfigFailed": "{originalError}", "tickets.fee.error.confirmModal.btnCancel": "取消", "tickets.fee.error.confirmModal.btnConfirm": "确认", + "tickets.fee.error.confirmModal.closeAnyway": "关闭全部", "tickets.fee.error.message": "您还有没有支付选票费,如果您继续并选择它们,这会使它们\n 被丢失。点击此按钮可重新获取同步费用。", "tickets.fee.error.title": "VSP选票费用错误", "tickets.importScriptConfirmation": "导入脚本确认", "tickets.importScriptHeader": "您已成功导入脚本。", + "tickets.mixer.running.confirmModal.closeAnyway": "关闭全部", "tickets.purchase.loading": "加载中", + "tickets.purchase.running": "混币器 或 自动购票 正在运行,请在购票前将其关闭。", "tickets.purchaseConfirmation": "购票确认", "tickets.purchaseConfirmation.legacy": "购票确认", "tickets.purchaseTicketsHeader": "你已购买 {numTickets, plural, one { # ticket } other { # tickets }}", + "tickets.purchaseTicketsHeaderLess": "您购买了 {numTickets,plural, one { # ticket } other { # tickets }}。 这小于您请求的 {numAttempted} 数量。 虽然您的余额充足,但您缺乏足够的可花费支出。 您现在可以尝试购买更多。", "tickets.purchasing.confirmModal.btnCancel": "取消", "tickets.purchasing.confirmModal.btnConfirm": "关闭Decrediton", + "tickets.purchasing.confirmModal.closeAnyway": "关闭全部", "tickets.purchasing.message": "Decrediton仍在确认购票。\n如果现在关闭Decrediton,这可能会\n导致购买失败。", "tickets.purchasing.title": "购买选票", + "tickets.qr.button": "活动选票二维码", + "tickets.qr.loading": "正在加载...", + "tickets.qr.notickets": "没有发现活跃选票", + "tickets.qr.tooltip": "选票二维码", + "tickets.revoke.running": "混币器 或 自动购票 正在运行,请在撤销选票之前将其关闭。", "tickets.revokeConfirmations": "撤销选票确认", "tickets.revokeConfirmations.legacy": "确认撤销选票", + "tickets.revokeTicketConfirmations": "确认撤销选票", + "tickets.revokeTicketDescription": "在继续之前,请确认此选票在 dcrdata 上丢失或过期。 任何仍在等待投票的选票都不会被撤销,并且可能会留下必须放弃的错误交易。", + "tickets.revokeTicketHeader": "您已成功撤销选票", "tickets.revokeTicketsHeader": "您已成功撤销选票", "tickets.sortby.newest": "最新", "tickets.sortby.oldest": "最初的", @@ -1118,6 +1643,8 @@ "tickets.title": "选票", "tickets.type.all": "所有", "tickets.updateStakePoolConfigHeader": "您已成功更新选票矿池设置。", + "tickets.warning.description": "当前价格:{ticketPrice}", + "tickets.warning.title": "质押", "transaction.confirmationHeight": "{确认,复数, =0 {已提交, 等待批准的区块} 一个 {# 确认} 其他 {# 确认}}", "transaction.sortby.newest": "最新", "transaction.sortby.oldest": "最初的", @@ -1143,13 +1670,31 @@ "transactions.tab.send": "发送", "transactions.title": "交易", "transactions.txtypes.tooltip": "交易类型", + "treasurySpending.description": "国库资金支出现在需要利益相关者对支出进行投票。您可以通过特定的 Politeia Key 或 Tspend 交易参与并设置投票偏好。可以在{link}中验证密钥。", + "treasurySpending.piKey": "Pi 密钥:", + "treasurySpending.policyOptions.abstain": "弃权", + "treasurySpending.policyOptions.no": "拒绝", + "treasurySpending.policyOptions.yes": "同意", + "treasurySpending.title": "国库支出", + "treasurySpending.txhash": "交易哈希:\n", + "treasurySpending.updatePreference": "更新偏好设置", + "treasurySpending.updateVoteChoiceModal.title": "确认您的投票", + "treasurySpending.votedFor": "投票给:", + "treasurySpending.votingFor": "投票给:", "trezor.backupDevice.success": "Trezor设备已备份。", "trezor.changeHomeScreen.success": "已成功更换Trezor主屏幕", "trezor.changeLabel.changeButton": "更改", + "trezor.changeLabel.changeLabelPlaceholder": "新标签", + "trezor.changeLabel.changeToDecredHomeScreen": "在主屏幕上使用 Decred 符号\n", "trezor.changeLabel.description": "新标签", "trezor.changeLabel.header": "更改标签", "trezor.changeLabel.success": "已选择trezor的标签改为{label}", "trezor.configButtons.header": "配置Trezor", + "trezor.deviceSetup.backupDevice": "备份装置\n", + "trezor.deviceSetup.header": "设备设置和恢复\n", + "trezor.deviceSetup.initDevice": "初始化设备(创建新钱包)\n", + "trezor.deviceSetup.recoverDevice": "恢复设备(从 Trezor 备份)\n", + "trezor.deviceSetup.wipeDevice": "抹除设备\n", "trezor.firmwareUpdate.header": "固件升级", "trezor.getStartedConfig.btnConnect": "连接到Trezor", "trezor.getStartedConfig.noDeviceFound": "未发现trezor设备。请检查连接及trezor bridge软件。", @@ -1159,38 +1704,68 @@ "trezor.noDevice.btnConnect": "连接到Trezor", "trezor.noDevice.message": "未检测到Trezor设备。请连接设备及检测已安装及运行trezor bridge。", "trezor.notBackedUp": "必须备份Trezor才能执行此操作。", - "trezor.passphraseModal.description": "", + "trezor.passphraseModal.description": "输入存储在 trezor {label} 中的钱包密码", "trezor.passphraseModal.title": "输入Trezor密码", + "trezor.passphraseOnDevice.disabled": "Trezor {label} 中禁用了设备上的密码", + "trezor.passphraseOnDevice.enabled": "Trezor {label} 中启用了设备上的密码", "trezor.passphraseProtectionSuccess.disabled": "trezor{label}的密码保护功能已被禁用", "trezor.passphraseProtectionSuccess.enabled": "在trezor{label}中启用密码保护功能", "trezor.pinModal.clear": "清除", - "trezor.pinModal.description": "", + "trezor.pinModal.description": "点击与您在 trezor {label} 上的 pin 对应的按钮序列", + "trezor.pinModal.learnMore": "了解更多\n", "trezor.pinModal.title": "输入密码", "trezor.pinProtectionSuccess.disabled": "在trezor{label}中已禁用Pin保护", "trezor.pinProtectionSuccess.enabled": "在trezor{label}中启用Pin保护", "trezor.recoverDevice.success": "已恢复Trezor设备", "trezor.recoveryButtons.header": "设备恢复", + "trezor.security.header": "安全\n", + "trezor.togglePassphraseConfirmModal.content": "{label} 的密码保护已{启用}。 {ifEnabled}您必须拔下并重新插入 Trezor 才能使这些更改在设备上生效。", + "trezor.togglePassphraseConfirmModal.disable": "禁用", + "trezor.togglePassphraseConfirmModal.disabled": "禁用", + "trezor.togglePassphraseConfirmModal.enable": "启用", + "trezor.togglePassphraseConfirmModal.enabled": "启用", + "trezor.togglePassphraseConfirmModal.ifEnabled": "对于您的第一次敏感操作,设备将要求输入密码。 这个短语为您的 Trezor 创建一个新种子。 如果与已创建的钱包一起使用,后续操作可能会失败,因为设备现在正在使用不同的帐户/钱包。 只要设备保持插入状态,进一步的操作将重新使用第一个传递的密码,并且不再询问。", + "trezor.togglePassphraseConfirmModal.title": "{启用} Trezor 密码", "trezor.updateFirmware.success": "Trezor设备固件已升级", "trezor.walletCreationPassPhraseModal.title": "输入创建钱包密码", "trezor.walletCreationPassphrasesMismatched": "密码不匹配", - "trezor.walletCreationpassphraseModal.description": "", + "trezor.walletCreationpassphraseModal.description": "输入要从 trezor 设备 {label} 恢复的钱包的密码", "trezor.walltCreationPrivatePassphrase": "钱包密码", "trezor.walltCreationPrivatePassphraseConfirm": "确认钱包密码", "trezor.wipeDevice.success": "已抹除Trezor设备", - "trezor.wordModal.description": "", + "trezor.wordModal.description": "在 trezor 设备中输入请求的单词。", "trezor.wordModal.selectPlaceholder": "开始输入...", + "trezor.wordModal.selectPlaceholderLoading": "正在加载...\n", "trezor.wordModal.title": "输入被要求的单词", + "trezorLoaderBarContainer.Connected": "{label} Trezor", + "trezorLoaderBarContainer.connected": "连接", + "trezorLoaderBarContainer.deviceLabel": "新DCR\n", "trezorPage.backupDeviceBtn": "备份设备", "trezorPage.changeHomeScreen": "更换主屏幕", "trezorPage.description": "管理你的Trezor设备。", "trezorPage.initDeviceBtn": "初始化设备", + "trezorPage.pathInputPlaceholder": "选择路径...\n", "trezorPage.recoverDeviceBtn": "恢复设备", + "trezorPage.security.loading": "加载中\n", + "trezorPage.security.off": "离开\n", + "trezorPage.security.on": "在\n", + "trezorPage.security.passphraseOnDeviceProtection.enabled": "禁用设备保护上的密码\n", + "trezorPage.security.passphraseOnDeviceProtection.not.enabled": "启用设备保护密码\n", + "trezorPage.security.passphraseProtection.enabled": "禁用密码保护\n", + "trezorPage.security.passphraseProtection.not.enabled": "启用密码短语保护\n", + "trezorPage.security.pinProtection.enabled": "禁用 PIN 码保护\n", + "trezorPage.security.pinProtection.not.enabled": "启用 PIN 码保护\n", + "trezorPage.security.togglePassPhraseProtection": "切换密码保护 ({status})\n", + "trezorPage.security.togglePassphraseOnDevice": "切换设备上的密码保护 ({status})\n", + "trezorPage.security.togglePinProtection": "切换 PIN 保护 ({status})\n", "trezorPage.title": "Trezor", "trezorPage.togglePassPhraseProtectionBtn": "切换密码保护", + "trezorPage.togglePassphraseOnDeviceBtn": "在设备上切换密码", "trezorPage.togglePinProtectionBtn": "切换密码保护", "trezorPage.updateFirmwareBtn": "更新固件", "trezorPage.updateFirmwarePah": "固件文件路径", "trezorPage.wipeDeviceBtn": "抹除设备", + "tspend.title": "支出\n", "tutorial.finishBtn": "完成", "tutorial.nextBtn": "下一步", "tutorial.skipBtn": "跳过", @@ -1200,41 +1775,150 @@ "tutorial.standardPage.previousPage": "前一个", "tutorial.subtitle": "指南", "tutorial.ticketLifecycle.title": "选票生命周期", + "tutorialpage.back": "后退\n", + "tutorials.blocks.label1": "投票简介", + "tutorials.blocks.label2": "投票数", + "tutorials.blocks.label3": "拒绝", + "tutorials.blocks.label4": "激励措施", + "tutorials.blocks.label5": "硬分叉", + "tutorials.blocks.title1": "投票简介", + "tutorials.blocks.title2": "投票数", + "tutorials.blocks.title3": "拒绝", + "tutorials.blocks.title4": "激励措施", + "tutorials.blocks.title5": "硬分叉", + "tutorials.consensusCode.label1": "共识代码", + "tutorials.consensusCode.title1": "关注共识代码", + "tutorials.consensusVoting.label1": "概述", + "tutorials.consensusVoting.label2": "规则", + "tutorials.consensusVoting.label3": "基础设施", + "tutorials.consensusVoting.label4": "升级", + "tutorials.consensusVoting.label5": "大概时间", + "tutorials.consensusVoting.label6": "选票生命周期", + "tutorials.consensusVoting.label7": "赞同", + "tutorials.consensusVoting.title1": "概述", + "tutorials.consensusVoting.title2": "规则", + "tutorials.consensusVoting.title3": "基础设施", + "tutorials.consensusVoting.title4": "升级", + "tutorials.consensusVoting.title5": "大概时间", + "tutorials.consensusVoting.title6": "选票生命周期", + "tutorials.consensusVoting.title7": "赞同", + "tutorials.decredIntro.label1": "为什么要创建Decred?", + "tutorials.decredIntro.label2": "治理体系", + "tutorials.decredIntro.title1": "为什么要创建 Decred?", + "tutorials.decredIntro.title2": "治理系统赋予设区权力", + "tutorials.identity.label1": "备份您的 ID", + "tutorials.identity.label2": "身份和提案", + "tutorials.identity.label3": "身份和CMS", + "tutorials.identity.label4": "及时提交发票", + "tutorials.identity.title1": "备份您的ID", + "tutorials.identity.title2": "身份和提案", + "tutorials.identity.title3": "身份和CMS", + "tutorials.identity.title4": "及时提交发票", + "tutorials.lifecycle.label1": "权益证明、区块奖励", + "tutorials.lifecycle.label2": "选票的不同状态", + "tutorials.lifecycle.label3": "未成熟的选票", + "tutorials.lifecycle.label4": "活跃选票", + "tutorials.lifecycle.label5": "投票", + "tutorials.lifecycle.label6": "“当出现问题时”", + "tutorials.lifecycle.title1": "权益证明、区块奖励", + "tutorials.lifecycle.title2": "选票在其生命周期中会经历不同的状态", + "tutorials.lifecycle.title3": "未成熟的选票", + "tutorials.lifecycle.title4": "活跃选票", + "tutorials.lifecycle.title5": "投票", + "tutorials.lifecycle.title6": "“当出现问题时”", + "tutorials.ln.label1": "备份", + "tutorials.ln.label2": "保持在线", + "tutorials.ln.label3": "暸望塔服务", + "tutorials.ln.label4": "通道及确认", + "tutorials.ln.label5": "操作期间解锁", + "tutorials.ln.label6": "闪电网络是第2层网络", + "tutorials.ln.title1": "除了钱包种子之外,还需要备份数据。", + "tutorials.ln.title2": "最好让你的钱包大部分时间保持在线。", + "tutorials.ln.title3": "为了安全起见,在线钱包应该使用暸望塔服务。", + "tutorials.ln.title4": "发送和接收金额不能超过通道金额。", + "tutorials.ln.title5": "通过使用单独的闪电钱包来降低风险。", + "tutorials.ln.title6": "闪电网络是Decred区块链上的第2层网络,旨在促进小额支付。", + "tutorials.powPos.label1": "PoW", + "tutorials.powPos.label2": "PoS", + "tutorials.powPos.label3": "混合 PoW/PoS", + "tutorials.powPos.title1": "1 cpu, 1 票 - 工作量证明", + "tutorials.powPos.title2": "1 币,1 票 - 权益证明", + "tutorials.powPos.title3": "混合 PoW/PoS - 更加强大", "tutorials.staking": "什么是POS(权益证明)?", + "tutorials.staking.label1": "创建新区块", + "tutorials.staking.label2": "共识变化", + "tutorials.staking.label3": "国库和项目管理", + "tutorials.staking.title1": "创建新区块", + "tutorials.staking.title2": "共识变化", + "tutorials.staking.title3": "国库和项目管理", "tutorials.subtitle": "指南", "tutorials.ticketLifecycle": "了解选票的生命周期", + "tutorials.tickets.label1": "时间锁定硬币", + "tutorials.tickets.label2": "选票有多种功能", + "tutorials.tickets.label3": "奖励", + "tutorials.tickets.title1": "选票是由时间锁定硬币创建的", + "tutorials.tickets.title2": "选票有多种功能", + "tutorials.tickets.title3": "奖励", "txDetails.abandontTransaction": "停止交易", + "txDetails.agendaChoices": "提案选择", "txDetails.backBtn": "返回", + "txDetails.bits": "投票", "txDetails.blockHeightLabel": "高度", "txDetails.blockLabel": "块", + "txDetails.false": "错误", + "txDetails.feeTxHashLabel": "tx 哈希费用", + "txDetails.feeTxStatusLabel": "费用交易状态", "txDetails.indicatorConfirmed": "已确认", "txDetails.indicatorPending": "正在处理", "txDetails.io.title": "I / O详细信息", + "txDetails.lastBlockValid": "最后一个区块有效", + "txDetails.missedOn": "错过了", "txDetails.nonWalletInputs": "非钱包输入", + "txDetails.nonWalletInputs.Stakebase": "权益基础", "txDetails.nonWalletOutputs": "非钱包输出", "txDetails.properties": "属性", "txDetails.purchasedOn": "购买于", "txDetails.rawTransactionLabel": "原始交易", "txDetails.rebroadcastTransactions": "重播交易", + "txDetails.revokeTicket": "撤销选票", "txDetails.reward": "奖励", "txDetails.sentFrom": "发送自", + "txDetails.signMessageBtn": "获取VSP选票状态", + "txDetails.signMessageModal": "获取VSP选票状态", + "txDetails.spendingTx": "支出交易", "txDetails.ticketCost": "选票成本", "txDetails.timestamp": "{timestamp, date, medium} {timestamp, time, medium}", "txDetails.toAddress": "发送地址", + "txDetails.tooManyNonWalletOutputs": "请使用上面的 txid 链接查看 dcrdata 上的所有非钱包输出。", + "txDetails.tooManyNonWalletOutputsAddresses": "请使用上面的 txid 链接查看 dcrdata 上的所有非钱包地址。", "txDetails.transactionFeeLabel": "交易费", "txDetails.transactionLabel": "交易", + "txDetails.treasuryPolicy": "国库政策", + "txDetails.true": "真", + "txDetails.tspendPolicy": "TS支出政策\n", "txDetails.type.coinbase": "Coinbase", + "txDetails.type.expired": "已到期", + "txDetails.type.immature": "不成熟", + "txDetails.type.live": "现场", + "txDetails.type.missed": "丢失", + "txDetails.type.revocation": "撤销", "txDetails.type.revoke": "撤销", + "txDetails.type.revoked": "已撤销", "txDetails.type.ticket": "选票", + "txDetails.type.unmined": "未开采", "txDetails.type.vote": "投票", + "txDetails.type.voted": "已投票", "txDetails.unConfirmed": "未确认", + "txDetails.version": "投票版本", "txDetails.votedOn": "投票于", + "txDetails.vspHost": "VSP主机", "txDetails.walletInputs": "钱包输入", "txDetails.walletOutputs": "钱包输出", "txFilter.type.all": "所有", "txFilter.type.mixed": "混币", "txFilter.type.received": "已接收", "txFilter.type.sent": "已发送", + "txFilter.type.ticketfee": "选票费", "txFilter.type.transfered": "已转账", "txHistory.Pending": "正在处理", "txHistory.dayMonthDisplay": "", @@ -1242,20 +1926,29 @@ "txHistory.in.tx": "从{acc}", "txHistory.out.tx": "发送 {acc}", "txRow.live.feeStatus.tooltip": "实时费用", + "txhistory.filterByAddrOrHashPlaceholder": "按地址或哈希搜索", "txhistory.filterByAddrPlaceholder": "按地址筛选", + "txhistory.filterByHashPlaceholder": "按哈希过滤", "txhistory.icon.mixed": "混合", "txhistory.icon.received": "已接收", "txhistory.icon.self": "内部转账", "txhistory.icon.sent": "已发送", + "txhistory.icon.ticketfee": "选票费", "txhistory.icon.transaction": "交易", + "txhistory.pending": "待办", "votingPreferences.dashboard": "投票面板", "votingPreferences.description": "共识更改是Decred的链上治理投票,这决定着是否采用新的网络共识规则,参与投票需要(PoS)选票。", "votingPreferences.noAgenda": "目前无投票议程", + "votingPreferences.noFoundAgenda": "没有与您的搜索匹配的议程。", + "votingPreferences.outdatedUsedVsps.alert": "请联系{host}要求升级。", "votingPreferences.title": "共识更改", "vsp.addNewPromptEmpty": "输入以添加新的选票池", "vsp.autobuyer.accountFrom": "来自", "vsp.autobuyer.balanceToMaintain": "保留余额", + "vsp.autobuyer.balanceToMaintain.placeholder": "数量", "vsp.autobuyer.balanceToMaintainError": "你要保留的余额无效", + "vsp.autobuyer.maxFeePercentage.label": "最高费用", + "vsp.autobuyer.maxFeePercentage.placeholder": "百分", "vsp.autobuyer.modal.balanceToMaintain": "保留余额", "vsp.autobuyer.modal.stakepool": "VSP", "vsp.autobuyer.stakePoolLabel": "VSP", @@ -1263,9 +1956,14 @@ "vsp.autobuyer.subtitle": "自动购票", "vsp.feeTooltip": "费用: {feePercentage} %", "vsp.mytickets.subtitle": "等待被选中的选票", + "vsp.outdated": "已过时", + "vsp.runautobuyer.failed": "{originalError}", + "vsp.select.error": "vsp 超时: {vsp}", + "vsp.ticket.confirmed": "已确认", "vsp.ticket.error": "错误", "vsp.ticket.paid": "支付", "vsp.ticket.started": "处理", + "vsp.ticket.vsp.fee.confirmed": "确认费用", "vsp.ticket.vsp.fee.errored": "费用错误", "vsp.ticket.vsp.fee.paid": "已付费用", "vsp.ticket.vsp.fee.started": "未付费用", @@ -1285,6 +1983,7 @@ "walletselection.editWallets": "编辑钱包", "walletselection.lastAccess": "上次访问", "walletselection.launchWallet": "启动钱包", + "walletselection.ln": "闪电", "walletselection.privacy": "隐私", "walletselection.removeConfirmModal.content": "警告此操作为永久性操作!继续之前,请确保已备份钱包种子。", "walletselection.removeConfirmModal.title": "删除 {wallet}", From f8447b20aaa1108efff94bc735f1d636f85405fa Mon Sep 17 00:00:00 2001 From: JoeGruffins <34998433+JoeGruffins@users.noreply.github.com> Date: Wed, 27 Sep 2023 01:07:08 +0900 Subject: [PATCH 13/15] ledger: Enable mainnet. (#3906) --- app/actions/LedgerActions.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/app/actions/LedgerActions.js b/app/actions/LedgerActions.js index 7e742d1543..c632e1945f 100644 --- a/app/actions/LedgerActions.js +++ b/app/actions/LedgerActions.js @@ -13,6 +13,13 @@ import { const coin = "decred"; +// This error will happen when ledger is on the home screen, but it is called +// "UNKNOWN_ERROR" so may be something else. +const wrongScreenStatus = 25873; +// This error indicates the wrong app, like the btc app, is open and will say +// CLA_NOT_SUPPORTED +const wrongAppStatus = 28160; + import * as selectors from "selectors"; export const LDG_LEDGER_ENABLED = "LDG_LEDGER_ENABLED"; @@ -133,11 +140,7 @@ export const LDG_GETWALLETCREATIONMASTERPUBKEY_SUCCESS = export const getWalletCreationMasterPubKey = () => async (dispatch, getState) => { dispatch({ type: LDG_GETWALLETCREATIONMASTERPUBKEY_ATTEMPT }); - // TODO: Enable on mainnet. const isTestnet = selectors.isTestNet(getState()); - if (!isTestnet) { - throw "disabled on mainnet"; - } try { const payload = await getPubKey(isTestnet); const hdpk = ledgerHelpers.fixPubKeyChecksum(payload, isTestnet); @@ -155,11 +158,24 @@ function doWithTransport(fn) { return fn(transport).then((r) => transport .close() - .catch((/*e*/) => {}) // throw? + .catch((e) => { + throw e; + }) .then(() => r) ); }) .catch((e) => { + const notDecred = "the decred ledger app is not open on the device"; + if (e.statusCode && e.message) { + switch (e.statusCode) { + case wrongScreenStatus: + e.message = notDecred; + throw e; + case wrongAppStatus: + e.message = notDecred; + throw e; + } + } throw e; }); } From fcee1a6493f2ab9305b2c1f225a13b4780792cad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 12:54:49 -0500 Subject: [PATCH 14/15] Bump electron from 23.3.8 to 23.3.13 (#3904) Bumps [electron](https://github.com/electron/electron) from 23.3.8 to 23.3.13. - [Release notes](https://github.com/electron/electron/releases) - [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md) - [Commits](https://github.com/electron/electron/compare/v23.3.8...v23.3.13) --- updated-dependencies: - dependency-name: electron dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2a0b26da71..43751a626d 100644 --- a/package.json +++ b/package.json @@ -239,7 +239,7 @@ "core-decorators": "^0.20.0", "cross-env": "^7.0.3", "css-loader": "5.2.7", - "electron": "23.3.8", + "electron": "23.3.13", "electron-builder": "22.14.13", "enzyme": "^3.11.0", "eslint": "7.32.0", diff --git a/yarn.lock b/yarn.lock index dfd57ecc84..dc81dbc9cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5781,10 +5781,10 @@ electron-to-chromium@^1.4.411: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.416.tgz#7291f704168d3842ae4da3ae9fdc7bfbeb97d116" integrity sha512-AUYh0XDTb2vrj0rj82jb3P9hHSyzQNdTPYWZIhPdCOui7/vpme7+HTE07BE5jwuqg/34TZ8ktlRz6GImJ4IXjA== -electron@23.3.8: - version "23.3.8" - resolved "https://registry.yarnpkg.com/electron/-/electron-23.3.8.tgz#7a8ea6bfa8dd747478f83e8043687b342272f74b" - integrity sha512-mxhKme3fUShiwOQR1HK8s/JXoiBfqq/jVZaEoWB4etFEiGTdRClXNo+y82eyY2zCOHFM0gVncZ7kdXiVtwvkzQ== +electron@23.3.13: + version "23.3.13" + resolved "https://registry.yarnpkg.com/electron/-/electron-23.3.13.tgz#bd2ae8eef83d1ed9504410fbe03598176c5f8817" + integrity sha512-BaXtHEb+KYKLouUXlUVDa/lj9pj4F5kiE0kwFdJV84Y2EU7euIDgPthfKtchhr5MVHmjtavRMIV/zAwEiSQ9rQ== dependencies: "@electron/get" "^2.0.0" "@types/node" "^16.11.26" From 360ce75a66722af8e1154b3504a2f49c2cb1854f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 13:10:50 -0500 Subject: [PATCH 15/15] Bump @grpc/grpc-js from 1.7.3 to 1.8.8 (#3888) Bumps [@grpc/grpc-js](https://github.com/grpc/grpc-node) from 1.7.3 to 1.8.8. - [Release notes](https://github.com/grpc/grpc-node/releases) - [Commits](https://github.com/grpc/grpc-node/compare/@grpc/grpc-js@1.7.3...@grpc/grpc-js@1.8.8) --- updated-dependencies: - dependency-name: "@grpc/grpc-js" dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 43751a626d..466fa35e02 100644 --- a/package.json +++ b/package.json @@ -271,7 +271,7 @@ "dependencies": { "@babel/eslint-parser": "7.19.1", "@formatjs/intl-utils": "^1.6.0", - "@grpc/grpc-js": "1.7.3", + "@grpc/grpc-js": "1.8.8", "@hot-loader/react-dom": "16.14.0", "@ledgerhq/hw-app-btc": "^10.0.5", "@ledgerhq/hw-transport-webusb": "6.27.1", diff --git a/yarn.lock b/yarn.lock index dc81dbc9cc..3de5e82cb4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1501,10 +1501,10 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@grpc/grpc-js@1.7.3": - version "1.7.3" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.7.3.tgz#f2ea79f65e31622d7f86d4b4c9ae38f13ccab99a" - integrity sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog== +"@grpc/grpc-js@1.8.8": + version "1.8.8" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.8.8.tgz#a7c6765d0302f47ba67c0ce3cb79718d6b028248" + integrity sha512-4gfDqMLXTrorvYTKA1jL22zLvVwiHJ73t6Re1OHwdCFRjdGTDOVtSJuaWhtHaivyeDGg0LeCkmU77MTKoV3wPA== dependencies: "@grpc/proto-loader" "^0.7.0" "@types/node" ">=12.12.47"