diff --git a/.github/workflows/build-and-test-package.yml b/.github/workflows/build-and-test-package.yml index f13de10..c7701f4 100644 --- a/.github/workflows/build-and-test-package.yml +++ b/.github/workflows/build-and-test-package.yml @@ -28,3 +28,6 @@ jobs: - run: pnpm install --frozen-lockfile - run: pnpm run build - run: pnpm test:unit + - run: pnpm test:format + - run: pnpm test:lint + - run: pnpm cov:check diff --git a/biome.json b/biome.json index 2374fc0..7aec70e 100644 --- a/biome.json +++ b/biome.json @@ -13,7 +13,8 @@ "enabled": true }, "formatter": { - "indentStyle": "space" + "indentStyle": "space", + "lineWidth": 90 }, "javascript": { "formatter": { diff --git a/package.json b/package.json index 90538ee..806a573 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,10 @@ "access": "public" }, "dependencies": { - "@fleet-sdk/core": "0.1.0-alpha.12", - "base-x": "^4.0.0", + "@fleet-sdk/common": "^0.4.1", + "@fleet-sdk/core": "0.5.0", + "@fleet-sdk/crypto": "^0.5.0", + "base-x": "^5.0.0", "bip32-path": "^0.4.2" }, "devDependencies": { @@ -66,11 +68,10 @@ "@ledgerhq/hw-transport": "^6.31.0", "@ledgerhq/hw-transport-mocker": "^6.29.0", "@types/node": "^20.14.9", - "@types/typescript": "^2.0.0", "@vitest/coverage-v8": "^1.6.0", "open-cli": "^8.0.0", "tsup": "^8.1.0", - "typescript": "^5.5.2", + "typescript": "^5.5.3", "vitest": "^1.6.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81a12f7..e579a8f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,18 @@ importers: .: dependencies: + '@fleet-sdk/common': + specifier: ^0.4.1 + version: 0.4.1 '@fleet-sdk/core': - specifier: 0.1.0-alpha.12 - version: 0.1.0-alpha.12 + specifier: 0.5.0 + version: 0.5.0 + '@fleet-sdk/crypto': + specifier: ^0.5.0 + version: 0.5.0 base-x: - specifier: ^4.0.0 - version: 4.0.0 + specifier: ^5.0.0 + version: 5.0.0 bip32-path: specifier: ^0.4.2 version: 0.4.2 @@ -30,9 +36,6 @@ importers: '@types/node': specifier: ^20.14.9 version: 20.14.9 - '@types/typescript': - specifier: ^2.0.0 - version: 2.0.0 '@vitest/coverage-v8': specifier: ^1.6.0 version: 1.6.0(vitest@1.6.0(@types/node@20.14.9)) @@ -41,10 +44,10 @@ importers: version: 8.0.0 tsup: specifier: ^8.1.0 - version: 8.1.0(postcss@8.4.39)(typescript@5.5.2) + version: 8.1.0(postcss@8.4.39)(typescript@5.5.3) typescript: - specifier: ^5.5.2 - version: 5.5.2 + specifier: ^5.5.3 + version: 5.5.3 vitest: specifier: ^1.6.0 version: 1.6.0(@types/node@20.14.9) @@ -266,9 +269,21 @@ packages: cpu: [x64] os: [win32] - '@fleet-sdk/core@0.1.0-alpha.12': - resolution: {integrity: sha512-pBrMcygKe2WNcEDR/ucJU/0Gb2VVmuHdBXnScdX4mnf8fZwdqRfO/YsXew2Z/fR8eGwB+KhPpteOhzamx8ZzoA==} - engines: {node: '>=10'} + '@fleet-sdk/common@0.4.1': + resolution: {integrity: sha512-jivyBz7kAye1FrS2gDlZkVpu7rrWAw1aySX+OkIWJA703P9BrrAEmBGU0jz4tKL+3LY2CsJhulOlpSAuc3/ivQ==} + engines: {node: '>=14'} + + '@fleet-sdk/core@0.5.0': + resolution: {integrity: sha512-uPWd3p4E+Feh1qXYe56WwSd98uB/bDzjziKOJWZBD1NJ2gd0F5TN+w9p31tp65xALAjZNRrztuslULCGbZOAZA==} + engines: {node: '>=18'} + + '@fleet-sdk/crypto@0.5.0': + resolution: {integrity: sha512-ktfhO8r/SFYQfz3QcPWvRkvm5qZHLMT/zXXs4jVIbt7jQUSXvEX5f90YzO1n6eqs3G3xeb/fg0wMI5dYb4QXMA==} + engines: {node: '>=18'} + + '@fleet-sdk/serializer@0.5.0': + resolution: {integrity: sha512-pQsebhoJKhdmj9jZSh5pqwiVn/S+FkYdltdYDoWLTTR8x4zAxNyB4OORybnMojJTpjoUyEO+kK+LiA+7OrbpCg==} + engines: {node: '>=18'} '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} @@ -430,10 +445,6 @@ packages: '@types/node@20.14.9': resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==} - '@types/typescript@2.0.0': - resolution: {integrity: sha512-WMEWfMISiJ2QKyk5/dSdgL0ZwP//PZj0jmDU0hMh51FmLq4WIYzjlngsUQZXejQL+QtkXJUOGjb3G3UCvgZuSQ==} - deprecated: This is a stub types definition for TypeScript (https://github.com/Microsoft/TypeScript). TypeScript provides its own type definitions, so you don't need @types/typescript installed! - '@vitest/coverage-v8@1.6.0': resolution: {integrity: sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==} peerDependencies: @@ -500,8 +511,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - base-x@4.0.0: - resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==} + base-x@5.0.0: + resolution: {integrity: sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==} binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} @@ -1200,8 +1211,8 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - typescript@5.5.2: - resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} + typescript@5.5.3: + resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==} engines: {node: '>=14.17'} hasBin: true @@ -1439,11 +1450,25 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true - '@fleet-sdk/core@0.1.0-alpha.12': + '@fleet-sdk/common@0.4.1': {} + + '@fleet-sdk/core@0.5.0': + dependencies: + '@fleet-sdk/common': 0.4.1 + '@fleet-sdk/crypto': 0.5.0 + '@fleet-sdk/serializer': 0.5.0 + + '@fleet-sdk/crypto@0.5.0': dependencies: + '@fleet-sdk/common': 0.4.1 '@noble/hashes': 1.4.0 '@scure/base': 1.1.7 + '@fleet-sdk/serializer@0.5.0': + dependencies: + '@fleet-sdk/common': 0.4.1 + '@fleet-sdk/crypto': 0.5.0 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -1577,10 +1602,6 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/typescript@2.0.0': - dependencies: - typescript: 5.5.2 - '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.14.9))': dependencies: '@ampproject/remapping': 2.3.0 @@ -1660,7 +1681,7 @@ snapshots: balanced-match@1.0.2: {} - base-x@4.0.0: {} + base-x@5.0.0: {} binary-extensions@2.3.0: {} @@ -2329,7 +2350,7 @@ snapshots: tslib@2.6.3: {} - tsup@8.1.0(postcss@8.4.39)(typescript@5.5.2): + tsup@8.1.0(postcss@8.4.39)(typescript@5.5.3): dependencies: bundle-require: 4.2.1(esbuild@0.21.5) cac: 6.7.14 @@ -2347,7 +2368,7 @@ snapshots: tree-kill: 1.2.2 optionalDependencies: postcss: 8.4.39 - typescript: 5.5.2 + typescript: 5.5.3 transitivePeerDependencies: - supports-color - ts-node @@ -2358,7 +2379,7 @@ snapshots: type-fest@2.19.0: {} - typescript@5.5.2: {} + typescript@5.5.3: {} ufo@1.5.3: {} diff --git a/rollup.config.ts b/rollup.config.ts deleted file mode 100644 index 6786ba3..0000000 --- a/rollup.config.ts +++ /dev/null @@ -1,62 +0,0 @@ -import json from "rollup-plugin-json"; -import typescript from "rollup-plugin-typescript2"; -import commonjs from "rollup-plugin-commonjs"; -import resolve from "rollup-plugin-node-resolve"; -import uglify from "@lopatnov/rollup-plugin-uglify"; - -import pkg from "./package.json"; -const globals = { - "bip32-path": "bip32Path", - "base-x": "basex", - "@fleet-sdk/core": "core" -}; - -export default [ - { - input: `src/${pkg.libraryFile}.ts`, - output: [ - { - file: pkg.main, - format: "umd", - name: pkg.umdName, - sourcemap: true, - globals - }, - { - file: pkg.module, - format: "es", - sourcemap: true, - globals - } - ], - external: [...Object.keys(pkg.devDependencies || {}), ...Object.keys(pkg.dependencies || {})], - plugins: [ - json(), - typescript({ - typescript: require("typescript") - }), - resolve(), - commonjs() - ] - }, - { - input: `src/${pkg.libraryFile}.ts`, - output: { - file: `dist/${pkg.libraryFile}.min.js`, - name: pkg.umdName, - format: "umd", - sourcemap: true, - globals - }, - external: [...Object.keys(pkg.devDependencies || {}), ...Object.keys(pkg.dependencies || {})], - plugins: [ - json(), - typescript({ - typescript: require("typescript") - }), - resolve(), - commonjs(), - uglify() - ] - } -]; diff --git a/src/assertions.spec.ts b/src/assertions.spec.ts new file mode 100644 index 0000000..d2e43f1 --- /dev/null +++ b/src/assertions.spec.ts @@ -0,0 +1,20 @@ +import { describe, it, expect } from "vitest"; +import { isErgoPath, isUint64String } from "./assertions"; +import { pathToArray } from "./serialization/serialize"; + +describe("assertions", () => { + it("Ergo path", () => { + expect(isErgoPath(pathToArray("m/44'/429'"))).to.be.true; + expect(isErgoPath(pathToArray("m/44'/2'"))).to.be.false; + expect(isErgoPath(pathToArray("m/44'"))).to.be.false; + }); + + it("UInt64", () => { + expect(isUint64String("0")).to.be.true; + expect(isUint64String("18446744073709551615")).to.be.true; + + expect(isUint64String("18446744073709551616")).to.be.false; + expect(isUint64String("1.2")).to.be.false; + expect(isUint64String("11a")).to.be.false; + }); +}); diff --git a/src/assertions.ts b/src/assertions.ts new file mode 100644 index 0000000..3d75cbe --- /dev/null +++ b/src/assertions.ts @@ -0,0 +1,34 @@ +import bip32Path from "bip32-path"; + +const MIN_UINT_64 = 0n; +const MAX_UINT_64 = 18446744073709551615n; +const MIN_UINT_VALUE = 0; +const MAX_UINT32_VALUE = 4294967295; +const MAX_UINT16_VALUE = 65535; +const MAX_UNIT8_VALUE = 255; + +const [ERGO_PURPOSE, ERGO_COIN_TYPE] = bip32Path.fromString("m/44'/429'").toPathArray(); + +export function isErgoPath(path: number[]): boolean { + if (path.length < 2) return false; + const [pathPurpose, pathCoinType] = path; + return pathPurpose === ERGO_PURPOSE && pathCoinType === ERGO_COIN_TYPE; +} + +export function isUint32(data: number): boolean { + return Number.isInteger(data) && data >= MIN_UINT_VALUE && data <= MAX_UINT32_VALUE; +} + +export function isUint16(data: number): boolean { + return Number.isInteger(data) && data >= MIN_UINT_VALUE && data <= MAX_UINT16_VALUE; +} + +export function isUint8(data: number): boolean { + return Number.isInteger(data) && data >= MIN_UINT_VALUE && data <= MAX_UNIT8_VALUE; +} + +export function isUint64String(value: string): boolean { + if (!/^[0-9]*$/.test(value)) return false; + const parsed = BigInt(value); + return parsed >= MIN_UINT_64 && parsed <= MAX_UINT_64; +} diff --git a/src/device.spec.ts b/src/device.spec.ts new file mode 100644 index 0000000..da3c130 --- /dev/null +++ b/src/device.spec.ts @@ -0,0 +1,51 @@ +import { describe, expect, it } from "vitest"; +import { Device, DeviceError, RETURN_CODE } from "./device"; +import { RecordStore, openTransportReplayer } from "@ledgerhq/hw-transport-mocker"; +import { CLA } from "./erg"; + +describe("DeviceError construction", () => { + it("should create a new DeviceError instance from a know RETURN_CODE", () => { + const error = new DeviceError(RETURN_CODE.TOO_MUCH_DATA); + expect(error.code).toBe(RETURN_CODE.TOO_MUCH_DATA); + expect(error.message).toBe("Too much data"); + expect(error.name).to.be.equal("DeviceError"); + }); + + it("should create a new DeviceError instance from a unknown RETURN_CODE", () => { + const error = new DeviceError(0 as RETURN_CODE); + expect(error.code).toBe(0); + expect(error.message).toBe("Unknown error"); + }); +}); + +describe("Negative tests", () => { + it("Should throw an error if the response length is less than the minimum", async () => { + const transport = await openTransportReplayer( + RecordStore.fromString(` + => e00102030104 + <= 69 + `) + ); + + const device = new Device(transport, CLA); + + await expect(() => device.send(0x1, 0x2, 0x3, Buffer.from([0x4]))).rejects.toThrow( + "Wrong response length" + ); + }); + + it("Should throw if data is too long", async () => { + const transport = await openTransportReplayer( + RecordStore.fromString(` + => e00102030104 + <= 9000 + `) + ); + + const device = new Device(transport, CLA); + + await expect(() => device.send(0x1, 0x2, 0x3, Buffer.alloc(260))).rejects.toThrow( + "Too much data" + ); + }); +}); diff --git a/src/errors/returnCodes.ts b/src/device.ts similarity index 58% rename from src/errors/returnCodes.ts rename to src/device.ts index 27e538c..720f807 100644 --- a/src/errors/returnCodes.ts +++ b/src/device.ts @@ -1,3 +1,109 @@ +import type Transport from "@ledgerhq/hw-transport"; +import type { DeviceResponse } from "./types/internal"; +import { serialize } from "./serialization/serialize"; + +export const enum COMMAND { + GET_APP_VERSION = 0x01, + GET_APP_NAME = 0x02, + + GET_EXTENDED_PUB_KEY = 0x10, + DERIVE_ADDRESS = 0x11, + ATTEST_INPUT = 0x20, + SIGN_TX = 0x21 +} + +const MAX_DATA_LENGTH = 255; +const MIN_RESPONSE_LENGTH = 2; + +export class Device { + #transport: Transport; + #cla: number; + + get transport(): Transport { + return this.#transport; + } + + constructor(transport: Transport, cla: number) { + this.#transport = transport; + this.#cla = cla; + } + + async sendData( + ins: COMMAND, + p1: number, + p2: number, + data: Buffer + ): Promise { + const responses: DeviceResponse[] = []; + for (let i = 0; i < Math.ceil(data.length / MAX_DATA_LENGTH); i++) { + const chunk = data.subarray( + i * MAX_DATA_LENGTH, + Math.min((i + 1) * MAX_DATA_LENGTH, data.length) + ); + + responses.push(await this.send(ins, p1, p2, chunk)); + } + + return responses; + } + + async send( + ins: COMMAND, + p1: number, + p2: number, + data: Buffer + ): Promise { + if (data.length > MAX_DATA_LENGTH) { + throw new DeviceError(RETURN_CODE.TOO_MUCH_DATA); + } + + const adpu = mountApdu(this.#cla, ins, p1, p2, data); + const response = await this.transport.exchange(adpu); + + if (response.length < MIN_RESPONSE_LENGTH) { + throw new DeviceError(RETURN_CODE.WRONG_RESPONSE_LENGTH); + } + const returnCode = response.readUInt16BE(response.length - 2); + if (returnCode !== RETURN_CODE.OK) throw new DeviceError(returnCode); + + const responseData = response.subarray(0, response.length - 2); + return { returnCode, data: responseData }; + } +} + +function mountApdu( + cla: number, + ins: COMMAND, + p1: number, + p2: number, + data: Buffer +): Buffer { + return Buffer.concat([ + serialize.uint8(cla), + serialize.uint8(ins), + serialize.uint8(p1), + serialize.uint8(p2), + serialize.uint8(data.length), + data + ]); +} + +export class DeviceError extends Error { + #code; + + get code() { + return this.#code; + } + + constructor(code: RETURN_CODE, options?: ErrorOptions) { + super(RETURN_MESSAGES[code] || "Unknown error", options); + this.#code = code; + + Object.setPrototypeOf(this, new.target.prototype); + this.name = new.target.name; + } +} + export enum RETURN_CODE { DENIED = 0x6985, WRONG_P1P2 = 0x6a86, @@ -83,7 +189,3 @@ export const RETURN_MESSAGES = { [RETURN_CODE.STACK_OVERFLOW]: "Stack overflow", [RETURN_CODE.OK]: "Ok" }; - -export function getReturnMessage(code: RETURN_CODE): string { - return RETURN_MESSAGES[code] || "Unknown error"; -} diff --git a/src/erg.spec.ts b/src/erg.spec.ts index d305aaf..6a0cf1e 100644 --- a/src/erg.spec.ts +++ b/src/erg.spec.ts @@ -1,9 +1,6 @@ import { describe, it, expect, test, vi } from "vitest"; import { ErgoLedgerApp } from "./erg"; -import { - openTransportReplayer, - RecordStore -} from "@ledgerhq/hw-transport-mocker"; +import { openTransportReplayer, RecordStore } from "@ledgerhq/hw-transport-mocker"; describe("construction", () => { it("should construct app with transport", async () => { @@ -96,10 +93,8 @@ describe("public key management with auth token", () => { expect(app.authToken).toEqual(authToken); expect(extendedPublicKey).toEqual({ - publicKey: - "025381e95e132a4b7a6fc66844a81657a07da1ef5041eaefb7fce03f71c06a11a9", - chainCode: - "9cc4eb9abc8d3f55afeff7bcb8fe2d0a8d100fa35f6fcbac74deded867633eba" + publicKey: "025381e95e132a4b7a6fc66844a81657a07da1ef5041eaefb7fce03f71c06a11a9", + chainCode: "9cc4eb9abc8d3f55afeff7bcb8fe2d0a8d100fa35f6fcbac74deded867633eba" }); }); @@ -150,10 +145,8 @@ describe("public key management without auth token", () => { const extendedPublicKey = await app.getExtendedPublicKey("m/44'/429'/0'"); expect(extendedPublicKey).toEqual({ - publicKey: - "025381e95e132a4b7a6fc66844a81657a07da1ef5041eaefb7fce03f71c06a11a9", - chainCode: - "9cc4eb9abc8d3f55afeff7bcb8fe2d0a8d100fa35f6fcbac74deded867633eba" + publicKey: "025381e95e132a4b7a6fc66844a81657a07da1ef5041eaefb7fce03f71c06a11a9", + chainCode: "9cc4eb9abc8d3f55afeff7bcb8fe2d0a8d100fa35f6fcbac74deded867633eba" }); }); @@ -166,9 +159,9 @@ describe("public key management without auth token", () => { ); const app = new ErgoLedgerApp(transport).useAuthToken(false); - await expect(() => - app.getExtendedPublicKey("m/44'/429'/0'") - ).rejects.toThrow("Operation denied by user"); + await expect(() => app.getExtendedPublicKey("m/44'/429'/0'")).rejects.toThrow( + "Operation denied by user" + ); }); it("should show address", async () => { @@ -198,19 +191,39 @@ describe("public key management without auth token", () => { "Operation denied by user" ); }); + + it("should fail when deriving address with invalid path", async () => { + const transport = await openTransportReplayer( + RecordStore.fromString(` + => e01102011600058000002c800001ad800000000000000000000000 + <= 6985 + `) + ); + + const app = new ErgoLedgerApp(transport).useAuthToken(false); + await expect(() => app.showAddress("m/44'/429'/0'/3/0")).rejects.toThrow( + "Invalid change path: 3" + ); + await expect(() => app.showAddress("m/44'/429'")).rejects.toThrow( + "Invalid path length. 2" + ); + + await expect(() => app.deriveAddress("m/44'/429'/0'/10/0")).rejects.toThrow( + "Invalid change path: 10" + ); + await expect(() => app.deriveAddress("m/44'/429'/0'/1")).rejects.toThrow( + "Invalid path length. 4" + ); + }); }); describe("transaction signing", () => { test.each(txTestVectors)( "should sign $name", async ({ adpuQueue, authToken, proofs, tx }) => { - const transport = await openTransportReplayer( - RecordStore.fromString(adpuQueue) - ); + const transport = await openTransportReplayer(RecordStore.fromString(adpuQueue)); - const app = new ErgoLedgerApp(transport, authToken).useAuthToken( - !!authToken - ); + const app = new ErgoLedgerApp(transport, authToken).useAuthToken(!!authToken); const result = await app.signTx(tx); expect(result).to.deep.equal(proofs); @@ -219,9 +232,7 @@ describe("transaction signing", () => { it("Should throw with empty inputs", async () => { const { adpuQueue, tx } = txTestVectors[0]; - const transport = await openTransportReplayer( - RecordStore.fromString(adpuQueue) - ); + const transport = await openTransportReplayer(RecordStore.fromString(adpuQueue)); const app = new ErgoLedgerApp(transport); await expect(() => app.signTx({ ...tx, inputs: [] })).rejects.toThrow( @@ -254,9 +265,8 @@ describe("transaction signing", () => { index: 0, value: "100000000", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, - 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, - 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, 245, + 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, 45 ]), creationHeight: 1298664, tokens: [], @@ -272,16 +282,15 @@ describe("transaction signing", () => { { amount: "100000000", attestation: "8c2d9e1dcd467155df32bf1ded800710", - boxId: - "2bb2f3111e33ad9d9ab1fa3ae184fc1f06a6ac5a71d40a825997f6637b44784f", - buffer: Buffer.from([ - 43, 178, 243, 17, 30, 51, 173, 157, 154, 177, 250, 58, 225, 132, 252, - 31, 6, 166, 172, 90, 113, 212, 10, 130, 89, 151, 246, 99, 123, 68, - 120, 79, 1, 0, 0, 0, 0, 0, 5, 245, 225, 0, 0, 140, 45, 158, 29, 205, - 70, 113, 85, 223, 50, 191, 29, 237, 128, 7, 16 + boxId: "2bb2f3111e33ad9d9ab1fa3ae184fc1f06a6ac5a71d40a825997f6637b44784f", + bytes: Buffer.from([ + 43, 178, 243, 17, 30, 51, 173, 157, 154, 177, 250, 58, 225, 132, 252, 31, 6, + 166, 172, 90, 113, 212, 10, 130, 89, 151, 246, 99, 123, 68, 120, 79, 1, 0, 0, 0, + 0, 0, 5, 245, 225, 0, 0, 140, 45, 158, 29, 205, 70, 113, 85, 223, 50, 191, 29, + 237, 128, 7, 16 ]), - frameIndex: 0, - framesCount: 1, + index: 0, + count: 1, tokens: [] } ]); @@ -351,9 +360,9 @@ const txTestVectors = [ index: 1, value: "108997360", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298593, tokens: [], @@ -367,9 +376,9 @@ const txTestVectors = [ { value: "100000000", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298664, tokens: [], @@ -378,13 +387,12 @@ const txTestVectors = [ { value: "1100000", ergoTree: Buffer.from([ - 16, 5, 4, 0, 4, 0, 14, 54, 16, 2, 4, 160, 11, 8, 205, 2, 121, 190, - 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, - 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, - 234, 2, 209, 146, 163, 154, 140, 199, 167, 1, 115, 0, 115, 1, 16, 1, - 2, 4, 2, 209, 150, 131, 3, 1, 147, 163, 140, 199, 178, 165, 115, 0, - 0, 1, 147, 194, 178, 165, 115, 1, 0, 116, 115, 2, 115, 3, 131, 1, 8, - 205, 238, 172, 147, 177, 165, 115, 4 + 16, 5, 4, 0, 4, 0, 14, 54, 16, 2, 4, 160, 11, 8, 205, 2, 121, 190, 102, 126, + 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, + 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 234, 2, 209, 146, 163, 154, + 140, 199, 167, 1, 115, 0, 115, 1, 16, 1, 2, 4, 2, 209, 150, 131, 3, 1, 147, + 163, 140, 199, 178, 165, 115, 0, 0, 1, 147, 194, 178, 165, 115, 1, 0, 116, + 115, 2, 115, 3, 131, 1, 8, 205, 238, 172, 147, 177, 165, 115, 4 ]), creationHeight: 1298664, tokens: [], @@ -393,9 +401,9 @@ const txTestVectors = [ { value: "7897360", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298664, tokens: [], @@ -410,10 +418,10 @@ const txTestVectors = [ }, proofs: [ Uint8Array.from([ - 68, 90, 41, 103, 107, 77, 70, 59, 154, 66, 102, 174, 66, 213, 208, 70, - 230, 97, 14, 147, 99, 71, 214, 174, 10, 34, 200, 245, 57, 246, 237, 96, - 119, 119, 126, 148, 120, 245, 1, 230, 87, 234, 188, 107, 140, 239, 212, - 6, 192, 10, 80, 184, 8, 64, 165, 254 + 68, 90, 41, 103, 107, 77, 70, 59, 154, 66, 102, 174, 66, 213, 208, 70, 230, 97, + 14, 147, 99, 71, 214, 174, 10, 34, 200, 245, 57, 246, 237, 96, 119, 119, 126, 148, + 120, 245, 1, 230, 87, 234, 188, 107, 140, 239, 212, 6, 192, 10, 80, 184, 8, 64, + 165, 254 ]) ] }, @@ -503,9 +511,9 @@ const txTestVectors = [ index: 11, value: "40680", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298572, tokens: [ @@ -523,9 +531,9 @@ const txTestVectors = [ index: 1, value: "111156680", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298572, tokens: [], @@ -539,9 +547,9 @@ const txTestVectors = [ { value: "1000000", ergoTree: Buffer.from([ - 0, 8, 205, 3, 83, 218, 249, 90, 145, 149, 98, 41, 151, 37, 20, 226, - 244, 145, 253, 241, 249, 226, 237, 71, 147, 249, 22, 130, 158, 228, - 39, 208, 35, 181, 27, 1 + 0, 8, 205, 3, 83, 218, 249, 90, 145, 149, 98, 41, 151, 37, 20, 226, 244, 145, + 253, 241, 249, 226, 237, 71, 147, 249, 22, 130, 158, 228, 39, 208, 35, 181, + 27, 1 ]), creationHeight: 1298578, tokens: [ @@ -555,13 +563,12 @@ const txTestVectors = [ { value: "1100000", ergoTree: Buffer.from([ - 16, 5, 4, 0, 4, 0, 14, 54, 16, 2, 4, 160, 11, 8, 205, 2, 121, 190, - 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, - 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, - 234, 2, 209, 146, 163, 154, 140, 199, 167, 1, 115, 0, 115, 1, 16, 1, - 2, 4, 2, 209, 150, 131, 3, 1, 147, 163, 140, 199, 178, 165, 115, 0, - 0, 1, 147, 194, 178, 165, 115, 1, 0, 116, 115, 2, 115, 3, 131, 1, 8, - 205, 238, 172, 147, 177, 165, 115, 4 + 16, 5, 4, 0, 4, 0, 14, 54, 16, 2, 4, 160, 11, 8, 205, 2, 121, 190, 102, 126, + 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, + 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 234, 2, 209, 146, 163, 154, + 140, 199, 167, 1, 115, 0, 115, 1, 16, 1, 2, 4, 2, 209, 150, 131, 3, 1, 147, + 163, 140, 199, 178, 165, 115, 0, 0, 1, 147, 194, 178, 165, 115, 1, 0, 116, + 115, 2, 115, 3, 131, 1, 8, 205, 238, 172, 147, 177, 165, 115, 4 ]), creationHeight: 1298578, tokens: [], @@ -570,9 +577,9 @@ const txTestVectors = [ { value: "109097360", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298578, tokens: [], @@ -581,9 +588,8 @@ const txTestVectors = [ ], distinctTokenIds: [ Uint8Array.from([ - 0, 177, 226, 54, 182, 11, 149, 194, 198, 248, 0, 122, 157, 137, 188, - 70, 15, 201, 231, 143, 152, 176, 159, 174, 201, 68, 144, 7, 180, 11, - 204, 243 + 0, 177, 226, 54, 182, 11, 149, 194, 198, 248, 0, 122, 157, 137, 188, 70, 15, + 201, 231, 143, 152, 176, 159, 174, 201, 68, 144, 7, 180, 11, 204, 243 ]) ], changeMap: { @@ -593,16 +599,16 @@ const txTestVectors = [ }, proofs: [ Uint8Array.from([ - 179, 67, 83, 188, 164, 121, 254, 90, 237, 4, 2, 94, 189, 71, 222, 179, - 210, 252, 37, 43, 22, 111, 112, 18, 7, 65, 100, 32, 251, 26, 136, 0, 5, - 102, 21, 145, 11, 28, 172, 27, 86, 234, 167, 11, 111, 55, 33, 165, 91, - 143, 156, 157, 109, 106, 90, 176 + 179, 67, 83, 188, 164, 121, 254, 90, 237, 4, 2, 94, 189, 71, 222, 179, 210, 252, + 37, 43, 22, 111, 112, 18, 7, 65, 100, 32, 251, 26, 136, 0, 5, 102, 21, 145, 11, + 28, 172, 27, 86, 234, 167, 11, 111, 55, 33, 165, 91, 143, 156, 157, 109, 106, 90, + 176 ]), Uint8Array.from([ - 179, 67, 83, 188, 164, 121, 254, 90, 237, 4, 2, 94, 189, 71, 222, 179, - 210, 252, 37, 43, 22, 111, 112, 18, 7, 65, 100, 32, 251, 26, 136, 0, 5, - 102, 21, 145, 11, 28, 172, 27, 86, 234, 167, 11, 111, 55, 33, 165, 91, - 143, 156, 157, 109, 106, 90, 176 + 179, 67, 83, 188, 164, 121, 254, 90, 237, 4, 2, 94, 189, 71, 222, 179, 210, 252, + 37, 43, 22, 111, 112, 18, 7, 65, 100, 32, 251, 26, 136, 0, 5, 102, 21, 145, 11, + 28, 172, 27, 86, 234, 167, 11, 111, 55, 33, 165, 91, 143, 156, 157, 109, 106, 90, + 176 ]) ] }, @@ -758,18 +764,17 @@ const txTestVectors = [ index: 1, value: "7553272000", ergoTree: Buffer.from([ - 16, 6, 4, 0, 14, 32, 215, 22, 147, 196, 154, 132, 251, 190, 205, 73, - 8, 201, 72, 19, 180, 101, 20, 177, 139, 103, 169, 153, 82, 220, 30, - 110, 71, 145, 85, 109, 228, 19, 4, 0, 4, 0, 5, 0, 5, 0, 216, 3, 214, - 1, 227, 0, 4, 214, 2, 228, 198, 167, 4, 8, 214, 3, 228, 198, 167, 5, - 5, 149, 230, 114, 1, 216, 4, 214, 4, 178, 165, 228, 114, 1, 0, 214, - 5, 178, 219, 99, 8, 114, 4, 115, 0, 0, 214, 6, 219, 99, 8, 167, 214, - 7, 153, 193, 167, 193, 114, 4, 209, 150, 131, 2, 1, 150, 131, 5, 1, - 147, 194, 114, 4, 194, 167, 147, 140, 114, 5, 1, 115, 1, 147, 228, - 198, 114, 4, 4, 8, 114, 2, 147, 228, 198, 114, 4, 5, 5, 114, 3, 147, - 228, 198, 114, 4, 6, 14, 197, 167, 150, 131, 2, 1, 146, 156, 153, - 140, 114, 5, 2, 149, 145, 177, 114, 6, 115, 2, 140, 178, 114, 6, - 115, 3, 0, 2, 115, 4, 114, 3, 114, 7, 146, 114, 7, 115, 5, 114, 2 + 16, 6, 4, 0, 14, 32, 215, 22, 147, 196, 154, 132, 251, 190, 205, 73, 8, 201, + 72, 19, 180, 101, 20, 177, 139, 103, 169, 153, 82, 220, 30, 110, 71, 145, 85, + 109, 228, 19, 4, 0, 4, 0, 5, 0, 5, 0, 216, 3, 214, 1, 227, 0, 4, 214, 2, 228, + 198, 167, 4, 8, 214, 3, 228, 198, 167, 5, 5, 149, 230, 114, 1, 216, 4, 214, 4, + 178, 165, 228, 114, 1, 0, 214, 5, 178, 219, 99, 8, 114, 4, 115, 0, 0, 214, 6, + 219, 99, 8, 167, 214, 7, 153, 193, 167, 193, 114, 4, 209, 150, 131, 2, 1, 150, + 131, 5, 1, 147, 194, 114, 4, 194, 167, 147, 140, 114, 5, 1, 115, 1, 147, 228, + 198, 114, 4, 4, 8, 114, 2, 147, 228, 198, 114, 4, 5, 5, 114, 3, 147, 228, 198, + 114, 4, 6, 14, 197, 167, 150, 131, 2, 1, 146, 156, 153, 140, 114, 5, 2, 149, + 145, 177, 114, 6, 115, 2, 140, 178, 114, 6, 115, 3, 0, 2, 115, 4, 114, 3, 114, + 7, 146, 114, 7, 115, 5, 114, 2 ]), creationHeight: 1215896, tokens: [ @@ -779,11 +784,11 @@ const txTestVectors = [ } ], additionalRegisters: Buffer.from([ - 3, 8, 205, 2, 15, 69, 105, 90, 11, 147, 166, 49, 136, 74, 44, 208, - 113, 106, 114, 82, 35, 35, 103, 133, 40, 82, 43, 49, 92, 84, 214, - 207, 156, 96, 76, 215, 5, 224, 209, 4, 14, 32, 219, 170, 253, 50, - 105, 221, 13, 51, 44, 153, 13, 165, 25, 164, 138, 117, 2, 78, 12, - 139, 200, 129, 160, 25, 177, 193, 63, 250, 87, 8, 230, 31 + 3, 8, 205, 2, 15, 69, 105, 90, 11, 147, 166, 49, 136, 74, 44, 208, 113, 106, + 114, 82, 35, 35, 103, 133, 40, 82, 43, 49, 92, 84, 214, 207, 156, 96, 76, 215, + 5, 224, 209, 4, 14, 32, 219, 170, 253, 50, 105, 221, 13, 51, 44, 153, 13, 165, + 25, 164, 138, 117, 2, 78, 12, 139, 200, 129, 160, 25, 177, 193, 63, 250, 87, + 8, 230, 31 ]), extension: Buffer.from([1, 0, 4, 2]), signPath: "m/44'/429'/0'/0/0" @@ -793,9 +798,9 @@ const txTestVectors = [ index: 18, value: "41760", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298593, tokens: [ @@ -813,9 +818,9 @@ const txTestVectors = [ index: 5, value: "40320", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298593, tokens: [ @@ -833,9 +838,9 @@ const txTestVectors = [ index: 0, value: "100000000", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298664, tokens: [], @@ -854,9 +859,9 @@ const txTestVectors = [ { value: "10000000", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298679, tokens: [ @@ -870,18 +875,17 @@ const txTestVectors = [ { value: "7551068000", ergoTree: Buffer.from([ - 16, 6, 4, 0, 14, 32, 215, 22, 147, 196, 154, 132, 251, 190, 205, 73, - 8, 201, 72, 19, 180, 101, 20, 177, 139, 103, 169, 153, 82, 220, 30, - 110, 71, 145, 85, 109, 228, 19, 4, 0, 4, 0, 5, 0, 5, 0, 216, 3, 214, - 1, 227, 0, 4, 214, 2, 228, 198, 167, 4, 8, 214, 3, 228, 198, 167, 5, - 5, 149, 230, 114, 1, 216, 4, 214, 4, 178, 165, 228, 114, 1, 0, 214, - 5, 178, 219, 99, 8, 114, 4, 115, 0, 0, 214, 6, 219, 99, 8, 167, 214, - 7, 153, 193, 167, 193, 114, 4, 209, 150, 131, 2, 1, 150, 131, 5, 1, - 147, 194, 114, 4, 194, 167, 147, 140, 114, 5, 1, 115, 1, 147, 228, - 198, 114, 4, 4, 8, 114, 2, 147, 228, 198, 114, 4, 5, 5, 114, 3, 147, - 228, 198, 114, 4, 6, 14, 197, 167, 150, 131, 2, 1, 146, 156, 153, - 140, 114, 5, 2, 149, 145, 177, 114, 6, 115, 2, 140, 178, 114, 6, - 115, 3, 0, 2, 115, 4, 114, 3, 114, 7, 146, 114, 7, 115, 5, 114, 2 + 16, 6, 4, 0, 14, 32, 215, 22, 147, 196, 154, 132, 251, 190, 205, 73, 8, 201, + 72, 19, 180, 101, 20, 177, 139, 103, 169, 153, 82, 220, 30, 110, 71, 145, 85, + 109, 228, 19, 4, 0, 4, 0, 5, 0, 5, 0, 216, 3, 214, 1, 227, 0, 4, 214, 2, 228, + 198, 167, 4, 8, 214, 3, 228, 198, 167, 5, 5, 149, 230, 114, 1, 216, 4, 214, 4, + 178, 165, 228, 114, 1, 0, 214, 5, 178, 219, 99, 8, 114, 4, 115, 0, 0, 214, 6, + 219, 99, 8, 167, 214, 7, 153, 193, 167, 193, 114, 4, 209, 150, 131, 2, 1, 150, + 131, 5, 1, 147, 194, 114, 4, 194, 167, 147, 140, 114, 5, 1, 115, 1, 147, 228, + 198, 114, 4, 4, 8, 114, 2, 147, 228, 198, 114, 4, 5, 5, 114, 3, 147, 228, 198, + 114, 4, 6, 14, 197, 167, 150, 131, 2, 1, 146, 156, 153, 140, 114, 5, 2, 149, + 145, 177, 114, 6, 115, 2, 140, 178, 114, 6, 115, 3, 0, 2, 115, 4, 114, 3, 114, + 7, 146, 114, 7, 115, 5, 114, 2 ]), creationHeight: 1298679, tokens: [ @@ -891,23 +895,22 @@ const txTestVectors = [ } ], registers: Buffer.from([ - 3, 8, 205, 2, 15, 69, 105, 90, 11, 147, 166, 49, 136, 74, 44, 208, - 113, 106, 114, 82, 35, 35, 103, 133, 40, 82, 43, 49, 92, 84, 214, - 207, 156, 96, 76, 215, 5, 224, 209, 4, 14, 32, 136, 100, 48, 96, 84, - 152, 13, 65, 112, 159, 76, 237, 216, 134, 240, 108, 158, 221, 45, - 15, 89, 161, 74, 250, 145, 226, 101, 96, 119, 216, 3, 242 + 3, 8, 205, 2, 15, 69, 105, 90, 11, 147, 166, 49, 136, 74, 44, 208, 113, 106, + 114, 82, 35, 35, 103, 133, 40, 82, 43, 49, 92, 84, 214, 207, 156, 96, 76, 215, + 5, 224, 209, 4, 14, 32, 136, 100, 48, 96, 84, 152, 13, 65, 112, 159, 76, 237, + 216, 134, 240, 108, 158, 221, 45, 15, 89, 161, 74, 250, 145, 226, 101, 96, + 119, 216, 3, 242 ]) }, { value: "2204000", ergoTree: Buffer.from([ - 16, 5, 4, 0, 4, 0, 14, 54, 16, 2, 4, 160, 11, 8, 205, 2, 121, 190, - 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, - 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, - 234, 2, 209, 146, 163, 154, 140, 199, 167, 1, 115, 0, 115, 1, 16, 1, - 2, 4, 2, 209, 150, 131, 3, 1, 147, 163, 140, 199, 178, 165, 115, 0, - 0, 1, 147, 194, 178, 165, 115, 1, 0, 116, 115, 2, 115, 3, 131, 1, 8, - 205, 238, 172, 147, 177, 165, 115, 4 + 16, 5, 4, 0, 4, 0, 14, 54, 16, 2, 4, 160, 11, 8, 205, 2, 121, 190, 102, 126, + 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, + 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 234, 2, 209, 146, 163, 154, + 140, 199, 167, 1, 115, 0, 115, 1, 16, 1, 2, 4, 2, 209, 150, 131, 3, 1, 147, + 163, 140, 199, 178, 165, 115, 0, 0, 1, 147, 194, 178, 165, 115, 1, 0, 116, + 115, 2, 115, 3, 131, 1, 8, 205, 238, 172, 147, 177, 165, 115, 4 ]), creationHeight: 1298679, tokens: [], @@ -916,9 +919,9 @@ const txTestVectors = [ { value: "90041760", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298679, tokens: [], @@ -927,9 +930,9 @@ const txTestVectors = [ { value: "40320", ergoTree: Buffer.from([ - 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, - 220, 2, 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, - 228, 25, 196, 33, 45 + 0, 8, 205, 2, 165, 26, 12, 94, 107, 69, 108, 44, 142, 113, 242, 56, 220, 2, + 245, 52, 90, 173, 154, 124, 91, 140, 101, 93, 210, 75, 197, 228, 25, 196, 33, + 45 ]), creationHeight: 1298679, tokens: [ @@ -943,14 +946,12 @@ const txTestVectors = [ ], distinctTokenIds: [ Uint8Array.from([ - 239, 128, 43, 71, 92, 6, 24, 159, 219, 248, 68, 21, 60, 220, 29, 68, - 154, 91, 168, 124, 206, 19, 209, 27, 180, 123, 90, 83, 159, 39, 241, - 43 + 239, 128, 43, 71, 92, 6, 24, 159, 219, 248, 68, 21, 60, 220, 29, 68, 154, 91, + 168, 124, 206, 19, 209, 27, 180, 123, 90, 83, 159, 39, 241, 43 ]), Uint8Array.from([ - 215, 22, 147, 196, 154, 132, 251, 190, 205, 73, 8, 201, 72, 19, 180, - 101, 20, 177, 139, 103, 169, 153, 82, 220, 30, 110, 71, 145, 85, 109, - 228, 19 + 215, 22, 147, 196, 154, 132, 251, 190, 205, 73, 8, 201, 72, 19, 180, 101, 20, + 177, 139, 103, 169, 153, 82, 220, 30, 110, 71, 145, 85, 109, 228, 19 ]) ], changeMap: { @@ -960,28 +961,28 @@ const txTestVectors = [ }, proofs: [ Uint8Array.from([ - 228, 0, 151, 4, 137, 84, 132, 119, 5, 78, 45, 157, 172, 174, 204, 178, - 193, 69, 249, 227, 25, 235, 202, 62, 90, 187, 180, 190, 115, 250, 4, 2, - 197, 134, 141, 125, 250, 104, 173, 255, 233, 180, 192, 134, 61, 74, 206, - 60, 60, 161, 247, 144, 129, 90, 237, 51 + 228, 0, 151, 4, 137, 84, 132, 119, 5, 78, 45, 157, 172, 174, 204, 178, 193, 69, + 249, 227, 25, 235, 202, 62, 90, 187, 180, 190, 115, 250, 4, 2, 197, 134, 141, 125, + 250, 104, 173, 255, 233, 180, 192, 134, 61, 74, 206, 60, 60, 161, 247, 144, 129, + 90, 237, 51 ]), Uint8Array.from([ - 228, 0, 151, 4, 137, 84, 132, 119, 5, 78, 45, 157, 172, 174, 204, 178, - 193, 69, 249, 227, 25, 235, 202, 62, 90, 187, 180, 190, 115, 250, 4, 2, - 197, 134, 141, 125, 250, 104, 173, 255, 233, 180, 192, 134, 61, 74, 206, - 60, 60, 161, 247, 144, 129, 90, 237, 51 + 228, 0, 151, 4, 137, 84, 132, 119, 5, 78, 45, 157, 172, 174, 204, 178, 193, 69, + 249, 227, 25, 235, 202, 62, 90, 187, 180, 190, 115, 250, 4, 2, 197, 134, 141, 125, + 250, 104, 173, 255, 233, 180, 192, 134, 61, 74, 206, 60, 60, 161, 247, 144, 129, + 90, 237, 51 ]), Uint8Array.from([ - 228, 0, 151, 4, 137, 84, 132, 119, 5, 78, 45, 157, 172, 174, 204, 178, - 193, 69, 249, 227, 25, 235, 202, 62, 90, 187, 180, 190, 115, 250, 4, 2, - 197, 134, 141, 125, 250, 104, 173, 255, 233, 180, 192, 134, 61, 74, 206, - 60, 60, 161, 247, 144, 129, 90, 237, 51 + 228, 0, 151, 4, 137, 84, 132, 119, 5, 78, 45, 157, 172, 174, 204, 178, 193, 69, + 249, 227, 25, 235, 202, 62, 90, 187, 180, 190, 115, 250, 4, 2, 197, 134, 141, 125, + 250, 104, 173, 255, 233, 180, 192, 134, 61, 74, 206, 60, 60, 161, 247, 144, 129, + 90, 237, 51 ]), Uint8Array.from([ - 228, 0, 151, 4, 137, 84, 132, 119, 5, 78, 45, 157, 172, 174, 204, 178, - 193, 69, 249, 227, 25, 235, 202, 62, 90, 187, 180, 190, 115, 250, 4, 2, - 197, 134, 141, 125, 250, 104, 173, 255, 233, 180, 192, 134, 61, 74, 206, - 60, 60, 161, 247, 144, 129, 90, 237, 51 + 228, 0, 151, 4, 137, 84, 132, 119, 5, 78, 45, 157, 172, 174, 204, 178, 193, 69, + 249, 227, 25, 235, 202, 62, 90, 187, 180, 190, 115, 250, 4, 2, 197, 134, 141, 125, + 250, 104, 173, 255, 233, 180, 192, 134, 61, 74, 206, 60, 60, 161, 247, 144, 129, + 90, 237, 51 ]) ] } diff --git a/src/erg.ts b/src/erg.ts index e75a4bb..1607a43 100644 --- a/src/erg.ts +++ b/src/erg.ts @@ -1,16 +1,14 @@ import type Transport from "@ledgerhq/hw-transport"; -import Device from "./interactions/common/device"; -import { +import { Device, DeviceError, RETURN_CODE } from "./device"; +import type { AppName, UnsignedBox, DerivedAddress, ExtendedPublicKey, Version, - UnsignedTx, - Network + UnsignedTransaction } from "./types/public"; -import { assert, isValidErgoPath } from "./validations"; -import AttestedBox from "./models/attestedBox"; +import type { AttestedBox } from "./types/attestedBox"; import { getAppName, getExtendedPublicKey, @@ -20,17 +18,13 @@ import { attestInput, signTx } from "./interactions"; -import Serialize from "./serialization/serialize"; -import { AttestedTx, SignTxResponse } from "./types/internal"; -import { uniq } from "./serialization/utils"; -import { DeviceError, RETURN_CODE } from "./errors"; +import type { AttestedTransaction, SignTransactionResponse } from "./types/internal"; +import { uniq, Network } from "@fleet-sdk/common"; -export * from "./errors"; +export { DeviceError, RETURN_CODE, Network }; export * from "./types/public"; export const CLA = 0xe0; -const CHANGE_PATH_INDEX = 3; - /** * Ergo's Ledger hardware wallet API */ @@ -126,10 +120,7 @@ export class ErgoLedgerApp { */ public async getExtendedPublicKey(path: string): Promise { this._debug("getExtendedPublicKey", path); - - const pathArray = Serialize.bip32PathAsArray(path); - assert(isValidErgoPath(pathArray), "Invalid Ergo path."); - return getExtendedPublicKey(this._device, pathArray, this.authToken); + return getExtendedPublicKey(this._device, path, this.authToken); } /** @@ -142,9 +133,7 @@ export class ErgoLedgerApp { network = Network.Mainnet ): Promise { this._debug("deriveAddress", path); - - const pathArray = this.getDerivationPathArray(path); - return deriveAddress(this._device, network, pathArray, this.authToken); + return deriveAddress(this._device, network, path, this.authToken); } /** @@ -152,26 +141,9 @@ export class ErgoLedgerApp { * @param path Bip44 path. * @returns a Promise with true if the user accepts or throws an exception if it get rejected. */ - public async showAddress( - path: string, - network = Network.Mainnet - ): Promise { + public async showAddress(path: string, network = Network.Mainnet): Promise { this._debug("showAddress", path); - - const pathArray = this.getDerivationPathArray(path); - return showAddress(this._device, network, pathArray, this.authToken); - } - - private getDerivationPathArray(path: string) { - const pathArray = Serialize.bip32PathAsArray(path); - assert(isValidErgoPath(pathArray), "Invalid Ergo path."); - assert(pathArray.length >= 5, "Invalid path length."); - assert( - pathArray[CHANGE_PATH_INDEX] in [0, 1], - "Invalid change path value." - ); - - return pathArray; + return showAddress(this._device, network, path, this.authToken); } public async attestInput(box: UnsignedBox): Promise { @@ -184,7 +156,7 @@ export class ErgoLedgerApp { } public async signTx( - tx: UnsignedTx, + tx: UnsignedTransaction, network = Network.Mainnet ): Promise { this._debug("signTx", { tx, network }); @@ -195,7 +167,7 @@ export class ErgoLedgerApp { const attestedInputs = await this._attestInputs(tx.inputs); const signPaths = uniq(tx.inputs.map((i) => i.signPath)); - const attestedTx: AttestedTx = { + const attestedTx: AttestedTransaction = { inputs: attestedInputs, dataInputs: tx.dataInputs, outputs: tx.outputs, @@ -203,8 +175,8 @@ export class ErgoLedgerApp { changeMap: tx.changeMap }; - const signatures: SignTxResponse = {}; - for (let path of signPaths) { + const signatures: SignTransactionResponse = {}; + for (const path of signPaths) { signatures[path] = await signTx( this._device, attestedTx, @@ -215,7 +187,7 @@ export class ErgoLedgerApp { } const signBytes: Uint8Array[] = []; - for (let input of tx.inputs) { + for (const input of tx.inputs) { signBytes.push(signatures[input.signPath]); } @@ -233,7 +205,7 @@ export class ErgoLedgerApp { return attestedBoxes; } - private _debug(caller: string, message: any = "") { + private _debug(caller: string, message: unknown = "") { if (!this._logging) { return; } diff --git a/src/errors/deviceError.ts b/src/errors/deviceError.ts deleted file mode 100644 index c349b1f..0000000 --- a/src/errors/deviceError.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { getReturnMessage } from "./returnCodes"; - -export class DeviceError extends Error { - private _code; - - public get code() { - return this._code; - } - - constructor(code: number) { - super(getReturnMessage(code)); - this._code = code; - this.name = this.constructor.name; - } -} diff --git a/src/errors/index.ts b/src/errors/index.ts deleted file mode 100644 index 422fc89..0000000 --- a/src/errors/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { DeviceError } from "./deviceError"; -export { getReturnMessage, RETURN_CODE, RETURN_MESSAGES } from "./returnCodes"; diff --git a/src/interactions/attestInput.spec.ts b/src/interactions/attestInput.spec.ts index e8a827f..917a2f3 100644 --- a/src/interactions/attestInput.spec.ts +++ b/src/interactions/attestInput.spec.ts @@ -1,19 +1,17 @@ import { describe, expect, it } from "vitest"; -import { parseAttestedFrameResponse } from "./attestInput"; +import { decodeAttestedFrameResponse } from "./attestInput"; describe("attestInput test", () => { const frameHex = "7cbe85a5f2d2154538eb883bbbee10dd414ec24b6e52b43495da906bce2c5e8a010000000000ab6a1fde032d554219a80c011cc51509e34fa4950965bb8e01de4d012536e766c9ca08bc2c000000174876e7febcd5db3a2872f279ef89edaa51a9344a6095ea1f03396874b695b5ba95ff602e00000017483412969f90c012e03bf99397e363fb1571b7999941e0862a217307e3467ee80cf53af700000000000000012f5151af1796a5827de6df5339ddca7a"; it("should parse frame response", () => { - const parsedFrame = parseAttestedFrameResponse( - Buffer.from(frameHex, "hex") - ); + const parsedFrame = decodeAttestedFrameResponse(Buffer.from(frameHex, "hex")); expect(parsedFrame).toMatchObject({ boxId: "7cbe85a5f2d2154538eb883bbbee10dd414ec24b6e52b43495da906bce2c5e8a", - framesCount: 1, - frameIndex: 0, + count: 1, + index: 0, amount: "2875858910", tokens: [ { diff --git a/src/interactions/attestInput.ts b/src/interactions/attestInput.ts index c99214b..7ea6902 100644 --- a/src/interactions/attestInput.ts +++ b/src/interactions/attestInput.ts @@ -1,9 +1,9 @@ -import Device, { COMMAND } from "./common/device"; -import { AttestedBoxFrame, UnsignedBox, Token } from "../types/public"; +import { COMMAND, type Device } from "../device"; +import type { AttestedBoxFrame, UnsignedBox, Token } from "../types/public"; import type { DeviceResponse } from "../types/internal"; -import AttestedBox from "../models/attestedBox"; -import Serialize from "../serialization/serialize"; -import Deserialize from "../serialization/deserialize"; +import { serialize } from "../serialization/serialize"; +import { deserialize } from "../serialization/deserialize"; +import { AttestedBox } from "../types/attestedBox"; const enum P1 { BOX_START = 0x01, @@ -35,16 +35,20 @@ export async function attestInput( return new AttestedBox(box, await getAttestedFrames(device, frameCount, sessionId)); } -async function sendHeader(device: Device, box: UnsignedBox, authToken?: number): Promise { +async function sendHeader( + device: Device, + box: UnsignedBox, + authToken?: number +): Promise { const header = Buffer.concat([ - Serialize.hex(box.txId), - Serialize.uint16(box.index), - Serialize.uint64(box.value), - Serialize.uint32(box.ergoTree.length), - Serialize.uint32(box.creationHeight), - Serialize.uint8(box.tokens.length), - Serialize.uint32(box.additionalRegisters.length), - authToken ? Serialize.uint32(authToken) : Buffer.alloc(0) + serialize.hex(box.txId), + serialize.uint16(box.index), + serialize.uint64(box.value), + serialize.uint32(box.ergoTree.length), + serialize.uint32(box.creationHeight), + serialize.uint8(box.tokens.length), + serialize.uint32(box.additionalRegisters.length), + authToken ? serialize.uint32(authToken) : Buffer.alloc(0) ]); const response = await device.send( @@ -56,7 +60,11 @@ async function sendHeader(device: Device, box: UnsignedBox, authToken?: number): return response.data[0]; } -async function sendErgoTree(device: Device, data: Buffer, sessionId: number): Promise { +async function sendErgoTree( + device: Device, + data: Buffer, + sessionId: number +): Promise { const results = await device.sendData( COMMAND.ATTEST_INPUT, P1.ADD_ERGO_TREE_CHUNK, @@ -67,14 +75,18 @@ async function sendErgoTree(device: Device, data: Buffer, sessionId: number): Pr return results.pop()?.data[0] || 0; } -async function sendTokens(device: Device, tokens: Token[], sessionId: number): Promise { +async function sendTokens( + device: Device, + tokens: Token[], + sessionId: number +): Promise { const MAX_PACKET_SIZE = 6; - const packets = Serialize.arrayAndChunk(tokens, MAX_PACKET_SIZE, (t) => - Buffer.concat([Serialize.hex(t.id), Serialize.uint64(t.amount)]) + const packets = serialize.arrayAsMappedChunks(tokens, MAX_PACKET_SIZE, (t) => + Buffer.concat([serialize.hex(t.id), serialize.uint64(t.amount)]) ); const results: DeviceResponse[] = []; - for (let p of packets) { + for (const p of packets) { results.push(await device.send(COMMAND.ATTEST_INPUT, P1.ADD_TOKENS, sessionId, p)); } @@ -82,7 +94,11 @@ async function sendTokens(device: Device, tokens: Token[], sessionId: number): P return results.pop()?.data[0] || 0; } -async function sendRegisters(device: Device, data: Buffer, sessionId: number): Promise { +async function sendRegisters( + device: Device, + data: Buffer, + sessionId: number +): Promise { const results = await device.sendData( COMMAND.ATTEST_INPUT, P1.ADD_REGISTERS_CHUNK, @@ -108,37 +124,37 @@ async function getAttestedFrames( Buffer.from([i]) ); - responses.push(parseAttestedFrameResponse(response.data)); + responses.push(decodeAttestedFrameResponse(response.data)); } return responses; } -export function parseAttestedFrameResponse(frameBuff: Buffer): AttestedBoxFrame { +export function decodeAttestedFrameResponse(bytes: Buffer): AttestedBoxFrame { let offset = 0; - const boxId = Deserialize.hex(frameBuff.slice(offset, (offset += 32))); - const count = Deserialize.uint8(frameBuff.slice(offset, (offset += 1))); - const index = Deserialize.uint8(frameBuff.slice(offset, (offset += 1))); - const amount = Deserialize.uint64(frameBuff.slice(offset, (offset += 8))); - const tokenCount = Deserialize.uint8(frameBuff.slice(offset, (offset += 1))); + const boxId = deserialize.hex(bytes.subarray(offset, (offset += 32))); + const count = deserialize.uint8(bytes.subarray(offset, (offset += 1))); + const index = deserialize.uint8(bytes.subarray(offset, (offset += 1))); + const amount = deserialize.uint64(bytes.subarray(offset, (offset += 8))); + const tokenCount = deserialize.uint8(bytes.subarray(offset, (offset += 1))); const tokens: Token[] = []; for (let i = 0; i < tokenCount; i++) { tokens.push({ - id: Deserialize.hex(frameBuff.slice(offset, (offset += 32))), - amount: Deserialize.uint64(frameBuff.slice(offset, (offset += 8))) + id: deserialize.hex(bytes.subarray(offset, (offset += 32))), + amount: deserialize.uint64(bytes.subarray(offset, (offset += 8))) }); } - const attestation = Deserialize.hex(frameBuff.slice(offset, (offset += 16))); + const attestation = deserialize.hex(bytes.subarray(offset, (offset += 16))); return { boxId, - framesCount: count, - frameIndex: index, + count, + index, amount, tokens, attestation, - buffer: frameBuff + bytes }; } diff --git a/src/interactions/common/device.ts b/src/interactions/common/device.ts deleted file mode 100644 index d096610..0000000 --- a/src/interactions/common/device.ts +++ /dev/null @@ -1,82 +0,0 @@ -import type Transport from "@ledgerhq/hw-transport"; -import { DeviceError } from "../../errors/deviceError"; -import { RETURN_CODE } from "../../errors"; -import { DeviceResponse } from "../../types/internal"; -import Serialize from "../../serialization/serialize"; - -export const enum COMMAND { - GET_APP_VERSION = 0x01, - GET_APP_NAME = 0x02, - - GET_EXTENTED_PUB_KEY = 0x10, - DERIVE_ADDRESS = 0x11, - ATTEST_INPUT = 0x20, - SIGN_TX = 0x21 -} - -const MAX_DATA_LENGTH = 255; -const MIN_RESPONSE_LENGTH = 2; - -export default class Device { - private _transport: Transport; - private _cla: number; - - public get transport(): Transport { - return this._transport; - } - - constructor(transport: Transport, cla: number) { - this._transport = transport; - this._cla = cla; - } - - public async sendData( - ins: COMMAND, - p1: number, - p2: number, - data: Buffer - ): Promise { - let responses: DeviceResponse[] = []; - for (let i = 0; i < Math.ceil(data.length / MAX_DATA_LENGTH); i++) { - const chunk = data.slice( - i * MAX_DATA_LENGTH, - Math.min((i + 1) * MAX_DATA_LENGTH, data.length) - ); - - responses.push(await this.send(ins, p1, p2, chunk)); - } - - return responses; - } - - public async send(ins: COMMAND, p1: number, p2: number, data: Buffer): Promise { - if (data.length > MAX_DATA_LENGTH) { - throw new DeviceError(RETURN_CODE.TOO_MUCH_DATA); - } - - const apdu = this.mountApdu(this._cla, ins, p1, p2, data); - const response = await this.transport.exchange(apdu); - - if (response.length < MIN_RESPONSE_LENGTH) { - throw new DeviceError(RETURN_CODE.WRONG_RESPONSE_LENGTH); - } - const returnCode = response.readUInt16BE(response.length - 2); - if (returnCode != RETURN_CODE.OK) { - throw new DeviceError(returnCode); - } - - const responseData = response.slice(0, response.length - 2); - return { returnCode, data: responseData }; - } - - private mountApdu(cla: number, ins: COMMAND, p1: number, p2: number, data: Buffer): Buffer { - return Buffer.concat([ - Serialize.uint8(cla), - Serialize.uint8(ins), - Serialize.uint8(p1), - Serialize.uint8(p2), - Serialize.uint8(data.length), - data - ]); - } -} diff --git a/src/interactions/deriveAddress.ts b/src/interactions/deriveAddress.ts index 0f770d0..f43e738 100644 --- a/src/interactions/deriveAddress.ts +++ b/src/interactions/deriveAddress.ts @@ -1,13 +1,13 @@ -import Device, { COMMAND } from "./common/device"; -import { DerivedAddress, Network } from "../types/public"; -import { DeviceResponse } from "../types/internal"; -import { RETURN_CODE } from "../errors"; -import Serialize from "../serialization/serialize"; -import Deserialize from "../serialization/deserialize"; +import { COMMAND, RETURN_CODE, type Device } from "../device"; +import type { DerivedAddress } from "../types/public"; +import type { DeviceResponse } from "../types/internal"; +import { pathToArray, serialize } from "../serialization/serialize"; +import { deserialize } from "../serialization/deserialize"; +import type { Network } from "@fleet-sdk/common"; const enum ReturnType { - Return, - Display + Return = 0x01, + Display = 0x02 } const enum P1 { @@ -20,38 +20,64 @@ const enum P2 { WITH_TOKEN = 0x02 } +const CHANGE_PATH_INDEX = 3; +const ALLOWED_CHANGE_PATHS = [0, 1]; + function sendDeriveAddress( device: Device, network: Network, - path: number[], + path: string, returnType: ReturnType, authToken?: number ): Promise { - const data = Buffer.concat([Buffer.alloc(1, network), Serialize.bip32Path(path)]); + const pathArray = pathToArray(path); + if (pathArray.length < 5) { + throw new Error(`Invalid path length. ${pathArray.length}`); + } + + const change = pathArray[CHANGE_PATH_INDEX]; + if (!ALLOWED_CHANGE_PATHS.includes(change)) { + throw new Error(`Invalid change path: ${change}`); + } + + const data = Buffer.concat([Buffer.alloc(1, network), serialize.path(pathArray)]); + return device.send( COMMAND.DERIVE_ADDRESS, - returnType == ReturnType.Return ? P1.RETURN : P1.DISPLAY, + returnType === ReturnType.Return ? P1.RETURN : P1.DISPLAY, authToken ? P2.WITH_TOKEN : P2.WITHOUT_TOKEN, - authToken ? Buffer.concat([data, Serialize.uint32(authToken)]) : data + authToken ? Buffer.concat([data, serialize.uint32(authToken)]) : data ); } export async function deriveAddress( device: Device, network: Network, - path: number[], + path: string, authToken?: number ): Promise { - const response = await sendDeriveAddress(device, network, path, ReturnType.Return, authToken); - return { addressHex: Deserialize.hex(response.data) }; + const response = await sendDeriveAddress( + device, + network, + path, + ReturnType.Return, + authToken + ); + return { addressHex: deserialize.hex(response.data) }; } export async function showAddress( device: Device, network: Network, - path: number[], + path: string, authToken?: number ): Promise { - const response = await sendDeriveAddress(device, network, path, ReturnType.Display, authToken); + const response = await sendDeriveAddress( + device, + network, + path, + ReturnType.Display, + authToken + ); return response.returnCode === RETURN_CODE.OK; } diff --git a/src/interactions/getAppName.ts b/src/interactions/getAppName.ts index d4108de..a8ba27e 100644 --- a/src/interactions/getAppName.ts +++ b/src/interactions/getAppName.ts @@ -1,6 +1,6 @@ -import Device, { COMMAND } from "./common/device"; -import { AppName } from "../types/public"; -import Deserialize from "../serialization/deserialize"; +import { COMMAND, type Device } from "../device"; +import type { AppName } from "../types/public"; +import { deserialize } from "../serialization/deserialize"; const enum P1 { UNUSED = 0x00 @@ -11,6 +11,11 @@ const enum P2 { } export async function getAppName(device: Device): Promise { - const response = await device.send(COMMAND.GET_APP_NAME, P1.UNUSED, P2.UNUSED, Buffer.from([])); - return { name: Deserialize.ascii(response.data) }; + const response = await device.send( + COMMAND.GET_APP_NAME, + P1.UNUSED, + P2.UNUSED, + Buffer.from([]) + ); + return { name: deserialize.ascii(response.data) }; } diff --git a/src/interactions/getExtendedPublicKey.ts b/src/interactions/getExtendedPublicKey.ts index a456bcf..36f82fe 100644 --- a/src/interactions/getExtendedPublicKey.ts +++ b/src/interactions/getExtendedPublicKey.ts @@ -1,8 +1,8 @@ -import Device, { COMMAND } from "./common/device"; -import { ExtendedPublicKey } from "../types/public"; +import { COMMAND, type Device } from "../device"; +import type { ExtendedPublicKey } from "../types/public"; import { chunkBy } from "../serialization/utils"; -import Serialize from "../serialization/serialize"; -import Deserialize from "../serialization/deserialize"; +import { serialize } from "../serialization/serialize"; +import { deserialize } from "../serialization/deserialize"; const enum P1 { WITHOUT_TOKEN = 0x01, @@ -15,20 +15,20 @@ const enum P2 { export async function getExtendedPublicKey( device: Device, - path: number[], + path: string, authToken?: number ): Promise { - const data = Serialize.bip32Path(path); + const data = serialize.path(path); const response = await device.send( - COMMAND.GET_EXTENTED_PUB_KEY, + COMMAND.GET_EXTENDED_PUB_KEY, authToken ? P1.WITH_TOKEN : P1.WITHOUT_TOKEN, P2.UNUSED, - authToken ? Buffer.concat([data, Serialize.uint32(authToken)]) : data + authToken ? Buffer.concat([data, serialize.uint32(authToken)]) : data ); const [publicKey, chainCode] = chunkBy(response.data, [33, 32]); return { - publicKey: Deserialize.hex(publicKey), - chainCode: Deserialize.hex(chainCode) + publicKey: deserialize.hex(publicKey), + chainCode: deserialize.hex(chainCode) }; } diff --git a/src/interactions/getVersion.ts b/src/interactions/getVersion.ts index 2fa0f5b..d3bebc1 100644 --- a/src/interactions/getVersion.ts +++ b/src/interactions/getVersion.ts @@ -1,7 +1,7 @@ -import { Version } from "../types/public"; -import Device, { COMMAND } from "./common/device"; +import type { Version } from "../types/public"; +import { COMMAND, type Device } from "../device"; -const FLAG_IS_DEBUG = 0x01; +const IS_DEBUG_FLAG = 0x01; const enum P1 { UNUSED = 0x00 @@ -23,6 +23,6 @@ export async function getAppVersion(device: Device): Promise { major: response.data[0], minor: response.data[1], patch: response.data[2], - flags: { isDebug: response.data[3] == FLAG_IS_DEBUG } + flags: { isDebug: response.data[3] === IS_DEBUG_FLAG } }; } diff --git a/src/interactions/signTx.ts b/src/interactions/signTx.ts index 9965d54..eed172c 100644 --- a/src/interactions/signTx.ts +++ b/src/interactions/signTx.ts @@ -1,10 +1,10 @@ -import AttestedBox from "../models/attestedBox"; -import Deserialize from "../serialization/deserialize"; -import Serialize from "../serialization/serialize"; -import { ChangeMap, BoxCandidate, Token, Network } from "../types/public"; -import Device, { COMMAND } from "./common/device"; -import { ErgoAddress } from "@fleet-sdk/core"; -import { AttestedTx } from "../types/internal"; +import { deserialize } from "../serialization/deserialize"; +import { serialize } from "../serialization/serialize"; +import type { ChangeMap, BoxCandidate, Token } from "../types/public"; +import { COMMAND, type Device } from "../device"; +import { ErgoAddress, type Network } from "@fleet-sdk/core"; +import type { AttestedTransaction } from "../types/internal"; +import type { AttestedBox } from "../types/attestedBox"; const MINER_FEE_TREE = "1005040004000e36100204a00b08cd0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ea02d192a39a8cc7a701730073011001020402d19683030193a38cc7b2a57300000193c2b2a57301007473027303830108cdeeac93b1a57304"; @@ -32,7 +32,7 @@ const enum P2 { export async function signTx( device: Device, - tx: AttestedTx, + tx: AttestedTransaction, signPath: string, network: Network, authToken?: number @@ -43,9 +43,9 @@ export async function signTx( await sendInputs(device, sessionId, tx.inputs); await sendDataInputs(device, sessionId, tx.dataInputs); await sendOutputs(device, sessionId, tx.outputs, tx.changeMap, tx.distinctTokenIds); - const signBytes = await sendConfirmAndSign(device, sessionId); + const proof = await sendConfirmAndSign(device, sessionId); - return new Uint8Array(signBytes); + return new Uint8Array(proof); } async function sendHeader( @@ -59,9 +59,9 @@ async function sendHeader( P1.START_SIGNING, authToken ? P2.WITH_TOKEN : P2.WITHOUT_TOKEN, Buffer.concat([ - Serialize.uint8(network), - Serialize.bip32Path(path), - authToken ? Serialize.uint32(authToken) : Buffer.alloc(0) + serialize.uint8(network), + serialize.path(path), + authToken ? serialize.uint32(authToken) : Buffer.alloc(0) ]) ); @@ -71,7 +71,7 @@ async function sendHeader( async function sendStartTx( device: Device, sessionId: number, - tx: AttestedTx, + tx: AttestedTransaction, uniqueTokenIdsCount: number ): Promise { const response = await device.send( @@ -79,42 +79,50 @@ async function sendStartTx( P1.START_TRANSACTION, sessionId, Buffer.concat([ - Serialize.uint16(tx.inputs.length), - Serialize.uint16(tx.dataInputs.length), - Serialize.uint8(uniqueTokenIdsCount), - Serialize.uint16(tx.outputs.length) + serialize.uint16(tx.inputs.length), + serialize.uint16(tx.dataInputs.length), + serialize.uint8(uniqueTokenIdsCount), + serialize.uint16(tx.outputs.length) ]) ); return response.data[0]; } -async function sendDistinctTokensIds(device: Device, sessionId: number, ids: Uint8Array[]) { - if (ids.length === 0) { - return; - } +async function sendDistinctTokensIds( + device: Device, + sessionId: number, + ids: Uint8Array[] +) { + if (ids.length === 0) return; const MAX_PACKET_SIZE = 7; - const packets = Serialize.arrayAndChunk(ids, MAX_PACKET_SIZE, (id) => Buffer.from(id)); + const packets = serialize.arrayAsMappedChunks(ids, MAX_PACKET_SIZE, (id) => + Buffer.from(id) + ); - for (let p of packets) { + for (const p of packets) { await device.send(COMMAND.SIGN_TX, P1.ADD_TOKEN_IDS, sessionId, p); } } -async function sendInputs(device: Device, sessionId: number, inputBoxes: AttestedBox[]) { - for (let box of inputBoxes) { - for (let frame of box.frames) { - await device.send(COMMAND.SIGN_TX, P1.ADD_INPUT_BOX_FRAME, sessionId, frame.buffer); +async function sendInputs(device: Device, sessionId: number, inputs: AttestedBox[]) { + for (const input of inputs) { + for (const frame of input.frames) { + await device.send(COMMAND.SIGN_TX, P1.ADD_INPUT_BOX_FRAME, sessionId, frame.bytes); } - if (box.extension !== undefined && box.extension.length > 0) { - await sendBoxContextExtension(device, sessionId, box.extension); + if (input.extension !== undefined && input.extension.length > 0) { + await sendBoxContextExtension(device, sessionId, input.extension); } } } -async function sendBoxContextExtension(device: Device, sessionId: number, extension: Buffer) { +async function sendBoxContextExtension( + device: Device, + sessionId: number, + extension: Buffer +) { await device.sendData( COMMAND.SIGN_TX, P1.ADD_INPUT_BOX_CONTEXT_EXTENSION_CHUNK, @@ -125,9 +133,9 @@ async function sendBoxContextExtension(device: Device, sessionId: number, extens async function sendDataInputs(device: Device, sessionId: number, boxIds: string[]) { const MAX_PACKET_SIZE = 7; - const packets = Serialize.arrayAndChunk(boxIds, MAX_PACKET_SIZE, (id) => Serialize.hex(id)); + const packets = serialize.arrayAsMappedChunks(boxIds, MAX_PACKET_SIZE, serialize.hex); - for (let p of packets) { + for (const p of packets) { await device.send(COMMAND.SIGN_TX, P1.ADD_DATA_INPUTS, sessionId, p); } } @@ -141,21 +149,21 @@ async function sendOutputs( ) { const distinctTokenIdsStr = distinctTokenIds.map((t) => Buffer.from(t).toString("hex")); - for (let box of boxes) { + for (const box of boxes) { await device.send( COMMAND.SIGN_TX, P1.ADD_OUTPUT_BOX_START, sessionId, Buffer.concat([ - Serialize.uint64(box.value), - Serialize.uint32(box.ergoTree.length), - Serialize.uint32(box.creationHeight), - Serialize.uint8(box.tokens.length), - Serialize.uint32(box.registers.length) + serialize.uint64(box.value), + serialize.uint32(box.ergoTree.length), + serialize.uint32(box.creationHeight), + serialize.uint8(box.tokens.length), + serialize.uint32(box.registers.length) ]) ); - const tree = Deserialize.hex(box.ergoTree); + const tree = deserialize.hex(box.ergoTree); if (tree === MINER_FEE_TREE) { await addOutputBoxMinersFeeTree(device, sessionId); } else if (ErgoAddress.fromErgoTree(tree).toString() === changeMap.address) { @@ -175,11 +183,21 @@ async function sendOutputs( } async function addOutputBoxErgoTree(device: Device, sessionId: number, ergoTree: Buffer) { - await device.sendData(COMMAND.SIGN_TX, P1.ADD_OUTPUT_BOX_ERGO_TREE_CHUNK, sessionId, ergoTree); + await device.sendData( + COMMAND.SIGN_TX, + P1.ADD_OUTPUT_BOX_ERGO_TREE_CHUNK, + sessionId, + ergoTree + ); } async function addOutputBoxMinersFeeTree(device: Device, sessionId: number) { - await device.send(COMMAND.SIGN_TX, P1.ADD_OUTPUT_BOX_MINERS_FEE_TREE, sessionId, Buffer.from([])); + await device.send( + COMMAND.SIGN_TX, + P1.ADD_OUTPUT_BOX_MINERS_FEE_TREE, + sessionId, + Buffer.from([]) + ); } async function addOutputBoxChangeTree(device: Device, sessionId: number, path: string) { @@ -187,7 +205,7 @@ async function addOutputBoxChangeTree(device: Device, sessionId: number, path: s COMMAND.SIGN_TX, P1.ADD_OUTPUT_BOX_CHANGE_TREE, sessionId, - Serialize.bip32Path(path) + serialize.path(path) ); } @@ -201,14 +219,26 @@ async function addOutputBoxTokens( COMMAND.SIGN_TX, P1.ADD_OUTPUT_BOX_TOKENS, sessionId, - Serialize.array(tokens, (t) => - Buffer.concat([Serialize.uint32(distinctTokenIds.indexOf(t.id)), Serialize.uint64(t.amount)]) + serialize.array(tokens, (t) => + Buffer.concat([ + serialize.uint32(distinctTokenIds.indexOf(t.id)), + serialize.uint64(t.amount) + ]) ) ); } -async function addOutputBoxRegisters(device: Device, sessionId: number, registers: Buffer) { - await device.sendData(COMMAND.SIGN_TX, P1.ADD_OUTPUT_BOX_REGISTERS_CHUNK, sessionId, registers); +async function addOutputBoxRegisters( + device: Device, + sessionId: number, + registers: Buffer +) { + await device.sendData( + COMMAND.SIGN_TX, + P1.ADD_OUTPUT_BOX_REGISTERS_CHUNK, + sessionId, + registers + ); } async function sendConfirmAndSign(device: Device, sessionId: number): Promise { diff --git a/src/models/attestedBox.ts b/src/models/attestedBox.ts deleted file mode 100644 index e783b42..0000000 --- a/src/models/attestedBox.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { AttestedBoxFrame, UnsignedBox } from "../types/public"; -import { assert } from "../validations"; - -export default class AttestedBox { - private _box: UnsignedBox; - private _frames: AttestedBoxFrame[]; - private _extension?: Buffer; - - constructor(box: UnsignedBox, frames: AttestedBoxFrame[]) { - this._box = box; - this._frames = frames; - } - - public get box(): UnsignedBox { - return this._box; - } - - public get frames(): AttestedBoxFrame[] { - return this._frames; - } - - public get extension(): Buffer | undefined { - return this._extension; - } - - public setExtension(extension: Buffer): AttestedBox { - assert(!this._extension, "extension already present"); - - const lengthBuffer = Buffer.alloc(4); - const firstFrame = this._frames[0]; - if (extension.length === 1 && extension[0] === 0) { - lengthBuffer.writeUInt32BE(0, 0); - } else { - this._extension = extension; - firstFrame.extensionLength = extension.length; - lengthBuffer.writeUInt32BE(extension.length, 0); - } - - firstFrame.buffer = Buffer.concat([firstFrame.buffer, lengthBuffer]); - - return this; - } -} diff --git a/src/serialization/deserialize.ts b/src/serialization/deserialize.ts index 6b2c8f3..ce91a9d 100644 --- a/src/serialization/deserialize.ts +++ b/src/serialization/deserialize.ts @@ -1,38 +1,28 @@ import basex from "base-x"; -import { assert } from "../validations"; +import { assert } from "@fleet-sdk/common"; const bs10 = basex("0123456789"); -export default class Deserialize { - public static hex(buffer: Buffer): string { +export const deserialize = { + hex(buffer: Buffer): string { return buffer.toString("hex"); - } + }, - public static ascii(buffer: Buffer): string { + ascii(buffer: Buffer): string { return buffer.toString("ascii"); - } + }, - public static uint8(data: Buffer): number { + uint8(data: Buffer): number { assert(data.length === 1, "invalid uint8 buffer"); return data.readUIntBE(0, 1); - } - - public static uint16(data: Buffer): number { - assert(data.length === 2, "invalid uint16 buffer"); - return data.readUIntBE(0, 2); - } - - public static uint32(data: Buffer): number { - assert(data.length === 4, "invalid uint32 buffer"); - return data.readUIntBE(0, 4); - } + }, - public static uint64(buffer: Buffer): string { + uint64(buffer: Buffer): string { assert(buffer.length === 8, "invalid uint64 buffer"); - return this.trimLeadingZeros(bs10.encode(buffer)); + return trimLeadingZeros(bs10.encode(buffer)); } +}; - private static trimLeadingZeros(text: string): string { - return text.replace(/^0+/, ""); - } +function trimLeadingZeros(text: string): string { + return text.replace(/^0+/, ""); } diff --git a/src/serialization/serialize.spec.ts b/src/serialization/serialize.spec.ts index 5e0e49e..b36a32f 100644 --- a/src/serialization/serialize.spec.ts +++ b/src/serialization/serialize.spec.ts @@ -1,13 +1,15 @@ import { describe, expect, it } from "vitest"; -import Serialize from "./serialize"; +import { serialize } from "./serialize"; describe("serializations", () => { describe("serialize class", () => { it("should serialize and split", () => { const MAX_CHUNK_LENGTH = 3; const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - const chunks = Serialize.arrayAndChunk(arr, MAX_CHUNK_LENGTH, (n) => - Serialize.uint8(n) + const chunks = serialize.arrayAsMappedChunks( + arr, + MAX_CHUNK_LENGTH, + serialize.uint8 ); expect(chunks).toHaveLength(4); diff --git a/src/serialization/serialize.ts b/src/serialization/serialize.ts index 06ec167..0885832 100644 --- a/src/serialization/serialize.ts +++ b/src/serialization/serialize.ts @@ -1,96 +1,90 @@ -import { - assert, - isHexString, - isUint16, - isUint32, - isUint64String, - isUint8, - isValidBip32Path -} from "../validations"; +import { isHex, assert } from "@fleet-sdk/common"; +import { isUint16, isUint32, isUint64String, isUint8, isErgoPath } from "../assertions"; import basex from "base-x"; import bip32Path from "bip32-path"; const bs10 = basex("0123456789"); -export default class Serialize { - public static bip32Path(path: number[] | string): Buffer { - var pathArray = typeof path === "string" ? this.bip32PathAsArray(path) : path; +export const serialize = { + path(path: number[] | string): Buffer { + const pathArray = typeof path === "string" ? pathToArray(path) : path; + assert(isErgoPath(pathArray), "Invalid Ergo path"); const buffer = Buffer.alloc(1 + pathArray.length * 4); buffer[0] = pathArray.length; - pathArray.forEach((element: any, index: number) => { - buffer.writeUInt32BE(element, 1 + 4 * index); - }); - return buffer; - } + for (let i = 0; i < pathArray.length; i++) { + buffer.writeUInt32BE(pathArray[i], 1 + 4 * i); + } - public static bip32PathAsArray(path: string): number[] { - assert(isValidBip32Path(path), "Invalid Bip32 path."); - return bip32Path.fromString(path).toPathArray(); - } + return buffer; + }, - public static uint8(value: number): Buffer { + uint8(value: number): Buffer { assert(isUint8(value), "invalid uint8 value"); const data = Buffer.alloc(1); data.writeUInt8(value, 0); return data; - } + }, - public static uint16(value: number): Buffer { + uint16(value: number): Buffer { assert(isUint16(value), "invalid uint16 value"); const data = Buffer.alloc(2); data.writeUInt16BE(value, 0); return data; - } + }, - public static uint32(value: number): Buffer { + uint32(value: number): Buffer { assert(isUint32(value), "invalid uint32 value"); const buffer = Buffer.alloc(4); buffer.writeUInt32BE(value, 0); return buffer; - } + }, - public static uint64(value: string): Buffer { + uint64(value: string): Buffer { assert(isUint64String(value), "invalid uint64 string"); const data = bs10.decode(value); - assert(data.length <= 8, "excessive data"); const padding = Buffer.alloc(8 - data.length); return Buffer.concat([padding, Buffer.from(data)]); - } + }, - public static hex(data: string): Buffer { - assert(isHexString(data), "invalid hex string"); + hex(data: string): Buffer { + assert(isHex(data), "invalid hex string"); return Buffer.from(data, "hex"); - } + }, - public static array(data: T[], serializeCallback: (value: T) => Buffer): Buffer { + array(data: T[], serializeCallback: (value: T) => Buffer): Buffer { const chucks: Buffer[] = []; for (let i = 0; i < data.length; i++) { chucks.push(serializeCallback(data[i])); } return Buffer.concat(chucks); - } + }, - public static arrayAndChunk( + arrayAsMappedChunks( data: T[], - maxPacketSize: number, - serializeCallback: (value: T) => Buffer + maxSize: number, + encode: (value: T) => Buffer ): Buffer[] { const packets = []; - for (let i = 0; i < Math.ceil(data.length / maxPacketSize); i++) { + for (let i = 0; i < Math.ceil(data.length / maxSize); i++) { const chunks = []; - for (let j = i * maxPacketSize; j < Math.min((i + 1) * maxPacketSize, data.length); j++) { - chunks.push(serializeCallback(data[j])); + for (let j = i * maxSize; j < Math.min((i + 1) * maxSize, data.length); j++) { + chunks.push(encode(data[j])); } + packets.push(Buffer.concat(chunks)); } return packets; } +}; + +export function pathToArray(path: string): number[] { + return bip32Path.fromString(path).toPathArray(); } diff --git a/src/serialization/utils.spec.ts b/src/serialization/utils.spec.ts index 0b73f73..212acb1 100644 --- a/src/serialization/utils.spec.ts +++ b/src/serialization/utils.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { chunkBy, uniq } from "./utils"; +import { chunkBy } from "./utils"; describe("Utils test", () => { it("should chunk buffers", () => { @@ -9,9 +9,4 @@ describe("Utils test", () => { expect(first.length).toEqual(5); expect(last.length).toEqual(6); }); - - it("should return a duplicate free array", () => { - const array = ["a", "b", "a", "c", "c", "a", "d", "a"]; - expect(uniq(array)).toEqual(["a", "b", "c", "d"]); - }); }); diff --git a/src/serialization/utils.ts b/src/serialization/utils.ts index 6f07bf9..d475118 100644 --- a/src/serialization/utils.ts +++ b/src/serialization/utils.ts @@ -1,28 +1,18 @@ -import { assert, isArray, isBuffer, isInteger } from "../validations"; +import { assert } from "@fleet-sdk/common"; -const sum = (arr: Array) => arr.reduce((x, y) => x + y, 0); +const sum = (arr: number[]) => arr.reduce((x, y) => x + y, 0); -export function uniq(array: T[]): T[] { - return [...new Set(array)]; -} - -export function chunkBy(data: Buffer, chunkLengths: Array) { - assert(isBuffer(data), "invalid buffer"); - assert(isArray(chunkLengths), "invalid chunks"); - for (const len of chunkLengths) { - assert(isInteger(len), "bad chunk length"); - assert(len > 0, "bad chunk length"); - } - assert(data.length <= sum(chunkLengths), "data too short"); +export function chunkBy(data: Buffer, chunkLengths: number[]) { + assert(data.length >= sum(chunkLengths), "data is too small"); let offset = 0; const result = []; const restLength = data.length - sum(chunkLengths); - for (let c of [...chunkLengths, restLength]) { - result.push(data.slice(offset, offset + c)); - - offset += c; + for (const length of [...chunkLengths, restLength]) { + assert(length >= 0, `bad chunk length: ${length}`); + result.push(data.subarray(offset, offset + length)); + offset += length; } return result; diff --git a/src/types/attestedBox.ts b/src/types/attestedBox.ts new file mode 100644 index 0000000..d633473 --- /dev/null +++ b/src/types/attestedBox.ts @@ -0,0 +1,43 @@ +import { assert } from "@fleet-sdk/common"; +import type { UnsignedBox, AttestedBoxFrame } from "./public"; + +export class AttestedBox { + #box: UnsignedBox; + #frames: AttestedBoxFrame[]; + #extension?: Buffer; + + constructor(box: UnsignedBox, frames: AttestedBoxFrame[]) { + this.#box = box; + this.#frames = frames; + } + + public get box(): UnsignedBox { + return this.#box; + } + + public get frames(): AttestedBoxFrame[] { + return this.#frames; + } + + public get extension(): Buffer | undefined { + return this.#extension; + } + + public setExtension(extension: Buffer): AttestedBox { + assert(!this.#extension, "The extension is already inserted"); + + const lengthBuffer = Buffer.alloc(4); + const firstFrame = this.#frames[0]; + if (extension.length === 1 && extension[0] === 0) { + lengthBuffer.writeUInt32BE(0, 0); + } else { + this.#extension = extension; + firstFrame.extensionLength = extension.length; + lengthBuffer.writeUInt32BE(extension.length, 0); + } + + firstFrame.bytes = Buffer.concat([firstFrame.bytes, lengthBuffer]); + + return this; + } +} diff --git a/src/bip32-path.d.ts b/src/types/bip32-path.d.ts similarity index 100% rename from src/bip32-path.d.ts rename to src/types/bip32-path.d.ts diff --git a/src/types/internal.ts b/src/types/internal.ts index 0d83c40..d1a1a1a 100644 --- a/src/types/internal.ts +++ b/src/types/internal.ts @@ -1,13 +1,13 @@ -import { RETURN_CODE } from "../errors"; -import AttestedBox from "../models/attestedBox"; -import { BoxCandidate, ChangeMap } from "./public"; +import type { RETURN_CODE } from "../device"; +import type { BoxCandidate, ChangeMap } from "./public"; +import type { AttestedBox } from "./attestedBox"; export type DeviceResponse = { data: Buffer; returnCode: RETURN_CODE; }; -export type AttestedTx = { +export type AttestedTransaction = { inputs: AttestedBox[]; dataInputs: string[]; outputs: BoxCandidate[]; @@ -15,6 +15,6 @@ export type AttestedTx = { changeMap: ChangeMap; }; -export type SignTxResponse = { +export type SignTransactionResponse = { [path: string]: Uint8Array; }; diff --git a/src/types/public.ts b/src/types/public.ts index 1aed31c..6508fb2 100644 --- a/src/types/public.ts +++ b/src/types/public.ts @@ -64,16 +64,16 @@ export type BoxCandidate = { export type AttestedBoxFrame = { boxId: string; - framesCount: number; - frameIndex: number; + count: number; + index: number; amount: string; tokens: Token[]; attestation: string; extensionLength?: number; - buffer: Buffer; + bytes: Buffer; }; -export type UnsignedTx = { +export type UnsignedTransaction = { inputs: UnsignedBox[]; dataInputs: string[]; outputs: BoxCandidate[]; @@ -85,8 +85,3 @@ export type ChangeMap = { address: string; path: string; }; - -export enum Network { - Mainnet = 0 << 4, - Testnet = 1 << 4 -} diff --git a/src/validations/assert.ts b/src/validations/assert.ts deleted file mode 100644 index 492e8e2..0000000 --- a/src/validations/assert.ts +++ /dev/null @@ -1,5 +0,0 @@ -export function assert(cond: boolean, errMsg: string): asserts cond { - if (!cond) { - throw new Error(`Assertion failed${errMsg ? `: ${errMsg}` : "."}`); - } -} diff --git a/src/validations/index.ts b/src/validations/index.ts deleted file mode 100644 index 66fe245..0000000 --- a/src/validations/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./assert"; -export * from "./parse"; diff --git a/src/validations/parse.ts b/src/validations/parse.ts deleted file mode 100644 index 5792fea..0000000 --- a/src/validations/parse.ts +++ /dev/null @@ -1,76 +0,0 @@ -import bip32Path from "bip32-path"; - -const MIN_UINT_64_STR = "0"; -const MAX_UINT_64_STR = "18446744073709551615"; -const MIN_UINT_VALUE = 0; -const MAX_UINT32_VALUE = 4294967295; -const MAX_UINT16_VALUE = 65535; -const MAX_UNIT8_VALUE = 255; - -export function isValidBip32Path(path: number[] | string): boolean { - if (typeof path == "string") { - return bip32Path.validateString(path, true); - } - - return bip32Path.validatePathArray(path, true); -} - -export function isValidErgoPath(path: number[]): boolean { - if (path.length < 2) { - return false; - } - - const [pathPurpose, pathCoinType] = path; - const [ergoPurpose, ergoCoinType] = bip32Path.fromString("m/44'/429'").toPathArray(); - return pathPurpose === ergoPurpose && pathCoinType === ergoCoinType; -} - -export function isInteger(data: unknown): boolean { - return Number.isInteger(data); -} - -export function isArray(data: unknown): boolean { - return Array.isArray(data); -} - -export function isBuffer(data: unknown): boolean { - return Buffer.isBuffer(data); -} - -export function isUint32(data: unknown): boolean { - return ( - typeof data == "number" && isInteger(data) && data >= MIN_UINT_VALUE && data <= MAX_UINT32_VALUE - ); -} - -export function isUint16(data: number): boolean { - return ( - typeof data == "number" && isInteger(data) && data >= MIN_UINT_VALUE && data <= MAX_UINT16_VALUE - ); -} - -export function isUint8(data: unknown): boolean { - return ( - typeof data == "number" && isInteger(data) && data >= MIN_UINT_VALUE && data <= MAX_UNIT8_VALUE - ); -} - -export function isUint64String(data: string): boolean { - return ( - typeof data === "string" && - /^[0-9]*$/.test(data) && - // Length checks - data.length > 0 && - data.length <= MAX_UINT_64_STR.length && - // Leading zeros - (data.length === 1 || data[0] !== "0") && - // less or equal than max value - // Note: this is string comparison! - (data.length < MAX_UINT_64_STR.length || data <= MAX_UINT_64_STR) && - // Note: this is string comparison! - (data.length > MIN_UINT_64_STR.length || data >= MIN_UINT_64_STR) - ); -} - -export const isHexString = (data: unknown) => - typeof data === "string" && data.length % 2 === 0 && /^[0-9a-fA-F]*$/.test(data);