@onekeyfe/hd-transport 1.2.8-alpha.9 → 1.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS,
3
+ PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW,
3
4
  PROTOCOL_V2_PACKET_SRC_COMMAND,
4
5
  } from '../../constants';
5
6
  import { ProtocolV2FrameAssembler, concatUint8Arrays } from './frame-assembler';
@@ -9,13 +10,18 @@ import {
9
10
  createProtocolV2LinkDisabledError,
10
11
  isProtocolV2LinkDisabledError,
11
12
  isProtocolV2LinkDisabledFailure,
13
+ isProtocolV2LinkError,
12
14
  } from './errors';
13
15
  import { ProtocolV2 } from '..';
14
16
  import * as check from '../../utils/highlevel-checks';
15
17
  import { LogBlockCommand } from '../../utils/logBlockCommand';
16
18
 
17
19
  import type { Root } from 'protobufjs/light';
18
- import type { MessageFromOneKey, TransportWriteMetrics } from '../../types';
20
+ import type {
21
+ MessageFromOneKey,
22
+ ProtocolV2FileWritePipeline,
23
+ TransportWriteMetrics,
24
+ } from '../../types';
19
25
 
20
26
  export * from './errors';
21
27
 
@@ -62,6 +68,7 @@ export type ProtocolV2CallOptions = {
62
68
  returnAfterWrite?: boolean;
63
69
  onResponseAfterWrite?: (response: MessageFromOneKey) => void;
64
70
  writeWithResponse?: boolean;
71
+ fileWritePipeline?: ProtocolV2FileWritePipeline;
65
72
  };
66
73
 
67
74
  export { concatUint8Arrays, ProtocolV2FrameAssembler };
@@ -214,6 +221,30 @@ export async function withProtocolTimeout<T>(
214
221
  }
215
222
  }
216
223
 
