Skip to content

Commit

Permalink
refactor interactions
Browse files Browse the repository at this point in the history
  • Loading branch information
arobsn committed Jul 2, 2024
1 parent 074bc60 commit 363a58e
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 102 deletions.
3 changes: 2 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"enabled": true
},
"formatter": {
"indentStyle": "space"
"indentStyle": "space",
"lineWidth": 90
},
"javascript": {
"formatter": {
Expand Down
6 changes: 3 additions & 3 deletions src/assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function isBuffer(data: unknown): boolean {

export function isUint32(data: unknown): boolean {
return (
typeof data == "number" &&
typeof data === "number" &&
isInteger(data) &&
data >= MIN_UINT_VALUE &&
data <= MAX_UINT32_VALUE
Expand All @@ -54,7 +54,7 @@ export function isUint32(data: unknown): boolean {

export function isUint16(data: number): boolean {
return (
typeof data == "number" &&
typeof data === "number" &&
isInteger(data) &&
data >= MIN_UINT_VALUE &&
data <= MAX_UINT16_VALUE
Expand All @@ -63,7 +63,7 @@ export function isUint16(data: number): boolean {

export function isUint8(data: unknown): boolean {
return (
typeof data == "number" &&
typeof data === "number" &&
isInteger(data) &&
data >= MIN_UINT_VALUE &&
data <= MAX_UNIT8_VALUE
Expand Down
30 changes: 27 additions & 3 deletions src/erg.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,30 @@ 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", () => {
Expand Down Expand Up @@ -274,14 +298,14 @@ describe("transaction signing", () => {
attestation: "8c2d9e1dcd467155df32bf1ded800710",
boxId:
"2bb2f3111e33ad9d9ab1fa3ae184fc1f06a6ac5a71d40a825997f6637b44784f",
buffer: Buffer.from([
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: []
}
]);
Expand Down
2 changes: 0 additions & 2 deletions src/erg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
type UnsignedTx,
Network
} from "./types/public";
import { assert, isValidErgoPath } from "./assertions";
import type { AttestedBox } from "./types/attestedBox";
import {
getAppName,
Expand All @@ -20,7 +19,6 @@ import {
attestInput,
signTx
} from "./interactions";
import { serialize } from "./serialization/serialize";
import { uniq } from "@fleet-sdk/common";
import type {
AttestedTransaction,
Expand Down
8 changes: 4 additions & 4 deletions src/interactions/attestInput.spec.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
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(
const parsedFrame = decodeAttestedFrameResponse(
Buffer.from(frameHex, "hex")
);

expect(parsedFrame).toMatchObject({
boxId: "7cbe85a5f2d2154538eb883bbbee10dd414ec24b6e52b43495da906bce2c5e8a",
framesCount: 1,
frameIndex: 0,
count: 1,
index: 0,
amount: "2875858910",
tokens: [
{
Expand Down
28 changes: 13 additions & 15 deletions src/interactions/attestInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,39 +133,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
};
}
10 changes: 7 additions & 3 deletions src/interactions/deriveAddress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ function sendDeriveAddress(
authToken?: number
): Promise<DeviceResponse> {
const pathArray = pathToArray(path);
if (pathArray.length < 5) throw new Error("Invalid path length.");
if (!ALLOWED_CHANGE_PATHS.includes(pathArray[CHANGE_PATH_INDEX])) {
throw new Error("Invalid change path value.");
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([
Expand Down
4 changes: 2 additions & 2 deletions src/interactions/getVersion.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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
Expand All @@ -23,6 +23,6 @@ export async function getAppVersion(device: Device): Promise<Version> {
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 }
};
}
85 changes: 21 additions & 64 deletions src/interactions/signTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,10 @@ export async function signTx(
await sendDistinctTokensIds(device, sessionId, tx.distinctTokenIds);
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);
await sendOutputs(device, sessionId, tx.outputs, tx.changeMap, tx.distinctTokenIds);
const proof = await sendConfirmAndSign(device, sessionId);

return new Uint8Array(signBytes);
return new Uint8Array(proof);
}

async function sendHeader(
Expand Down Expand Up @@ -100,37 +94,26 @@ async function sendDistinctTokensIds(
sessionId: number,
ids: Uint8Array[]
) {
if (ids.length === 0) {
return;
}
if (ids.length === 0) return;

const MAX_PACKET_SIZE = 7;
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);
}
}
}
Expand All @@ -148,17 +131,11 @@ async function sendBoxContextExtension(
);
}

async function sendDataInputs(
device: Device,
sessionId: number,
boxIds: string[]
) {
async function sendDataInputs(device: Device, sessionId: number, boxIds: string[]) {
const MAX_PACKET_SIZE = 7;
const packets = serialize.arrayAsMappedChunks(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);
}
}
Expand All @@ -170,11 +147,9 @@ async function sendOutputs(
changeMap: ChangeMap,
distinctTokenIds: Uint8Array[]
) {
const distinctTokenIdsStr = distinctTokenIds.map((t) =>
Buffer.from(t).toString("hex")
);
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,
Expand All @@ -191,21 +166,14 @@ async function sendOutputs(
const tree = deserialize.hex(box.ergoTree);
if (tree === MINER_FEE_TREE) {
await addOutputBoxMinersFeeTree(device, sessionId);
} else if (
ErgoAddress.fromErgoTree(tree).toString() === changeMap.address
) {
} else if (ErgoAddress.fromErgoTree(tree).toString() === changeMap.address) {
await addOutputBoxChangeTree(device, sessionId, changeMap.path);
} else {
await addOutputBoxErgoTree(device, sessionId, box.ergoTree);
}

if (box.tokens && box.tokens.length > 0) {
await addOutputBoxTokens(
device,
sessionId,
box.tokens,
distinctTokenIdsStr
);
await addOutputBoxTokens(device, sessionId, box.tokens, distinctTokenIdsStr);
}

if (box.registers.length > 0) {
Expand All @@ -214,11 +182,7 @@ async function sendOutputs(
}
}

async function addOutputBoxErgoTree(
device: Device,
sessionId: number,
ergoTree: Buffer
) {
async function addOutputBoxErgoTree(device: Device, sessionId: number, ergoTree: Buffer) {
await device.sendData(
COMMAND.SIGN_TX,
P1.ADD_OUTPUT_BOX_ERGO_TREE_CHUNK,
Expand All @@ -236,11 +200,7 @@ async function addOutputBoxMinersFeeTree(device: Device, sessionId: number) {
);
}

async function addOutputBoxChangeTree(
device: Device,
sessionId: number,
path: string
) {
async function addOutputBoxChangeTree(device: Device, sessionId: number, path: string) {
await device.send(
COMMAND.SIGN_TX,
P1.ADD_OUTPUT_BOX_CHANGE_TREE,
Expand Down Expand Up @@ -281,10 +241,7 @@ async function addOutputBoxRegisters(
);
}

async function sendConfirmAndSign(
device: Device,
sessionId: number
): Promise<Buffer> {
async function sendConfirmAndSign(device: Device, sessionId: number): Promise<Buffer> {
const response = await device.send(
COMMAND.SIGN_TX,
P1.CONFIRM_AND_SIGN,
Expand Down
2 changes: 1 addition & 1 deletion src/serialization/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { assert } from "../assertions";
const sum = (arr: number[]) => arr.reduce((x, y) => x + y, 0);

export function chunkBy(data: Buffer, chunkLengths: number[]) {
assert(data.length <= sum(chunkLengths), "data is too big");
assert(data.length >= sum(chunkLengths), "data is too small");

let offset = 0;
const result = [];
Expand Down
2 changes: 1 addition & 1 deletion src/types/attestedBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class AttestedBox {
lengthBuffer.writeUInt32BE(extension.length, 0);
}

firstFrame.buffer = Buffer.concat([firstFrame.buffer, lengthBuffer]);
firstFrame.bytes = Buffer.concat([firstFrame.bytes, lengthBuffer]);

return this;
}
Expand Down
Loading

0 comments on commit 363a58e

Please sign in to comment.