diff --git a/package.json b/package.json index 698d9d14c0..366cc59081 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "format:verify": "yarn prettier --check \"**/*.{js,ts,tsx,mdx,md,html,json}\"", "update-submodules": "./scripts/update-submodules.sh", "update-coins": "./scripts/update-coins.sh", - "update-protobuf": "yarn workspace @trezor/transport update:protobuf", + "update-protobuf": "yarn workspace @trezor/protobuf update:protobuf", "update-coinjoin-middleware": "yarn workspace @trezor/suite-data update-coinjoin-middleware", "prepare-release": "./scripts/prepare-release.sh", "_______ Aliases _______": "Aliases for longer commands which we often have to run manually. Names don't have to be pretty or make total sense.", diff --git a/packages/connect-explorer/package.json b/packages/connect-explorer/package.json index 0686218bcb..25ab0346bb 100644 --- a/packages/connect-explorer/package.json +++ b/packages/connect-explorer/package.json @@ -11,7 +11,7 @@ "dependencies": { "@trezor/components": "workspace:*", "@trezor/connect-web": "workspace:*", - "@trezor/transport": "workspace:*", + "@trezor/protobuf": "workspace:*", "markdown-it": "^13.0.1", "markdown-it-link-attributes": "^4.0.1", "markdown-it-replace-link": "^1.2.0", diff --git a/packages/connect-explorer/src/data/methods/cardano/common.ts b/packages/connect-explorer/src/data/methods/cardano/common.ts index af339aef1a..8e43342c88 100644 --- a/packages/connect-explorer/src/data/methods/cardano/common.ts +++ b/packages/connect-explorer/src/data/methods/cardano/common.ts @@ -1,7 +1,7 @@ import { CardanoNativeScriptHashDisplayFormat, CardanoTxSigningMode, -} from '@trezor/transport/lib/types/messages'; +} from '@trezor/protobuf/lib/messages'; export const cardanoDerivationType = { name: 'derivationType', diff --git a/packages/connect-explorer/src/data/methods/cardano/getAddress.ts b/packages/connect-explorer/src/data/methods/cardano/getAddress.ts index ba01e30549..99161dfcd0 100644 --- a/packages/connect-explorer/src/data/methods/cardano/getAddress.ts +++ b/packages/connect-explorer/src/data/methods/cardano/getAddress.ts @@ -1,4 +1,4 @@ -import { CardanoAddressType } from '@trezor/transport/lib/types/messages'; +import { CardanoAddressType } from '@trezor/protobuf/lib/messages'; import { cardanoDerivationType } from './common'; diff --git a/packages/connect-explorer/src/data/methods/cardano/getNativeScriptHash.ts b/packages/connect-explorer/src/data/methods/cardano/getNativeScriptHash.ts index 3f2f8d95ad..24056a53be 100644 --- a/packages/connect-explorer/src/data/methods/cardano/getNativeScriptHash.ts +++ b/packages/connect-explorer/src/data/methods/cardano/getNativeScriptHash.ts @@ -1,4 +1,4 @@ -import { CardanoNativeScriptType } from '@trezor/transport/lib/types/messages'; +import { CardanoNativeScriptType } from '@trezor/protobuf/lib/messages'; import { cardanoDerivationType, cardanoNativeScriptHashDisplayFormat } from './common'; diff --git a/packages/connect-explorer/tsconfig.json b/packages/connect-explorer/tsconfig.json index 1a52af9a32..bf99f45f44 100644 --- a/packages/connect-explorer/tsconfig.json +++ b/packages/connect-explorer/tsconfig.json @@ -12,7 +12,7 @@ "references": [ { "path": "../components" }, { "path": "../connect-web" }, - { "path": "../transport" } + { "path": "../protobuf" } ], "ts-node": { "compilerOptions": { diff --git a/packages/connect-iframe/webpack/prod.webpack.config.ts b/packages/connect-iframe/webpack/prod.webpack.config.ts index 78806ac2be..9b447156aa 100644 --- a/packages/connect-iframe/webpack/prod.webpack.config.ts +++ b/packages/connect-iframe/webpack/prod.webpack.config.ts @@ -5,7 +5,7 @@ import CopyWebpackPlugin from 'copy-webpack-plugin'; import TerserPlugin from 'terser-webpack-plugin'; const COMMON_DATA_SRC = '../../packages/connect-common/files'; -const MESSAGES_SRC = '../../packages/transport/messages.json'; +const MESSAGES_SRC = '../../packages/protobuf/messages.json'; const DIST = path.resolve(__dirname, '../build'); diff --git a/packages/connect/e2e/__fixtures__/cardanoGetAddress.ts b/packages/connect/e2e/__fixtures__/cardanoGetAddress.ts index fb21cee952..1dd29d90e9 100644 --- a/packages/connect/e2e/__fixtures__/cardanoGetAddress.ts +++ b/packages/connect/e2e/__fixtures__/cardanoGetAddress.ts @@ -1,5 +1,5 @@ import { NETWORK_IDS, PROTOCOL_MAGICS } from '../../src/constants/cardano'; -import { CardanoAddressType } from '@trezor/transport/lib/types/messages'; +import { CardanoAddressType } from '@trezor/protobuf/lib/messages'; const legacyResults = [ { diff --git a/packages/connect/e2e/__fixtures__/cardanoGetAddressDerivations.ts b/packages/connect/e2e/__fixtures__/cardanoGetAddressDerivations.ts index 085cf62d7e..f0f74b21d6 100644 --- a/packages/connect/e2e/__fixtures__/cardanoGetAddressDerivations.ts +++ b/packages/connect/e2e/__fixtures__/cardanoGetAddressDerivations.ts @@ -2,7 +2,7 @@ // @ts-ignore import commonFixtures from '../../../../submodules/trezor-common/tests/fixtures/cardano/get_base_address.derivations.json'; -import { CardanoAddressType, CardanoDerivationType } from '@trezor/transport/lib/types/messages'; +import { CardanoAddressType, CardanoDerivationType } from '@trezor/protobuf/lib/messages'; export default { method: 'cardanoGetAddress', diff --git a/packages/connect/e2e/__fixtures__/cardanoGetNativeScriptHash.ts b/packages/connect/e2e/__fixtures__/cardanoGetNativeScriptHash.ts index a209964165..3686deaae5 100644 --- a/packages/connect/e2e/__fixtures__/cardanoGetNativeScriptHash.ts +++ b/packages/connect/e2e/__fixtures__/cardanoGetNativeScriptHash.ts @@ -1,7 +1,7 @@ import { CardanoNativeScriptHashDisplayFormat, CardanoNativeScriptType, -} from '@trezor/transport/lib/types/messages'; +} from '@trezor/protobuf/lib/messages'; export default { method: 'cardanoGetNativeScriptHash', diff --git a/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts b/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts index 7f2f8677ac..87eb21aab1 100644 --- a/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts +++ b/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts @@ -6,7 +6,7 @@ import { CardanoCertificateType, CardanoTxOutputSerializationFormat, CardanoTxSigningMode, -} from '@trezor/transport/lib/types/messages'; +} from '@trezor/protobuf/lib/messages'; // vectors from https://github.com/trezor/trezor-firmware/tree/master/python/trezorlib/tests/device_tests/test_msg_cardano_sign_transaction.py diff --git a/packages/connect/src/api/ethereum/ethereumDefinitions.ts b/packages/connect/src/api/ethereum/ethereumDefinitions.ts index b3d0ea9286..c4d2e2596e 100644 --- a/packages/connect/src/api/ethereum/ethereumDefinitions.ts +++ b/packages/connect/src/api/ethereum/ethereumDefinitions.ts @@ -1,6 +1,6 @@ import fetch from 'cross-fetch'; -import { EthereumDefinitions } from '@trezor/transport/lib/types/messages'; +import { EthereumDefinitions } from '@trezor/protobuf/lib/messages'; /** * For given chainId and optionally contractAddress download ethereum definitions for transaction signing. diff --git a/packages/connect/src/api/ethereum/ethereumSignTx.ts b/packages/connect/src/api/ethereum/ethereumSignTx.ts index d9fe0fd027..48c295c69b 100644 --- a/packages/connect/src/api/ethereum/ethereumSignTx.ts +++ b/packages/connect/src/api/ethereum/ethereumSignTx.ts @@ -1,6 +1,6 @@ // origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/helpers/ethereumSignTx.js -import { EthereumDefinitions } from '@trezor/transport/lib/types/messages'; +import { EthereumDefinitions } from '@trezor/protobuf/lib/messages'; import { PROTO, ERRORS } from '../../constants'; import type { TypedCall } from '../../device/DeviceCommands'; import type { EthereumAccessList } from '../../types/api/ethereum'; diff --git a/packages/connect/src/constants/index.ts b/packages/connect/src/constants/index.ts index b7121f4e42..04f410f379 100644 --- a/packages/connect/src/constants/index.ts +++ b/packages/connect/src/constants/index.ts @@ -3,4 +3,4 @@ export * as NETWORK from './network'; export * as CARDANO from './cardano'; export * as NEM from './nem'; // NOTE: Protobuf file is intentionally exported directly -export * as PROTO from '@trezor/transport/lib/types/messages'; +export * as PROTO from '@trezor/protobuf/lib/messages'; diff --git a/packages/connect/src/types/device.ts b/packages/connect/src/types/device.ts index 1e6637ecef..44ab1272ee 100644 --- a/packages/connect/src/types/device.ts +++ b/packages/connect/src/types/device.ts @@ -70,7 +70,7 @@ export type UnreadableDevice = { export type Device = KnownDevice | UnknownDevice | UnreadableDevice; export type Features = PROTO.Features; -export { DeviceModelInternal } from '@trezor/transport/lib/types/messages'; +export { DeviceModelInternal } from '@trezor/protobuf/lib/messages'; type FeaturesNarrowing = | { diff --git a/packages/connect/src/utils/assetUtils.ts b/packages/connect/src/utils/assetUtils.ts index 947eae64fe..fc287abfad 100644 --- a/packages/connect/src/utils/assetUtils.ts +++ b/packages/connect/src/utils/assetUtils.ts @@ -12,7 +12,7 @@ export const getAssetByUrl = (url: string) => { case './data/firmware/2/releases.json': return require('@trezor/connect-common/files/firmware/2/releases.json'); case './data/messages/messages.json': - return require('@trezor/transport/messages.json'); + return require('@trezor/protobuf/messages.json'); default: return null; } diff --git a/packages/protobuf/.eslintrc.js b/packages/protobuf/.eslintrc.js new file mode 100644 index 0000000000..b201a1fde1 --- /dev/null +++ b/packages/protobuf/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + rules: { + '@typescript-eslint/ban-types': 'off', // allow {} in protobuf.d.ts + }, +}; diff --git a/packages/transport/messages.json b/packages/protobuf/messages.json similarity index 100% rename from packages/transport/messages.json rename to packages/protobuf/messages.json diff --git a/packages/protobuf/package.json b/packages/protobuf/package.json new file mode 100644 index 0000000000..3524f4f1fd --- /dev/null +++ b/packages/protobuf/package.json @@ -0,0 +1,35 @@ +{ + "name": "@trezor/protobuf", + "version": "1.0.0", + "license": "See LICENSE.md in repo root", + "sideEffects": false, + "main": "lib/index", + "files": [ + "lib/", + "!**/*.map", + "scripts/protobuf-build.sh", + "scripts/protobuf-patches", + "scripts/protobuf-types.js", + "messages.json" + ], + "scripts": { + "lint:js": "yarn g:eslint '**/*.{ts,tsx,js}'", + "test:unit": "jest -c ../../jest.config.base.js --passWithNoTests", + "type-check": "tsc --build", + "build:lib": "rimraf ./lib && yarn tsc --build tsconfig.lib.json", + "update:protobuf": "./scripts/protobuf-build.sh && npx prettier --write \"{messages.json,src/messages.ts}\"", + "prepublishOnly": "yarn tsx ../../scripts/prepublishNPM.js", + "prepublish": "yarn tsx ../../scripts/prepublish.js" + }, + "dependencies": { + "bytebuffer": "^5.0.1", + "long": "^4.0.0", + "protobufjs": "7.2.4" + }, + "devDependencies": { + "jest": "^29.5.0", + "rimraf": "^5.0.1", + "tsx": "^3.12.7", + "typescript": "4.9.5" + } +} diff --git a/packages/transport/scripts/protobuf-build.sh b/packages/protobuf/scripts/protobuf-build.sh similarity index 100% rename from packages/transport/scripts/protobuf-build.sh rename to packages/protobuf/scripts/protobuf-build.sh diff --git a/packages/transport/scripts/protobuf-patches/TxAck.js b/packages/protobuf/scripts/protobuf-patches/TxAck.js similarity index 100% rename from packages/transport/scripts/protobuf-patches/TxAck.js rename to packages/protobuf/scripts/protobuf-patches/TxAck.js diff --git a/packages/transport/scripts/protobuf-patches/TxInputType.js b/packages/protobuf/scripts/protobuf-patches/TxInputType.js similarity index 100% rename from packages/transport/scripts/protobuf-patches/TxInputType.js rename to packages/protobuf/scripts/protobuf-patches/TxInputType.js diff --git a/packages/transport/scripts/protobuf-patches/TxOutputType.js b/packages/protobuf/scripts/protobuf-patches/TxOutputType.js similarity index 100% rename from packages/transport/scripts/protobuf-patches/TxOutputType.js rename to packages/protobuf/scripts/protobuf-patches/TxOutputType.js diff --git a/packages/transport/scripts/protobuf-patches/index.js b/packages/protobuf/scripts/protobuf-patches/index.js similarity index 100% rename from packages/transport/scripts/protobuf-patches/index.js rename to packages/protobuf/scripts/protobuf-patches/index.js diff --git a/packages/transport/scripts/protobuf-types.js b/packages/protobuf/scripts/protobuf-types.js similarity index 98% rename from packages/transport/scripts/protobuf-types.js rename to packages/protobuf/scripts/protobuf-types.js index 0069a96824..2b9d5b8b11 100644 --- a/packages/transport/scripts/protobuf-types.js +++ b/packages/protobuf/scripts/protobuf-types.js @@ -198,7 +198,7 @@ export type TypedCall = ( `); // save to file -const filePath = path.join(__dirname, '../src/types/messages.ts'); +const filePath = path.join(__dirname, '../src/messages.ts'); fs.writeFile(filePath, lines.join('\n'), err => { if (err) return console.log(err); diff --git a/packages/transport/src/lowlevel/protobuf/decode.ts b/packages/protobuf/src/decode.ts similarity index 89% rename from packages/transport/src/lowlevel/protobuf/decode.ts rename to packages/protobuf/src/decode.ts index 153c2d7c33..ab639da66f 100644 --- a/packages/transport/src/lowlevel/protobuf/decode.ts +++ b/packages/protobuf/src/decode.ts @@ -1,6 +1,7 @@ import ByteBuffer from 'bytebuffer'; import { Type, Message, Field } from 'protobufjs/light'; -import { isPrimitiveField } from '../../utils/protobuf'; + +import { isPrimitiveField } from './utils'; const transform = (field: Field, value: any) => { if (isPrimitiveField(field.type)) { @@ -41,9 +42,12 @@ const transform = (field: Field, value: any) => { throw new Error(`transport: decode: case not handled: ${field}`); }; -function messageToJSON(Message: Message>, fields: Type['fields']) { +export function messageToJSON( + MessageParam: Message>, + fields: Type['fields'], +) { // get rid of Message.prototype references - const { ...message } = Message; + const { ...message } = MessageParam; const res: { [key: string]: any } = {}; Object.keys(fields).forEach(key => { @@ -61,10 +65,10 @@ function messageToJSON(Message: Message>, fields: Type[' return res; } -export const decode = (Message: Type, data: ByteBuffer) => { +export const decode = (MessageParam: Type, data: ByteBuffer) => { const buff = data.toBuffer(); const a = new Uint8Array(buff); - const decoded = Message.decode(a); + const decoded = MessageParam.decode(a); // [compatibility]: in the end it should be possible to get rid of messageToJSON method and call // Message.toObject(decoded) to return result as plain javascript object. This method should be able to do diff --git a/packages/transport/src/lowlevel/protobuf/encode.ts b/packages/protobuf/src/encode.ts similarity index 97% rename from packages/transport/src/lowlevel/protobuf/encode.ts rename to packages/protobuf/src/encode.ts index 0d0bb2efea..179bffc47d 100644 --- a/packages/transport/src/lowlevel/protobuf/encode.ts +++ b/packages/protobuf/src/encode.ts @@ -1,7 +1,7 @@ import ByteBuffer from 'bytebuffer'; import { Type } from 'protobufjs/light'; -import { isPrimitiveField } from '../../utils/protobuf'; +import { isPrimitiveField } from './utils'; const transform = (fieldType: string, value: any) => { if (fieldType === 'bytes') { diff --git a/packages/protobuf/src/index.ts b/packages/protobuf/src/index.ts new file mode 100644 index 0000000000..a60aa3ab7d --- /dev/null +++ b/packages/protobuf/src/index.ts @@ -0,0 +1,5 @@ +export * from './decode'; +export * from './encode'; +export * as Messages from './messages'; +export * from './types'; +export { parseConfigure, createMessageFromName, createMessageFromType } from './utils'; diff --git a/packages/transport/src/types/messages.ts b/packages/protobuf/src/messages.ts similarity index 100% rename from packages/transport/src/types/messages.ts rename to packages/protobuf/src/messages.ts diff --git a/packages/protobuf/src/types.ts b/packages/protobuf/src/types.ts new file mode 100644 index 0000000000..70353e2e5b --- /dev/null +++ b/packages/protobuf/src/types.ts @@ -0,0 +1,6 @@ +import * as Messages from './messages'; + +export type MessageFromTrezor = { + type: keyof Messages.MessageType; + message: Record; +}; diff --git a/packages/transport/src/lowlevel/protobuf/messages.ts b/packages/protobuf/src/utils.ts similarity index 57% rename from packages/transport/src/lowlevel/protobuf/messages.ts rename to packages/protobuf/src/utils.ts index 19c2a31a88..e625d50c7d 100644 --- a/packages/transport/src/lowlevel/protobuf/messages.ts +++ b/packages/protobuf/src/utils.ts @@ -1,7 +1,40 @@ // Module for loading the protobuf description from serialized description import * as protobuf from 'protobufjs/light'; -import type { MessageFromTrezor } from '../../types'; + +import type { MessageFromTrezor } from './types'; + +const primitiveTypes = [ + 'bool', + 'string', + 'bytes', + 'int32', + 'int64', + 'uint32', + 'uint64', + 'sint32', + 'sint64', + 'fixed32', + 'fixed64', + 'sfixed32', + 'sfixed64', + 'double', + 'float', +]; + +/** + * Determines whether given field is "primitive" + * bool, strings, uint32 => true + * HDNodeType => false + */ +export const isPrimitiveField = (field: any) => primitiveTypes.includes(field); + +export function parseConfigure(data: protobuf.INamespace) { + if (typeof data === 'string') { + return protobuf.Root.fromJSON(JSON.parse(data)); + } + return protobuf.Root.fromJSON(data); +} export const createMessageFromName = (messages: protobuf.Root, name: string) => { const Message = messages.lookupType(name); diff --git a/packages/transport/tests/encode-decode-basic.test.ts b/packages/protobuf/tests/encode-decode-basic.test.ts similarity index 98% rename from packages/transport/tests/encode-decode-basic.test.ts rename to packages/protobuf/tests/encode-decode-basic.test.ts index c45e90578f..9a22605bd3 100644 --- a/packages/transport/tests/encode-decode-basic.test.ts +++ b/packages/protobuf/tests/encode-decode-basic.test.ts @@ -1,7 +1,7 @@ import * as ProtoBuf from 'protobufjs/light'; -import { encode } from '../src/lowlevel/protobuf/encode'; -import { decode } from '../src/lowlevel/protobuf/decode'; +import { encode } from '../src/encode'; +import { decode } from '../src/decode'; const messages = { nested: { @@ -232,6 +232,7 @@ describe('basic concepts', () => { }); test('Different protobuf between receiving ends', () => { + /* eslint-disable-next-line @typescript-eslint/no-shadow */ const messages = { nested: { messages: { diff --git a/packages/transport/tests/encode-decode.test.ts b/packages/protobuf/tests/encode-decode.test.ts similarity index 99% rename from packages/transport/tests/encode-decode.test.ts rename to packages/protobuf/tests/encode-decode.test.ts index 0db2849d2a..e2a0d27d19 100644 --- a/packages/transport/tests/encode-decode.test.ts +++ b/packages/protobuf/tests/encode-decode.test.ts @@ -1,7 +1,7 @@ import * as ProtoBuf from 'protobufjs/light'; -import { encode } from '../src/lowlevel/protobuf/encode'; -import { decode } from '../src/lowlevel/protobuf/decode'; +import { encode } from '../src/encode'; +import { decode } from '../src/decode'; const HDNodeType = { fields: { diff --git a/packages/transport/tests/messages.test.ts b/packages/protobuf/tests/messages.test.ts similarity index 96% rename from packages/transport/tests/messages.test.ts rename to packages/protobuf/tests/messages.test.ts index 0df9f13958..b734a37a25 100644 --- a/packages/transport/tests/messages.test.ts +++ b/packages/protobuf/tests/messages.test.ts @@ -1,6 +1,6 @@ import * as protobuf from 'protobufjs/light'; -import { createMessageFromName } from '../src/lowlevel/protobuf/messages'; +import { createMessageFromName } from '../src/utils'; const json = { nested: { diff --git a/packages/protobuf/tsconfig.json b/packages/protobuf/tsconfig.json new file mode 100644 index 0000000000..b88e01405a --- /dev/null +++ b/packages/protobuf/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { "outDir": "libDev" }, + "include": ["src"], + "references": [] +} diff --git a/packages/protobuf/tsconfig.lib.json b/packages/protobuf/tsconfig.lib.json new file mode 100644 index 0000000000..7358953497 --- /dev/null +++ b/packages/protobuf/tsconfig.lib.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.lib.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["./src"] +} diff --git a/packages/protocol/package.json b/packages/protocol/package.json new file mode 100644 index 0000000000..46356dd8aa --- /dev/null +++ b/packages/protocol/package.json @@ -0,0 +1,24 @@ +{ + "name": "@trezor/protocol", + "version": "1.0.0", + "license": "See LICENSE.md in repo root", + "sideEffects": false, + "main": "lib/index", + "scripts": { + "lint:js": "yarn g:eslint '**/*.{ts,tsx,js}'", + "test:unit": "jest -c ../../jest.config.base.js --passWithNoTests", + "type-check": "tsc --build", + "build:lib": "rimraf ./lib && yarn tsc --build tsconfig.lib.json", + "prepublishOnly": "yarn tsx ../../scripts/prepublishNPM.js", + "prepublish": "yarn tsx ../../scripts/prepublish.js" + }, + "dependencies": { + "bytebuffer": "^5.0.1" + }, + "devDependencies": { + "jest": "^29.5.0", + "rimraf": "^5.0.1", + "tsx": "^3.12.7", + "typescript": "4.9.5" + } +} diff --git a/packages/protocol/src/errors.ts b/packages/protocol/src/errors.ts new file mode 100644 index 0000000000..b70129b040 --- /dev/null +++ b/packages/protocol/src/errors.ts @@ -0,0 +1 @@ +export const PROTOCOL_MALFORMED = 'Malformed protocol format' as const; diff --git a/packages/protocol/src/index.ts b/packages/protocol/src/index.ts new file mode 100644 index 0000000000..7e3626e3a3 --- /dev/null +++ b/packages/protocol/src/index.ts @@ -0,0 +1,3 @@ +export * as v1 from './protocol-v1'; +export * as trzd from './protocol-trzd'; +export * from './errors'; diff --git a/packages/protocol/src/protocol-trzd/decode.ts b/packages/protocol/src/protocol-trzd/decode.ts new file mode 100644 index 0000000000..c1a3b28155 --- /dev/null +++ b/packages/protocol/src/protocol-trzd/decode.ts @@ -0,0 +1,18 @@ +import ByteBuffer from 'bytebuffer'; + +export const decode = (bytes: ArrayBuffer) => { + const byteBuffer = ByteBuffer.wrap(bytes); + const magic = byteBuffer.readBytes(5).toUTF8(); + const definitionType = byteBuffer.readUint8(); + const dataVersion = byteBuffer.readUint32(); + const protobufLength = byteBuffer.readUint8(); + const protobufPayload = byteBuffer.slice(12, 12 + protobufLength); + + return { + magic, + definitionType, + dataVersion, + protobufLength, + protobufPayload, + }; +}; diff --git a/packages/transport/src/lowlevel/protocol/index.ts b/packages/protocol/src/protocol-trzd/index.ts similarity index 50% rename from packages/transport/src/lowlevel/protocol/index.ts rename to packages/protocol/src/protocol-trzd/index.ts index 6267e16e3d..92fccb94c3 100644 --- a/packages/transport/src/lowlevel/protocol/index.ts +++ b/packages/protocol/src/protocol-trzd/index.ts @@ -1,2 +1 @@ export * from './decode'; -export * from './encode'; diff --git a/packages/protocol/src/protocol-v1/constants.ts b/packages/protocol/src/protocol-v1/constants.ts new file mode 100644 index 0000000000..9ac6534bba --- /dev/null +++ b/packages/protocol/src/protocol-v1/constants.ts @@ -0,0 +1,3 @@ +export const MESSAGE_HEADER_BYTE = 0x23; +export const HEADER_SIZE = 1 + 1 + 4 + 2; +export const BUFFER_SIZE = 63; diff --git a/packages/transport/src/lowlevel/protocol/decode.ts b/packages/protocol/src/protocol-v1/decode.ts similarity index 93% rename from packages/transport/src/lowlevel/protocol/decode.ts rename to packages/protocol/src/protocol-v1/decode.ts index 139a6743ba..9d669dccbc 100644 --- a/packages/transport/src/lowlevel/protocol/decode.ts +++ b/packages/protocol/src/protocol-v1/decode.ts @@ -1,6 +1,7 @@ import ByteBuffer from 'bytebuffer'; -import { MESSAGE_HEADER_BYTE } from '../../constants'; -import * as ERRORS from '../../errors'; + +import * as ERRORS from '../errors'; +import { MESSAGE_HEADER_BYTE } from './constants'; /** * Reads meta information from buffer diff --git a/packages/transport/src/lowlevel/protocol/encode.ts b/packages/protocol/src/protocol-v1/encode.ts similarity index 98% rename from packages/transport/src/lowlevel/protocol/encode.ts rename to packages/protocol/src/protocol-v1/encode.ts index d84383ad80..517fc1342b 100644 --- a/packages/transport/src/lowlevel/protocol/encode.ts +++ b/packages/protocol/src/protocol-v1/encode.ts @@ -1,6 +1,6 @@ import ByteBuffer from 'bytebuffer'; -import { HEADER_SIZE, MESSAGE_HEADER_BYTE, BUFFER_SIZE } from '../../constants'; +import { HEADER_SIZE, MESSAGE_HEADER_BYTE, BUFFER_SIZE } from './constants'; type Options = { chunked: Chunked; diff --git a/packages/transport/src/lowlevel/protobuf/index.ts b/packages/protocol/src/protocol-v1/index.ts similarity index 100% rename from packages/transport/src/lowlevel/protobuf/index.ts rename to packages/protocol/src/protocol-v1/index.ts diff --git a/packages/protocol/tsconfig.json b/packages/protocol/tsconfig.json new file mode 100644 index 0000000000..4c175bbc3c --- /dev/null +++ b/packages/protocol/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { "outDir": "libDev" }, + "references": [] +} diff --git a/packages/protocol/tsconfig.lib.json b/packages/protocol/tsconfig.lib.json new file mode 100644 index 0000000000..7358953497 --- /dev/null +++ b/packages/protocol/tsconfig.lib.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.lib.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["./src"] +} diff --git a/packages/transport/.eslintrc.js b/packages/transport/.eslintrc.js index 5dfa7b8add..dd1f8774d9 100644 --- a/packages/transport/.eslintrc.js +++ b/packages/transport/.eslintrc.js @@ -4,7 +4,7 @@ module.exports = { 'no-underscore-dangle': 'off', 'no-restricted-syntax': 'off', 'no-await-in-loop': 'off', - '@typescript-eslint/ban-types': 'off', // allow {} in protobuf.d.ts + '@typescript-eslint/ban-types': 'off', // still one file that needs refactoring 'react-hooks/rules-of-hooks': 'off', // there is no react here }, }; diff --git a/packages/transport/package.json b/packages/transport/package.json index d9eaac90c1..f50ac9256e 100644 --- a/packages/transport/package.json +++ b/packages/transport/package.json @@ -24,11 +24,7 @@ }, "files": [ "lib/", - "!**/*.map", - "scripts/protobuf-build.sh", - "scripts/protobuf-patches", - "scripts/protobuf-types.js", - "messages.json" + "!**/*.map" ], "scripts": { "lint:js": "yarn g:eslint '**/*.{ts,tsx,js}'", @@ -38,7 +34,6 @@ "test:unit": "jest", "test:e2e": "ts-node -O '{\"module\": \"commonjs\", \"esModuleInterop\": true}' ./e2e/run.ts", "example:bridge": "jest --verbose -c jest.config.e2e.js --testPathPattern bridge.integration", - "update:protobuf": "./scripts/protobuf-build.sh && yarn prettier --write \"{messages.json,src/types/messages.ts}\"", "prepublishOnly": "yarn tsx ../../scripts/prepublishNPM.js", "prepublish": "yarn tsx ../../scripts/prepublish.js" }, @@ -55,12 +50,13 @@ "typescript": "4.9.5" }, "dependencies": { + "@trezor/protobuf": "workspace:*", + "@trezor/protocol": "workspace:*", "@trezor/utils": "workspace:*", "bytebuffer": "^5.0.1", "cross-fetch": "^3.1.6", "json-stable-stringify": "^1.0.2", "long": "^4.0.0", - "prettier": "2.8.8", "protobufjs": "7.2.4", "usb": "^2.9.0" } diff --git a/packages/transport/src/errors.ts b/packages/transport/src/errors.ts index 2edb5aca12..740e69859c 100644 --- a/packages/transport/src/errors.ts +++ b/packages/transport/src/errors.ts @@ -88,4 +88,3 @@ export const ABORTED_BY_SIGNAL = 'Aborted by signal' as const; * see scheduleAction */ export const ABORTED_BY_TIMEOUT = 'Aborted by timeout' as const; -export const PROTOCOL_MALFORMED = 'Malformed protocol format' as const; diff --git a/packages/transport/src/index.ts b/packages/transport/src/index.ts index c29c9d6412..cd7fb601cb 100644 --- a/packages/transport/src/index.ts +++ b/packages/transport/src/index.ts @@ -11,14 +11,14 @@ export * as TRANSPORT_ERROR from './errors'; protobuf.util.Long = Long; protobuf.configure(); -export type { MessageFromTrezor, Descriptor } from './types'; +export type { Descriptor } from './types'; export { TREZOR_USB_DESCRIPTORS, TRANSPORT } from './constants'; export { AbstractTransport as Transport } from './transports/abstract'; // messages are exported but there is no real need to use them elsewhere // transports have reference to this already -export * as Messages from './types/messages'; +export { Messages } from '@trezor/protobuf'; // browser + node export { BridgeTransport } from './transports/bridge'; diff --git a/packages/transport/src/lowlevel/receive.ts b/packages/transport/src/lowlevel/receive.ts index 50c8321a18..3f508aa167 100644 --- a/packages/transport/src/lowlevel/receive.ts +++ b/packages/transport/src/lowlevel/receive.ts @@ -1,16 +1,15 @@ import ByteBuffer from 'bytebuffer'; import { Root } from 'protobufjs/light'; -import * as decodeProtobuf from './protobuf/decode'; -import * as decodeProtocol from './protocol/decode'; -import { createMessageFromType } from './protobuf/messages'; +import { decode as decodeProtobuf, createMessageFromType } from '@trezor/protobuf'; +import { v1 as protocolV1 } from '@trezor/protocol'; export function receiveOne(messages: Root, data: string) { const bytebuffer = ByteBuffer.wrap(data, 'hex'); - const { typeId, buffer } = decodeProtocol.decode(bytebuffer); + const { typeId, buffer } = protocolV1.decode(bytebuffer); const { Message, messageName } = createMessageFromType(messages, typeId); - const message = decodeProtobuf.decode(Message, buffer); + const message = decodeProtobuf(Message, buffer); return { message, type: messageName, @@ -38,7 +37,7 @@ async function receiveRest( async function receiveBuffer(receiver: () => Promise) { const data = await receiver(); - const { length, typeId, restBuffer } = decodeProtocol.decodeChunked(data); + const { length, typeId, restBuffer } = protocolV1.decodeChunked(data); const decoded = new ByteBuffer(length); if (length) { decoded.append(restBuffer); @@ -51,7 +50,7 @@ export async function receiveAndParse(messages: Root, receiver: () => Promise Promise, buffers: Array) { @@ -19,7 +18,7 @@ export function buildOne(messages: Root, name: string, data: Record) => { const { Message, messageType } = createMessageFromName(messages, name); const buffer = encodeProtobuf(Message, data); - return encodeProtocol(buffer, { + return protocolV1.encode(buffer, { addTrezorHeaders: true, chunked: true, messageType, diff --git a/packages/transport/src/transports/abstract.ts b/packages/transport/src/transports/abstract.ts index e28fa25478..3b703c5416 100644 --- a/packages/transport/src/transports/abstract.ts +++ b/packages/transport/src/transports/abstract.ts @@ -1,9 +1,10 @@ import * as protobuf from 'protobufjs/light'; import { scheduleAction, ScheduleActionParams, ScheduledAction, Deferred } from '@trezor/utils'; import { TypedEmitter } from '@trezor/utils/lib/typedEventEmitter'; +import { PROTOCOL_MALFORMED } from '@trezor/protocol'; +import { MessageFromTrezor } from '@trezor/protobuf'; import { - MessageFromTrezor, Session, Descriptor, AbortableCall, @@ -256,7 +257,7 @@ export abstract class AbstractTransport extends TypedEmitter<{ | typeof ERRORS.WRONG_RESULT_TYPE | typeof ERRORS.OTHER_CALL_IN_PROGRESS // webusb + bridge - | typeof ERRORS.PROTOCOL_MALFORMED + | typeof PROTOCOL_MALFORMED | typeof ERRORS.UNEXPECTED_ERROR | typeof ERRORS.SESSION_NOT_FOUND | typeof ERRORS.ABORTED_BY_TIMEOUT @@ -274,7 +275,7 @@ export abstract class AbstractTransport extends TypedEmitter<{ | typeof ERRORS.WRONG_RESULT_TYPE | typeof ERRORS.OTHER_CALL_IN_PROGRESS // webusb + bridge - | typeof ERRORS.PROTOCOL_MALFORMED + | typeof PROTOCOL_MALFORMED | typeof ERRORS.DEVICE_DISCONNECTED_DURING_ACTION | typeof ERRORS.UNEXPECTED_ERROR | typeof ERRORS.SESSION_NOT_FOUND @@ -302,7 +303,7 @@ export abstract class AbstractTransport extends TypedEmitter<{ | typeof ERRORS.OTHER_CALL_IN_PROGRESS // webusb + bridge | typeof ERRORS.DEVICE_DISCONNECTED_DURING_ACTION - | typeof ERRORS.PROTOCOL_MALFORMED + | typeof PROTOCOL_MALFORMED | typeof ERRORS.UNEXPECTED_ERROR | typeof ERRORS.ABORTED_BY_TIMEOUT | typeof ERRORS.ABORTED_BY_SIGNAL diff --git a/packages/transport/src/transports/bridge.ts b/packages/transport/src/transports/bridge.ts index 98c956890d..4c4f3f8044 100644 --- a/packages/transport/src/transports/bridge.ts +++ b/packages/transport/src/transports/bridge.ts @@ -1,5 +1,5 @@ import { versionUtils, createDeferred, Deferred, createTimeoutPromise } from '@trezor/utils'; - +import { PROTOCOL_MALFORMED } from '@trezor/protocol'; import { bridgeApiCall } from '../utils/bridgeApiCall'; import * as bridgeApiResult from '../utils/bridgeApiResult'; import { buildOne } from '../lowlevel/send'; @@ -318,7 +318,7 @@ export class BridgeTransport extends AbstractTransport { string, | BridgeCommonErrors | typeof ERRORS.DEVICE_DISCONNECTED_DURING_ACTION - | typeof ERRORS.PROTOCOL_MALFORMED + | typeof PROTOCOL_MALFORMED | typeof ERRORS.OTHER_CALL_IN_PROGRESS >; private async _post( @@ -332,7 +332,7 @@ export class BridgeTransport extends AbstractTransport { string, | BridgeCommonErrors | typeof ERRORS.DEVICE_DISCONNECTED_DURING_ACTION - | typeof ERRORS.PROTOCOL_MALFORMED + | typeof PROTOCOL_MALFORMED | typeof ERRORS.OTHER_CALL_IN_PROGRESS >; private async _post( @@ -342,7 +342,7 @@ export class BridgeTransport extends AbstractTransport { string, | BridgeCommonErrors | typeof ERRORS.DEVICE_DISCONNECTED_DURING_ACTION - | typeof ERRORS.PROTOCOL_MALFORMED + | typeof PROTOCOL_MALFORMED | typeof ERRORS.OTHER_CALL_IN_PROGRESS >; private async _post( @@ -390,6 +390,7 @@ export class BridgeTransport extends AbstractTransport { ERRORS.SESSION_NOT_FOUND, ERRORS.DEVICE_DISCONNECTED_DURING_ACTION, ERRORS.OTHER_CALL_IN_PROGRESS, + PROTOCOL_MALFORMED, ]); case '/enumerate': case '/listen': @@ -399,6 +400,7 @@ export class BridgeTransport extends AbstractTransport { ERRORS.SESSION_NOT_FOUND, ERRORS.DEVICE_DISCONNECTED_DURING_ACTION, ERRORS.OTHER_CALL_IN_PROGRESS, + PROTOCOL_MALFORMED, ]); case '/release': return this.unknownError(response.error, [ diff --git a/packages/transport/src/types/apiCall.ts b/packages/transport/src/types/apiCall.ts index 27467ca46a..0cdab1842d 100644 --- a/packages/transport/src/types/apiCall.ts +++ b/packages/transport/src/types/apiCall.ts @@ -1,6 +1,8 @@ +import { PROTOCOL_MALFORMED } from '@trezor/protocol'; + import * as ERRORS from '../errors'; -export type AnyError = (typeof ERRORS)[keyof typeof ERRORS]; +export type AnyError = (typeof ERRORS)[keyof typeof ERRORS] | typeof PROTOCOL_MALFORMED; export interface Success { success: true; diff --git a/packages/transport/src/types/index.ts b/packages/transport/src/types/index.ts index 425e704959..0038235102 100644 --- a/packages/transport/src/types/index.ts +++ b/packages/transport/src/types/index.ts @@ -1,12 +1,5 @@ -import * as Messages from './messages'; - export * from './apiCall'; -export type MessageFromTrezor = { - type: keyof Messages.MessageType; - message: Record; -}; - export type Session = null | string; export type Descriptor = { path: string; session?: Session }; diff --git a/packages/transport/src/utils/bridgeApiCall.ts b/packages/transport/src/utils/bridgeApiCall.ts index d9997ffe56..e54884f950 100644 --- a/packages/transport/src/utils/bridgeApiCall.ts +++ b/packages/transport/src/utils/bridgeApiCall.ts @@ -4,6 +4,8 @@ import { success, error, unknownError } from './result'; import * as ERRORS from '../errors'; +import { PROTOCOL_MALFORMED } from '@trezor/protocol'; + export type HttpRequestOptions = { body?: Array | Record | string; url: string; @@ -97,10 +99,10 @@ export async function bridgeApiCall(options: HttpRequestOptions) { return error({ error: ERRORS.INTERFACE_UNABLE_TO_OPEN_DEVICE }); } if (errStr === BRIDGE_MALFORMED_PROTOBUF) { - return error({ error: ERRORS.PROTOCOL_MALFORMED }); + return error({ error: PROTOCOL_MALFORMED }); } if (errStr === BRIDGE_MALFORMED_WIRE_FORMAT) { - return error({ error: ERRORS.PROTOCOL_MALFORMED }); + return error({ error: PROTOCOL_MALFORMED }); } return unknownError(new Error(errStr), [ ERRORS.DEVICE_NOT_FOUND, diff --git a/packages/transport/src/utils/protobuf.ts b/packages/transport/src/utils/protobuf.ts deleted file mode 100644 index 05808c6cfc..0000000000 --- a/packages/transport/src/utils/protobuf.ts +++ /dev/null @@ -1,24 +0,0 @@ -const primitiveTypes = [ - 'bool', - 'string', - 'bytes', - 'int32', - 'int64', - 'uint32', - 'uint64', - 'sint32', - 'sint64', - 'fixed32', - 'fixed64', - 'sfixed32', - 'sfixed64', - 'double', - 'float', -]; - -/** - * Determines whether given field is "primitive" - * bool, strings, uint32 => true - * HDNodeType => false - */ -export const isPrimitiveField = (field: any) => primitiveTypes.includes(field); diff --git a/packages/transport/tests/abstractUsb.test.ts b/packages/transport/tests/abstractUsb.test.ts index 57e35ef81f..59099e08d0 100644 --- a/packages/transport/tests/abstractUsb.test.ts +++ b/packages/transport/tests/abstractUsb.test.ts @@ -3,7 +3,7 @@ import { AbstractUsbTransport, UsbTransportConstructorParams } from '../src/tran import { UsbInterface } from '../src/interfaces/usb'; import { SessionsClient } from '../src/sessions/client'; import { SessionsBackground } from '../src/sessions/background'; -import * as messages from '../messages.json'; +import * as messages from '@trezor/protobuf/messages.json'; // we cant directly use abstract class (UsbTransport) class TestUsbTransport extends AbstractUsbTransport { diff --git a/packages/transport/tsconfig.json b/packages/transport/tsconfig.json index 672a627809..2ceb46e7ca 100644 --- a/packages/transport/tsconfig.json +++ b/packages/transport/tsconfig.json @@ -6,6 +6,8 @@ }, "include": ["src"], "references": [ + { "path": "../protobuf" }, + { "path": "../protocol" }, { "path": "../utils" }, { "path": "../trezor-user-env-link" } ] diff --git a/yarn.lock b/yarn.lock index 599831c040..b88c0c7ed9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7883,7 +7883,7 @@ __metadata: dependencies: "@trezor/components": "workspace:*" "@trezor/connect-web": "workspace:*" - "@trezor/transport": "workspace:*" + "@trezor/protobuf": "workspace:*" "@types/markdown-it": ^12.2.3 "@types/markdown-it-link-attributes": ^3.0.1 "@types/react-router": ^5.1.20 @@ -8067,6 +8067,8 @@ __metadata: "@trezor/blockchain-link-types": "workspace:*" "@trezor/connect-analytics": "workspace:*" "@trezor/connect-common": "workspace:*" + "@trezor/protobuf": "workspace:*" + "@trezor/protocol": "workspace:*" "@trezor/transport": "workspace:*" "@trezor/trezor-user-env-link": "workspace:*" "@trezor/utils": "workspace:*" @@ -8173,6 +8175,32 @@ __metadata: languageName: unknown linkType: soft +"@trezor/protobuf@workspace:*, @trezor/protobuf@workspace:packages/protobuf": + version: 0.0.0-use.local + resolution: "@trezor/protobuf@workspace:packages/protobuf" + dependencies: + bytebuffer: ^5.0.1 + jest: ^29.5.0 + long: ^4.0.0 + protobufjs: 7.2.4 + rimraf: ^5.0.1 + tsx: ^3.12.7 + typescript: 4.9.5 + languageName: unknown + linkType: soft + +"@trezor/protocol@workspace:*, @trezor/protocol@workspace:packages/protocol": + version: 0.0.0-use.local + resolution: "@trezor/protocol@workspace:packages/protocol" + dependencies: + bytebuffer: ^5.0.1 + jest: ^29.5.0 + rimraf: ^5.0.1 + tsx: ^3.12.7 + typescript: 4.9.5 + languageName: unknown + linkType: soft + "@trezor/react-utils@workspace:*, @trezor/react-utils@workspace:packages/react-utils": version: 0.0.0-use.local resolution: "@trezor/react-utils@workspace:packages/react-utils" @@ -8727,6 +8755,8 @@ __metadata: version: 0.0.0-use.local resolution: "@trezor/transport@workspace:packages/transport" dependencies: + "@trezor/protobuf": "workspace:*" + "@trezor/protocol": "workspace:*" "@trezor/trezor-user-env-link": "workspace:*" "@trezor/utils": "workspace:*" "@types/bytebuffer": ^5.0.44 @@ -8738,7 +8768,6 @@ __metadata: jest-environment-jsdom: ^29.5.0 json-stable-stringify: ^1.0.2 long: ^4.0.0 - prettier: 2.8.8 protobufjs: 7.2.4 rimraf: ^5.0.1 ts-node: ^10.9.1