224
+ const resolveProtocolV2CallTimeoutMs = (timeoutMs?: number) =>
225
+ timeoutMs && timeoutMs > 0 ? timeoutMs : PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS;
226
+
227
+ function parseFileWriteChunk(request: Record<string, unknown>) {
228
+ const file = request.file as
229
+ | { path?: unknown; offset?: unknown; total_size?: unknown; data?: { byteLength?: unknown } }
230
+ | undefined;
231
+ const offset = Number(file?.offset);
232
+ const totalSize = Number(file?.total_size);
233
+ const length = Number(file?.data?.byteLength);
234
+ if (
235
+ typeof file?.path !== 'string' ||
236
+ !Number.isSafeInteger(offset) ||
237
+ offset < 0 ||
238
+ !Number.isSafeInteger(totalSize) ||
239
+ !Number.isSafeInteger(length) ||
240
+ length <= 0 ||
241
+ offset + length > totalSize
242
+ ) {
243
+ throw new Error('Protocol V2 file write pipeline chunk is invalid');
244
+ }
245
+ return { path: file.path, offset, end: offset + length, totalSize };
246
+ }
247
+
217
248
  export class ProtocolV2Session {
218
249
  private readonly options: ProtocolV2SessionOptions;
219
250
 
@@ -293,28 +324,46 @@ export class ProtocolV2Session {
293
324
  return result;
294
325
  }
295
326
 
327
+ private reportWriteCompleted(
328
+ callOptions: ProtocolV2CallOptions,
329
+ writeStartedAt: number,
330
+ frameBytes: number
331
+ ) {
332
+ try {
333
+ callOptions.onWriteCompleted?.({
334
+ elapsedMs: Math.max(Date.now() - writeStartedAt, 0),
335
+ frameBytes,
336
+ });
337
+ } catch (error) {
338
+ const { logger, logPrefix = 'ProtocolV2' } = this.options;
339
+ logger?.error?.(`${logPrefix} write metrics callback failed: ${String(error)}`);
340
+ }
341
+ }
342
+
343
+ private createCallTimeoutError(name: string, timeoutMs: number): Error {
344
+ const { createTimeoutError } = this.options;
345
+ return createTimeoutError
346
+ ? createTimeoutError(name, timeoutMs)
347
+ : new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${name}`);
348
+ }
349
+
296
350
  private executeCall(
297
351
  name: string,
298
352
  data: Record<string, unknown>,
299
353
  callOptions: ProtocolV2CallOptions
300
354
  ): Promise<MessageFromOneKey> {
355
+ if (callOptions.fileWritePipeline) {
356
+ return this.executeFileWritePipeline(name, data, callOptions, callOptions.fileWritePipeline);
357
+ }
301
358
  const {
302
- schemas,
303
- router,
304
- packetSrc = PROTOCOL_V2_PACKET_SRC_COMMAND,
305
- maxFrameBytes,
306
359
  prepareCall,
307
360
  writeFrame,
308
361
  readFrame,
309
362
  logger,
310
363
  logPrefix = 'ProtocolV2',
311
- createTimeoutError,
312
364
  generation = 0,
313
365
  } = this.options;
314
- const timeoutMs =
315
- callOptions.timeoutMs && callOptions.timeoutMs > 0
316
- ? callOptions.timeoutMs
317
- : PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS;
366
+ const timeoutMs = resolveProtocolV2CallTimeoutMs(callOptions.timeoutMs);
318
367
  const abortController = new AbortController();
319
368
 
320
369
  const shouldReduceDebug = shouldReduceProtocolV2Debug(name);
@@ -335,29 +384,11 @@ export class ProtocolV2Session {
335
384
  if (!this.pendingResponseAfterWrite) {
336
385
  await prepareCall?.(baseCallContext);
337
386
  }
338
- const protoSeq = this.sequenceCursor.next();
339
- const frame = ProtocolV2.encodeFrame(schemas, name, data, {
340
- packetSrc,
341
- router,
342
- seq: protoSeq,
343
- });
344
-
345
- if (maxFrameBytes !== undefined && frame.length > maxFrameBytes) {
346
- throw new Error(
347
- `Protocol V2 frame too large for transport: ${frame.length} > ${maxFrameBytes}`
348
- );
349
- }
387
+ const { seq: protoSeq, frame } = this.encodeRequestFrame(name, data);
350
388
 
351
389
  const writeStartedAt = Date.now();
352
390
  await this.serializeWrite(() => writeFrame(frame, baseCallContext));
353
- try {
354
- callOptions.onWriteCompleted?.({
355
- elapsedMs: Math.max(Date.now() - writeStartedAt, 0),
356
- frameBytes: frame.byteLength,
357
- });
358
- } catch (error) {
359
- logger?.error?.(`${logPrefix} write metrics callback failed: ${String(error)}`);
360
- }
391
+ this.reportWriteCompleted(callOptions, writeStartedAt, frame.byteLength);
361
392
 
362
393
  if (callOptions.returnAfterWrite) {
363
394
  if (callOptions.expectedTypes?.length) {
@@ -376,68 +407,8 @@ export class ProtocolV2Session {
376
407
  const rxFrame = await readFrame(baseCallContext);
377
408
  if (abortController.signal.aborted) break;
378
409
 
379
- let header: ReturnType<typeof ProtocolV2.inspectFrameHeader>;
380
- try {
381
- header = ProtocolV2.inspectFrameHeader(rxFrame);
382
- } catch (cause) {
383
- throw new ProtocolV2LinkError(
384
- 'frame',
385
- `Protocol V2 frame validation failed: ${getErrorMessage(cause)}`,
386
- cause
387
- );
388
- }
389
- if (header.router !== router) {
390
- throw new ProtocolV2LinkError(
391
- 'router',
392
- `Protocol V2 router mismatch: expected ${router}, got ${header.router}`
393
- );
394
- }
395
- if (header.packetSrc !== packetSrc) {
396
- throw new ProtocolV2LinkError(
397
- 'packet-source',
398
- `Protocol V2 packet source mismatch: expected ${packetSrc}, got ${header.packetSrc}`
399
- );
400
- }
401
-
402
- let isAck: boolean;
403
- try {
404
- isAck = ProtocolV2.isAckFrame(rxFrame);
405
- } catch (cause) {
406
- throw new ProtocolV2LinkError(
407
- 'frame',
408
- `Protocol V2 ACK validation failed: ${getErrorMessage(cause)}`,
409
- cause
410
- );
411
- }
412
- if (isAck) {
413
- if (header.seq !== protoSeq) {
414
- throw new ProtocolV2LinkError(
415
- 'ack-sequence',
416
- `Protocol V2 ACK sequence mismatch: expected ${protoSeq}, got ${header.seq}`
417
- );
418
- }
419
- } else {
420
- let decoded: ReturnType<typeof ProtocolV2.decodeFrame>;
421
- try {
422
- decoded = ProtocolV2.decodeFrame(schemas, rxFrame);
423
- } catch (cause) {
424
- throw new ProtocolV2LinkError(
425
- 'frame',
426
- `Protocol V2 frame decode failed: ${getErrorMessage(cause)}`,
427
- cause
428
- );
429
- }
430
- // Firmware owns one TX sequence across all channels and packet sources,
431
- // so frames routed elsewhere create valid gaps in this session.
432
- if (this.lastResponseSequence === decoded.seq) {
433
- throw new ProtocolV2LinkError(
434
- 'response-sequence',
435
- `Protocol V2 duplicate response sequence: ${decoded.seq}`
436
- );
437
- }
438
- this.lastResponseSequence = decoded.seq;
439
-
440
- const response = check.call(decoded);
410
+ const response = this.parseResponseFrame(rxFrame, [protoSeq]);
411
+ if (response) {
441
412
  const { pendingResponseAfterWrite } = this;
442
413
  let belongsToWriteOnlyCall = false;
443
414
  if (pendingResponseAfterWrite) {
@@ -477,15 +448,274 @@ export class ProtocolV2Session {
477
448
  return withProtocolTimeout(
478
449
  runCall(),
479
450
  timeoutMs,
480
- () =>
481
- createTimeoutError
482
- ? createTimeoutError(name, timeoutMs)
483
- : new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${name}`),
451
+ () => this.createCallTimeoutError(name, timeoutMs),
484
452
  () => {
485
453
  abortController.abort();
486
454
  }
487
455
  );
