Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ (signer-btc): Get PSBT signature
Browse files Browse the repository at this point in the history
jdabbech-ledger committed Jan 8, 2025
1 parent cee7864 commit 0663f38
Showing 8 changed files with 37 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -7,11 +7,11 @@ import {
} from "@ledgerhq/device-management-kit";

import { type Psbt } from "@api/model/Psbt";
import { type Signature } from "@api/model/Signature";
import { type Wallet } from "@api/model/Wallet";
import { type BtcErrorCodes } from "@internal/app-binder/command/utils/bitcoinAppErrors";

export type SignPsbtDAOutput = Signature;
// @toDo Update this return value to Psbt once it would be updated in SignPsbtTask
export type SignPsbtDAOutput = Uint8Array[];

export type SignPsbtDAInput = {
psbt: Psbt;
@@ -36,7 +36,8 @@ export type SignPsbtDAState = DeviceActionState<

export type SignPsbtDAInternalState = {
readonly error: SignPsbtDAError | null;
readonly signature: Signature | null;
// [SHOULD] be psbt instead of signature
readonly signature: Uint8Array[] | null;
};

export type SignPsbtDAReturnType = ExecuteDeviceActionReturnType<
Original file line number Diff line number Diff line change
@@ -56,11 +56,7 @@ describe("SignPsbtDeviceAction", () => {
.mockReturnValue(extractDependenciesMock());
signPersonalPsbtMock.mockResolvedValueOnce(
CommandResultFactory({
data: {
v: 0x1c,
r: "0x8a540510e13b0f2b11a451275716d29e08caad07e89a1c84964782fb5e1ad788",
s: "0x64a0de235b270fbe81e8e40688f4a9f9ad9d283d690552c9331d7773ceafa513",
},
data: [Uint8Array.from([0x01, 0x02, 0x03])],
}),
);

@@ -86,11 +82,7 @@ describe("SignPsbtDeviceAction", () => {
status: DeviceActionStatus.Pending,
},
{
output: {
v: 0x1c,
r: "0x8a540510e13b0f2b11a451275716d29e08caad07e89a1c84964782fb5e1ad788",
s: "0x64a0de235b270fbe81e8e40688f4a9f9ad9d283d690552c9331d7773ceafa513",
},
output: [Uint8Array.from([0x01, 0x02, 0x03])],
status: DeviceActionStatus.Completed,
},
];
Original file line number Diff line number Diff line change
@@ -20,15 +20,14 @@ import {
type SignPsbtDAOutput,
} from "@api/app-binder/SignPsbtDeviceActionTypes";
import { type Psbt } from "@api/model/Psbt";
import { type Signature } from "@api/model/Signature";
import { type Wallet as ApiWallet } from "@api/model/Wallet";
import { type BtcErrorCodes } from "@internal/app-binder/command/utils/bitcoinAppErrors";
import { SignPsbtTask } from "@internal/app-binder/task/SignPsbtTask";

export type MachineDependencies = {
readonly signPsbt: (arg0: {
input: { wallet: ApiWallet; psbt: Psbt };
}) => Promise<CommandResult<Signature, BtcErrorCodes>>;
}) => Promise<CommandResult<Uint8Array[], BtcErrorCodes>>;
};

export type ExtractMachineDependencies = (
@@ -206,7 +205,7 @@ export class SignPsbtDeviceAction extends XStateDeviceAction<
extractDependencies(internalApi: InternalApi): MachineDependencies {
const signPsbt = async (arg0: {
input: { wallet: ApiWallet; psbt: Psbt };
}): Promise<CommandResult<Signature, BtcErrorCodes>> => {
}): Promise<CommandResult<Uint8Array[], BtcErrorCodes>> => {
return await new SignPsbtTask(internalApi, arg0.input).run();
};
return {
Original file line number Diff line number Diff line change
@@ -55,9 +55,10 @@ describe("ContinueTask", () => {
// when
const task = new ContinueTask(
api as unknown as InternalApi,
{} as DataStore,
clientCommandInterpreter,
);
await task.run({} as DataStore, fromResult);
await task.run(fromResult);
// then
expect(
clientCommandInterpreter.getClientCommandPayload,
@@ -79,9 +80,10 @@ describe("ContinueTask", () => {
// when
const task = new ContinueTask(
api as unknown as InternalApi,
{} as DataStore,
clientCommandInterpreter,
);
const result = await task.run({} as DataStore, fromResult);
const result = await task.run(fromResult);
// then
expect(api.sendCommand).toHaveBeenCalledTimes(0);
expect(result).toStrictEqual(
@@ -101,9 +103,10 @@ describe("ContinueTask", () => {
// when
const task = new ContinueTask(
api as unknown as InternalApi,
{} as DataStore,
clientCommandInterpreter,
);
const result = await task.run({} as DataStore, fromResult);
const result = await task.run(fromResult);
// then
expect(
clientCommandInterpreter.getClientCommandPayload,
Original file line number Diff line number Diff line change
@@ -20,36 +20,33 @@ import { BtcCommandUtils } from "@internal/utils/BtcCommandUtils";

export class ContinueTask {
private readonly _clientCommandInterpreter: ClientCommandInterpreter;
private readonly _context: ClientCommandContext;

constructor(
private readonly _api: InternalApi,
dataStore: DataStore,
clientCommandInterpreter?: ClientCommandInterpreter,
) {
this._context = {
dataStore,
queue: [],
yieldedResults: [],
};
this._clientCommandInterpreter =
clientCommandInterpreter || new ClientCommandInterpreter();
}

async run(
dataStore: DataStore,
fromResult: CommandResult<ApduResponse, BtcErrorCodes>,
): Promise<CommandResult<ContinueCommandResponse, BtcErrorCodes>> {
let currentResponse: CommandResult<ContinueCommandResponse, BtcErrorCodes> =
fromResult;
const commandHandlersContext: ClientCommandContext = {
dataStore,
queue: [],
yieldedResults: [],
};

while (
this.isApduResult(currentResponse) &&
BtcCommandUtils.isContinueResponse(currentResponse.data)
) {
currentResponse = await this._clientCommandInterpreter
.getClientCommandPayload(
currentResponse.data.data,
commandHandlersContext,
)
.getClientCommandPayload(currentResponse.data.data, this._context)
.caseOf({
Left: (error) =>
Promise.resolve(
@@ -67,6 +64,11 @@ export class ContinueTask {
}
return currentResponse;
}

getYieldedResults() {
return this._context.yieldedResults;
}

private isApduResult = (
response: CommandResult<ContinueCommandResponse, BtcErrorCodes>,
): response is CommandSuccessResult<ApduResponse> => {
Original file line number Diff line number Diff line change
@@ -66,8 +66,7 @@ export class SendSignMessageTask {
messageMerkleRoot: merkleRoot,
}),
);
const response = await new ContinueTask(this.api).run(
dataStore,
const response = await new ContinueTask(this.api, dataStore).run(
signMessageFirstCommandResponse,
);
if (isSuccessCommandResult(response)) {
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ jest.mock("@internal/app-binder/task/BuildPsbtTask", () => ({
jest.mock("@internal/app-binder/task/ContinueTask", () => ({
ContinueTask: jest.fn().mockImplementation(() => ({
run: mockRunContinue,
getYieldedResults: jest.fn(() => []),
})),
}));
jest.mock("@internal/app-binder/task/PrepareWalletPolicyTask", () => ({
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {
CommandResult,
CommandResultFactory,
type InternalApi,
isSuccessCommandResult,
} from "@ledgerhq/device-management-kit";
@@ -7,13 +9,13 @@ import { injectable } from "inversify";
import { Psbt } from "@api/model/Psbt";
import { Wallet as ApiWallet } from "@api/model/Wallet";
import { SignPsbtCommand } from "@internal/app-binder/command/SignPsbtCommand";
import { BtcErrorCodes } from "@internal/app-binder/command/utils/bitcoinAppErrors";
import { BuildPsbtTask } from "@internal/app-binder/task/BuildPsbtTask";
import { ContinueTask } from "@internal/app-binder/task/ContinueTask";
import { PrepareWalletPolicyTask } from "@internal/app-binder/task/PrepareWalletPolicyTask";
import { DataStore } from "@internal/data-store/model/DataStore";
import { PsbtCommitment } from "@internal/data-store/service/DataStoreService";
import { Sha256HasherService } from "@internal/merkle-tree/service/Sha256HasherService";
import { BtcCommandUtils } from "@internal/utils/BtcCommandUtils";
import { Wallet as InternalWallet } from "@internal/wallet/model/Wallet";
import { DefaultWalletSerializer } from "@internal/wallet/service/DefaultWalletSerializer";
import type { WalletSerializer } from "@internal/wallet/service/WalletSerializer";
@@ -51,7 +53,7 @@ export class SignPsbtTask {
inputsCount: number,
outputsCount: number,
wallet: InternalWallet,
) {
): Promise<CommandResult<Uint8Array[], BtcErrorCodes>> {
const signPsbtCommandResult = await this._api.sendCommand(
new SignPsbtCommand({
globalCommitments: psbtCommitment.globalCommitment,
@@ -64,11 +66,12 @@ export class SignPsbtTask {
}),
);

const continueTask = new ContinueTask(this._api);
const result = await continueTask.run(dataStore, signPsbtCommandResult);
const continueTask = new ContinueTask(this._api, dataStore);
const result = await continueTask.run(signPsbtCommandResult);

if (isSuccessCommandResult(result)) {
return BtcCommandUtils.getSignature(result);
const signatureList = continueTask.getYieldedResults();
return CommandResultFactory({ data: signatureList });
}
return result;
}

0 comments on commit 0663f38

Please sign in to comment.