488
456
  }
457
+
458
+ private encodeRequestFrame(name: string, data: Record<string, unknown>) {
459
+ const {
460
+ schemas,
461
+ router,
462
+ packetSrc = PROTOCOL_V2_PACKET_SRC_COMMAND,
463
+ maxFrameBytes,
464
+ } = this.options;
465
+ const seq = this.sequenceCursor.next();
466
+ const frame = ProtocolV2.encodeFrame(schemas, name, data, { packetSrc, router, seq });
467
+ if (maxFrameBytes !== undefined && frame.length > maxFrameBytes) {
468
+ throw new Error(
469
+ `Protocol V2 frame too large for transport: ${frame.length} > ${maxFrameBytes}`
470
+ );
471
+ }
472
+ return { seq, frame };
473
+ }
474
+
475
+ /**
476
+ * Validates one received frame for this session. Returns undefined for an ACK of one of
477
+ * `ackSequences`, otherwise the decoded response.
478
+ */
479
+ private parseResponseFrame(
480
+ rxFrame: Uint8Array,
481
+ ackSequences: readonly number[]
482
+ ): MessageFromOneKey | undefined {
483
+ const { schemas, router, packetSrc = PROTOCOL_V2_PACKET_SRC_COMMAND } = this.options;
484
+ let header: ReturnType<typeof ProtocolV2.inspectFrameHeader>;
485
+ try {
486
+ header = ProtocolV2.inspectFrameHeader(rxFrame);
487
+ } catch (cause) {
488
+ throw new ProtocolV2LinkError(
489
+ 'frame',
490
+ `Protocol V2 frame validation failed: ${getErrorMessage(cause)}`,
491
+ cause
492
+ );
493
+ }
494
+ if (header.router !== router) {
495
+ throw new ProtocolV2LinkError(
496
+ 'router',
497
+ `Protocol V2 router mismatch: expected ${router}, got ${header.router}`
498
+ );
499
+ }
500
+ if (header.packetSrc !== packetSrc) {
501
+ throw new ProtocolV2LinkError(
502
+ 'packet-source',
503
+ `Protocol V2 packet source mismatch: expected ${packetSrc}, got ${header.packetSrc}`
504
+ );
505
+ }
506
+
507
+ let isAck: boolean;
508
+ try {
509
+ isAck = ProtocolV2.isAckFrame(rxFrame);
510
+ } catch (cause) {
511
+ throw new ProtocolV2LinkError(
512
+ 'frame',
513
+ `Protocol V2 ACK validation failed: ${getErrorMessage(cause)}`,
514
+ cause
515
+ );
516
+ }
517
+ if (isAck) {
518
+ if (!ackSequences.includes(header.seq)) {
519
+ throw new ProtocolV2LinkError(
520
+ 'ack-sequence',
521
+ `Protocol V2 ACK sequence mismatch: expected ${ackSequences.join('|')}, got ${header.seq}`
522
+ );
523
+ }
524
+ return undefined;
525
+ }
526
+
527
+ let decoded: ReturnType<typeof ProtocolV2.decodeFrame>;
528
+ try {
529
+ decoded = ProtocolV2.decodeFrame(schemas, rxFrame);
530
+ } catch (cause) {
531
+ throw new ProtocolV2LinkError(
532
+ 'frame',
533
+ `Protocol V2 frame decode failed: ${getErrorMessage(cause)}`,
534
+ cause
535
+ );
536
+ }
537
+ // Firmware owns one TX sequence across all channels and packet sources,
538
+ // so frames routed elsewhere create valid gaps in this session.
539
+ if (this.lastResponseSequence === decoded.seq) {
540
+ throw new ProtocolV2LinkError(
541
+ 'response-sequence',
542
+ `Protocol V2 duplicate response sequence: ${decoded.seq}`
543
+ );
544
+ }
545
+ this.lastResponseSequence = decoded.seq;
546
+ return check.call(decoded);
547
+ }
548
+
549
+ /**
550
+ * Writes one file as ordered FilesystemFileWrite chunks while keeping up to
551
+ * PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW chunks in flight. Firmware still processes and
552
+ * answers every chunk in order, so responses are matched to chunks by position. Upstream
553
+ * bootloaders append at their own cursor without checking the request offset, so the echoed
554
+ * offset is what exposes a lost chunk. A device Failure, or `next` returning undefined, stops
555
+ * sending; the remaining in-flight responses are drained so the link stays usable.
556
+ */
557
+ private async executeFileWritePipeline(
558
+ name: string,
559
+ data: Record<string, unknown>,
560
+ callOptions: ProtocolV2CallOptions,
561
+ pipeline: ProtocolV2FileWritePipeline
562
+ ): Promise<MessageFromOneKey> {
563
+ if (name !== 'FilesystemFileWrite') {
564
+ throw new Error(`Protocol V2 file write pipeline does not support ${name}`);
565
+ }
566
+ if (this.pendingResponseAfterWrite) {
567
+ throw new Error('Protocol V2 file write pipeline cannot start while a response is pending');
568
+ }
569
+ const { prepareCall, writeFrame, readFrame, generation = 0 } = this.options;
570
+ const timeoutMs = resolveProtocolV2CallTimeoutMs(callOptions.timeoutMs);
571
+ const abortController = new AbortController();
572
+ const context: ProtocolV2CallContext = {
573
+ messageName: name,
574
+ timeoutMs,
575
+ highThroughput: true,
576
+ ...(callOptions.writeWithResponse === undefined
577
+ ? {}
578
+ : { writeWithResponse: callOptions.writeWithResponse }),
579
+ generation,
580
+ signal: abortController.signal,
581
+ };
582
+ // Transport errors and timeouts propagate unchanged: each transport classifies its own.
583
+ let transportFailure: unknown;
584
+ // One file can take far longer than one chunk, so the watchdog bounds each step instead.
585
+ const withStepTimeout = <T>(promise: Promise<T>, stepTimeoutMs = timeoutMs) =>
586
+ withProtocolTimeout(
587
+ promise,
588
+ stepTimeoutMs,
589
+ () => this.createCallTimeoutError(name, timeoutMs),
590
+ () => {
591
+ abortController.abort();
592
+ }
593
+ ).catch((error: unknown) => {
594
+ transportFailure = error;
595
+ throw error;
596
+ });
597
+ // ACKs and skipped frames must not extend the wait for an in-flight chunk's response.
598
+ let progressDeadline = 0;
599
+ const markProgress = () => {
600
+ progressDeadline = Date.now() + timeoutMs;
601
+ };
602
+
603
+ const inFlight: Array<{ seq: number; offset: number; end: number }> = [];
604
+ let path: string | undefined;
605
+ let totalSize: number | undefined;
606
+ let nextOffset: number | undefined;
607
+ let sourceDone = false;
608
+ let failure: MessageFromOneKey | undefined;
609
+ let lastConfirmed: MessageFromOneKey | undefined;
610
+
611
+ const sendChunk = async (request: Record<string, unknown>) => {
612
+ const chunk = parseFileWriteChunk(request);
613
+ if (path !== undefined && (chunk.path !== path || chunk.totalSize !== totalSize)) {
614
+ throw new Error('Protocol V2 file write pipeline chunk belongs to a different file');
615
+ }
616
+ if (nextOffset !== undefined && chunk.offset !== nextOffset) {
617
+ throw new Error(
618
+ `Protocol V2 file write pipeline chunk out of order: expected offset ${nextOffset}, got ${chunk.offset}`
619
+ );
620
+ }
621
+ path = chunk.path;
622
+ totalSize = chunk.totalSize;
623
+ nextOffset = chunk.end;
624
+
625
+ const { seq, frame } = this.encodeRequestFrame(name, request);
626
+ const writeStartedAt = Date.now();
627
+ await withStepTimeout(this.serializeWrite(() => writeFrame(frame, context)));
628
+ inFlight.push({ seq, offset: chunk.offset, end: chunk.end });
629
+ markProgress();
630
+ this.reportWriteCompleted(callOptions, writeStartedAt, frame.byteLength);
631
+ };
632
+ const sendNextChunk = async () => {
633
+ const request = await pipeline.next();
634
+ if (!request) {
635
+ sourceDone = true;
636
+ return;
637
+ }
638
+ await sendChunk(request);
639
+ };
640
+
641
+ await withStepTimeout(Promise.resolve().then(() => prepareCall?.(context)));
642
+ try {
643
+ await sendChunk(data);
644
+ while (!sourceDone && inFlight.length < PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW) {
645
+ await sendNextChunk();
646
+ }
647
+
648
+ while (inFlight.length > 0) {
649
+ // Floor at 1 ms: withProtocolTimeout treats 0 as "no timeout", so an expired deadline
650
+ // must still time out.
651
+ const rxFrame = await withStepTimeout(
652
+ readFrame(context),
653
+ Math.max(progressDeadline - Date.now(), 1)
654
+ );
655
+ const response = this.parseResponseFrame(
656
+ rxFrame,
657
+ inFlight.map(item => item.seq)
658
+ );
659
+ if (!response) {
660
+ // ACK of an in-flight chunk; its FilesystemFile response follows.
661
+ } else if (response.type === 'FilesystemFile') {
662
+ const [expected] = inFlight;
663
+ const { message } = response;
664
+ if (
665
+ Number(message.processed_byte) !== expected.end ||
666
+ message.path !== path ||
667
+ Number(message.offset) !== expected.offset ||
668
+ Number(message.total_size) !== totalSize
669
+ ) {
670
+ throw new ProtocolV2LinkError(
671
+ 'frame',
672
+ `Protocol V2 file write response does not match chunk ${expected.offset}-${
673
+ expected.end
674
+ }: got offset ${String(message.offset)} processed_byte ${String(
675
+ message.processed_byte
676
+ )}`
677
+ );
678
+ }
679
+ inFlight.shift();
680
+ markProgress();
681
+ if (!failure) {
682
+ lastConfirmed = response;
683
+ pipeline.onResponse(response);
684
+ if (!sourceDone) {
685
+ await sendNextChunk();
686
+ }
687
+ }
688
+ } else if (response.type === 'Failure') {
689
+ inFlight.shift();
690
+ markProgress();
691
+ failure = failure ?? response;
692
+ sourceDone = true;
693
+ } else if (COMMON_TERMINAL_RESPONSE_TYPES.has(response.type)) {
694
+ throw new ProtocolV2LinkError(
695
+ 'frame',
696
+ `Protocol V2 file write pipeline received unexpected ${response.type}`
697
+ );
698
+ }
699
+ }
700
+ } catch (error) {
701
+ // Unread responses of in-flight chunks would answer the next call on this link, so a
702
+ // session or caller error raised with chunks in flight must invalidate it.
703
+ if (inFlight.length === 0 || error === transportFailure || isProtocolV2LinkError(error)) {
704
+ throw error;
705
+ }
706
+ throw new ProtocolV2LinkError(
707
+ 'frame',
708
+ `Protocol V2 file write pipeline aborted with chunks in flight: ${getErrorMessage(error)}`,
709
+ error
710
+ );
711
+ }
712
+
713
+ const terminal = failure ?? lastConfirmed;
714
+ if (!terminal) {
715
+ throw new ProtocolV2LinkError('frame', 'Protocol V2 file write pipeline ended unconfirmed');
716
+ }
717
+ return terminal;
718
+ }
489
719
  }
490
720
 
491
721
  export async function probeProtocolV2({
@@ -4554,6 +4554,28 @@ export enum CommandFlags {
4554
4554
  Factory_Only = 1,
4555
4555
  }
4556
4556
 
4557
+ export enum Enum_CanonicalBitcoinNetwork {
4558
+ BITCOIN_MAINNET = 0,
4559
+ BITCOIN_TESTNET = 1,
4560
+ BITCOIN_SIGNET = 2,
4561
+ BITCOIN_REGTEST = 3,
4562
+ }
4563
+ export type CanonicalBitcoinNetwork = keyof typeof Enum_CanonicalBitcoinNetwork;
4564
+
4565
+ // BabylonDeriveContextHash
4566
+ export type BabylonDeriveContextHash = {
4567
+ address_n: number[];
4568
+ script_type: InputScriptType;
4569
+ app_name: string;
4570
+ context: string;
4571
+ network: CanonicalBitcoinNetwork;
4572
+ };
4573
+
4574
+ // BabylonDerivedContextHash
4575
+ export type BabylonDerivedContextHash = {
4576
+ secret: string;
4577
+ };
4578
+
4557
4579
  export type TextMemo = {
4558
4580
  text: string;
4559
4581
  };
@@ -4658,6 +4680,70 @@ export type UiAnimationRequest = {
4658
4680
  type?: UiAnimationType;
4659
4681
  };
4660
4682
 
4683
+ export enum ZcashAddressType {
4684
+ UNIFIED_ORCHARD_P2PKH = 0,
4685
+ TRANSPARENT_P2PKH = 1,
4686
+ UNIFIED_ORCHARD = 2,
4687
+ }
4688
+
4689
+ export enum ZcashAddressScope {
4690
+ EXTERNAL = 0,
4691
+ INTERNAL = 1,
4692
+ }
4693
+
4694
+ // ZcashGetAddress
4695
+ export type ZcashGetAddress = {
4696
+ address_n: number[];
4697
+ show_display?: boolean;
4698
+ include_seed_fingerprint?: boolean;
4699
+ diversifier_index?: number;
4700
+ address_type?: ZcashAddressType;
4701
+ scope?: ZcashAddressScope;
4702
+ };
4703
+
4704
+ // ZcashAddress
4705
+ export type ZcashAddress = {
4706
+ address: string;
4707
+ seed_fingerprint?: string;
4708
+ };
4709
+
4710
+ // ZcashGetFullViewingKey
4711
+ export type ZcashGetFullViewingKey = {
4712
+ address_n: number[];
4713
+ };
4714
+
4715
+ // ZcashFullViewingKey
4716
+ export type ZcashFullViewingKey = {
4717
+ ufvk: string;
4718
+ };
4719
+
4720
+ // ZcashSignPczt
4721
+ export type ZcashSignPczt = {
4722
+ pczt_length: number;
4723
+ pczt_initial_chunk: string;
4724
+ };
4725
+
4726
+ // ZcashPcztChunkRequest
4727
+ export type ZcashPcztChunkRequest = {
4728
+ chunk_length: number;
4729
+ };
4730
+
4731
+ // ZcashPcztChunkAck
4732
+ export type ZcashPcztChunkAck = {
4733
+ data_chunk: string;
4734
+ };
4735
+
4736
+ // ZcashSignedPczt
4737
+ export type ZcashSignedPczt = {
4738
+ pczt_length: number;
4739
+ pczt_initial_chunk: string;
4740
+ };
4741
+
4742
+ // ZcashSignedPcztChunkRequest
4743
+ export type ZcashSignedPcztChunkRequest = {
4744
+ chunk_length: number;
4745
+ };
4746
+
4661
4747
  // experimental_message
4662
4748
  export type experimental_message = {};
4663
4749
 
@@ -5949,6 +6035,8 @@ export type MessageType = {
5949
6035
  TronSignMessage: TronSignMessage;
5950
6036
  TronMessageSignature: TronMessageSignature;
5951
6037
  facotry: facotry;
6038
+ BabylonDeriveContextHash: BabylonDeriveContextHash;
6039
+ BabylonDerivedContextHash: BabylonDerivedContextHash;
5952
6040
  TextMemo: TextMemo;
5953
6041
  RefundMemo: RefundMemo;
5954
6042
  CoinPurchaseMemo: CoinPurchaseMemo;
@@ -5964,6 +6052,15 @@ export type MessageType = {
5964
6052
  UnlockPath: UnlockPath;
5965
6053
  UnlockedPathRequest: UnlockedPathRequest;
5966
6054
  UiAnimationRequest: UiAnimationRequest;
6055
+ ZcashGetAddress: ZcashGetAddress;
6056
+ ZcashAddress: ZcashAddress;
6057
+ ZcashGetFullViewingKey: ZcashGetFullViewingKey;
6058
+ ZcashFullViewingKey: ZcashFullViewingKey;
6059
+ ZcashSignPczt: ZcashSignPczt;
6060
+ ZcashPcztChunkRequest: ZcashPcztChunkRequest;
6061
+ ZcashPcztChunkAck: ZcashPcztChunkAck;
6062
+ ZcashSignedPczt: ZcashSignedPczt;
6063
+ ZcashSignedPcztChunkRequest: ZcashSignedPcztChunkRequest;
5967
6064
  experimental_message: experimental_message;
5968
6065
  experimental_field: experimental_field;
5969
6066
  ProtocolInfoRequest: ProtocolInfoRequest;
@@ -34,6 +34,12 @@ export type OneKeyDeviceInfoWithSession = OneKeyUsbDeviceInfo & {
34
34
  export type OneKeyMobileDeviceInfo = {
35
35
  id: string;
36
36
  name: string | null;
37
+ /**
38
+ * Color letter of the serial number ('A'-'Z') from the Pro2 / Neo BLE advertisement,
39
+ * possibly seen in an earlier scan. Set by the React Native and Electron BLE
40
+ * transports; absent when unknown.
41
+ */
42
+ bleColorCode?: string;
37
43
  };
38
44
 
39
45
  export type OneKeyDeviceInfoBase = {
@@ -70,6 +76,19 @@ export type TransportWriteMetrics = {
70
76
  frameBytes: number;
71
77
  };
72
78
 
79
+ /**
80
+ * Supplies the remaining chunks of one file for an ordered FilesystemFileWrite pipeline.
81
+ * The call's request data is the first chunk; `next` returns the following chunk in offset
82
+ * order, or undefined to stop. The call then resolves with the last confirmation (or the
83
+ * device Failure), so the caller must check `processed_byte` for completeness. Callbacks
84
+ * should not throw: an error escapes with chunks still in flight and invalidates the link.
85
+ */
86
+ export type ProtocolV2FileWritePipeline = {
87
+ next: () => Promise<Record<string, unknown> | undefined>;
88
+ /** Called for each chunk after the device confirms it, in offset order. */
89
+ onResponse: (response: MessageFromOneKey) => void;
90
+ };
91
+
73
92
  export type TransportCallOptions = {
74
93
  timeoutMs?: number;
75
94
  expectedTypes?: string[];
@@ -83,6 +102,12 @@ export type TransportCallOptions = {
83
102
  onResponseAfterWrite?: (response: MessageFromOneKey) => void;
84
103
  /** Prefer acknowledged BLE characteristic writes for this call when supported. */
85
104
  writeWithResponse?: boolean;
105
+ /**
106
+ * Protocol V2 only: keep up to PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW ordered
107
+ * FilesystemFileWrite frames of one file in flight within this call. `timeoutMs`
108
+ * bounds each frame write and each response instead of the whole file.
109
+ */
110
+ fileWritePipeline?: ProtocolV2FileWritePipeline;
86
111
  };
87
112
 
88
113
  type ITransportInitFn = (