Skip to content

Commit

Permalink
Merge pull request #146 from KeystoneHQ/add-ton-lib
Browse files Browse the repository at this point in the history
feat: add ton qr protocols
  • Loading branch information
soralit authored May 14, 2024
2 parents 33f83e8 + 4ce5967 commit 371cbf9
Show file tree
Hide file tree
Showing 11 changed files with 463 additions and 18 deletions.
15 changes: 15 additions & 0 deletions packages/ur-registry-ton/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# BC-UR-Registry-Tron

This repository is the Ton extension of [bc-ur-registry](https://github.com/KeystoneHQ/ur-registry)

## Installing

To install, run:

```bash
yarn add @keystonehq/bc-ur-registry-ton
```

```bash
npm install --save @keystonehq/bc-ur-registry-ton
```
71 changes: 71 additions & 0 deletions packages/ur-registry-ton/__test__/TonSignRequest.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// @ts-nocheck

import { TonSignRequest, DataType } from "../src";
import * as uuid from "uuid";

describe("ton-sign-request", () => {
it("should generate ton-sign-request", () => {
const tonData = Buffer.from(
"te6cckEBAgEARwABHCmpoxdmOz6lAAAACAADAQBoQgArFnMvHAX9tOjTp4/RDd3vP2Bn8xG+U5MTuKRKUE1NoqHc1lAAAAAAAAAAAAAAAAAAAHBy4G8=",
"base64"
);

const tronRequestId = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d";
const idBuffer = uuid.parse(tronRequestId) as Uint8Array;

const tonSignRequest = new TonSignRequest({
requestId: Buffer.from(idBuffer),
signData: tonData,
dataType: DataType.TRANSACTION,
address: "UQC1IywyQwixSOU8pezOZDC9rv2xCV4CGJzOWH6RX8BTsGJx",
origin: "TonKeeper",
});

const cborHex = tonSignRequest.toCBOR().toString("hex");
console.log(cborHex.toString("hex"));

expect(cborHex.toString("hex")).toBe(
"a501d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d025856b5ee9c7241010201004700011c29a9a317663b3ea500000008000301006842002b16732f1c05fdb4e8d3a78fd10dddef3f6067f311be539313b8a44a504d4da2a1dcd65000000000000000000000000000007072e06f0301057830555143314979777951776978534f553870657a4f5a4443397276327843563443474a7a4f574836525838425473474a780669546f6e4b6565706572"
);

const ur = tonSignRequest.toUREncoder(1000).nextPart();

expect(ur).toBe(
"ur:ton-sign-request/onadtpdagdndcawmgtfrkigrpmndutdnbtkgfssbjnaohdhfrewynsjpfpadaoadaeflaeadcedtptotchiyfrfmonaeaeaeayaeaxadaeisfwaedncmjkdlceahzcqzvsteosmyttbtutwsfhhniowfbyrngumubwrooxgegdgtgtoeoyuotbgdaeaeaeaeaeaeaeaeaeaeaeaeaeaejojpvtjlaxadahksdygogyfxehgakkktkkgyktinksgugwgoetjoihkngwhtfyfxesjpkoeyksfxhfeefxflgekngwhgfdengmhdetfwghjkflgeksaminghjljtgrihihjoihjpmduydrch"
);
const tonSignRequestDecoded = TonSignRequest.fromCBOR(
Buffer.from(cborHex, "hex")
);
expect(uuid.stringify(tonSignRequest.getRequestId())).toBe(tronRequestId);
expect(tonSignRequest.getOrigin()).toBe("TonKeeper");
expect(tonSignRequestDecoded.getSignData().toString("base64")).toEqual(
"te6cckEBAgEARwABHCmpoxdmOz6lAAAACAADAQBoQgArFnMvHAX9tOjTp4/RDd3vP2Bn8xG+U5MTuKRKUE1NoqHc1lAAAAAAAAAAAAAAAAAAAHBy4G8="
);
expect(tonSignRequestDecoded.getDerivationPath()).toEqual(undefined);
expect(tonSignRequestDecoded.getAddress()).toEqual(
"UQC1IywyQwixSOU8pezOZDC9rv2xCV4CGJzOWH6RX8BTsGJx"
);
});

it("should construct an TonSignRequest object from string", () => {
const tonData = Buffer.from(
"te6cckEBAgEARwABHCmpoxdmOz6lAAAACAADAQBoQgArFnMvHAX9tOjTp4/RDd3vP2Bn8xG+U5MTuKRKUE1NoqHc1lAAAAAAAAAAAAAAAAAAAHBy4G8=",
"base64"
);
const requestID = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d";

const request = TonSignRequest.constructTonRequest(
tonData,
DataType.TRANSACTION,
"UQC1IywyQwixSOU8pezOZDC9rv2xCV4CGJzOWH6RX8BTsGJx",
requestID,
"TonKeeper",
undefined,
undefined
);
const ur = request.toUREncoder(1000).nextPart();
expect(ur).toBe(
"ur:ton-sign-request/onadtpdagdndcawmgtfrkigrpmndutdnbtkgfssbjnaohdhfrewynsjpfpadaoadaeflaeadcedtptotchiyfrfmonaeaeaeayaeaxadaeisfwaedncmjkdlceahzcqzvsteosmyttbtutwsfhhniowfbyrngumubwrooxgegdgtgtoeoyuotbgdaeaeaeaeaeaeaeaeaeaeaeaeaeaejojpvtjlaxadahksdygogyfxehgakkktkkgyktinksgugwgoetjoihkngwhtfyfxesjpkoeyksfxhfeefxflgekngwhgfdengmhdetfwghjkflgeksaminghjljtgrihihjoihjpmduydrch"
);
});
});
45 changes: 45 additions & 0 deletions packages/ur-registry-ton/__test__/TonSignature.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// @ts-nocheck

import { TonSignature, URRegistryDecoder } from "../src";
import * as uuid from "uuid";

describe("ton-signature", () => {
it("should generate ton-signature", () => {
const signature = Buffer.from(
"f4b79835417490958c72492723409289b444f3af18274ba484a9eeaca9e760520e453776e5975df058b537476932a45239685f694fc6362fe5af6ba714da6505",
"hex"
);
const requestId = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d";
const idBuffer = uuid.parse(requestId) as Uint8Array;

const tonSignature = new TonSignature(
signature,
Buffer.from(idBuffer),
"Keystone"
);

const cborHex = tonSignature.toCBOR().toString("hex");
expect(cborHex.toString("hex")).toBe("a301d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d025840f4b79835417490958c72492723409289b444f3af18274ba484a9eeaca9e760520e453776e5975df058b537476932a45239685f694fc6362fe5af6ba714da650503684b657973746f6e65");

const ur = tonSignature.toUREncoder(1000).nextPart();
expect(ur).toBe(
"ur:ton-signature/otadtpdagdndcawmgtfrkigrpmndutdnbtkgfssbjnaohdfzwkrlmkecfpjymhmdlkjpgadicnfzmoldqzfywfpecsdigroxlrptwypsptvdhngmbafeemkovwmshlwthdreemflineyoxgmesisheingwswendlvwpejeosbbtnihahaxisgrihkkjkjyjljtihgmcalnzo"
);
const tonSignatureDecoded = TonSignature.fromCBOR(
Buffer.from(cborHex, "hex")
);
expect(uuid.stringify(tonSignatureDecoded.getRequestId())).toBe(
requestId
);
expect(tonSignatureDecoded.getSignature().toString("hex")).toEqual(
"f4b79835417490958c72492723409289b444f3af18274ba484a9eeaca9e760520e453776e5975df058b537476932a45239685f694fc6362fe5af6ba714da6505"
);

const urDecoder = new URRegistryDecoder();
urDecoder.receivePart("ur:ton-signature/otadtpdagdndcawmgtfrkigrpmndutdnbtkgfssbjnaohdfzwkrlmkecfpjymhmdlkjpgadicnfzmoldqzfywfpecsdigroxlrptwypsptvdhngmbafeemkovwmshlwthdreemflineyoxgmesisheingwswendlvwpejeosbbtnihahaxisgrihkkjkjyjljtihgmcalnzo");
const decodeSig = TonSignature.fromCBOR(urDecoder.resultUR().cbor);
expect(decodeSig.getSignature().toString('hex')).toEqual('f4b79835417490958c72492723409289b444f3af18274ba484a9eeaca9e760520e453776e5975df058b537476932a45239685f694fc6362fe5af6ba714da6505');
expect(decodeSig.getRequestId().toString('hex')).toEqual('9b1deb4d3b7d4bad9bdd2b0d7b3dcb6d');
expect(decodeSig.getOrigin()).toEqual('Keystone');
});
});
6 changes: 6 additions & 0 deletions packages/ur-registry-ton/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
presets: [
["@babel/preset-env", { targets: { node: "current" } }],
"@babel/preset-typescript",
],
};
33 changes: 33 additions & 0 deletions packages/ur-registry-ton/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@keystonehq/bc-ur-registry-ton",
"version": "0.1.0",
"description": "bc-ur-registry extension for Tron",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"directories": {
"lib": "src",
"test": "__tests__"
},
"files": [
"dist"
],
"scripts": {
"clean": "rm -rf ./dist",
"start": "tsdx watch",
"build": "tsdx build",
"test": "jest --passWithNoTests"
},
"author": "Sora Li<sora@keyst.one>",
"license": "ISC",
"dependencies": {
"@keystonehq/bc-ur-registry": "^0.5.4",
"uuid": "^9.0.0"
},
"devDependencies": {
"@babel/preset-typescript": "^7.15.0",
"@types/uuid": "^9.0.0",
"jest": "^27.5.1",
"tsdx": "^0.14.1",
"typescript": "^4.9.4"
}
}
6 changes: 6 additions & 0 deletions packages/ur-registry-ton/src/RegistryType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { RegistryType } from "@keystonehq/bc-ur-registry";

export const ExtendedRegistryTypes = {
TRON_SIGN_REQUEST: new RegistryType("ton-sign-request", 7201),
TRON_SIGNATURE: new RegistryType("ton-signature", 7202)
};
155 changes: 155 additions & 0 deletions packages/ur-registry-ton/src/TonSignRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import {
CryptoKeypath,
extend,
DataItem,
PathComponent,
RegistryItem,
DataItemMap,
} from "@keystonehq/bc-ur-registry";
import { ExtendedRegistryTypes } from "./RegistryType";
import { parse as uuidParse } from "uuid";

const { decodeToDataItem, RegistryTypes } = extend;

enum Keys {
requestId = 1,
signData,
dataType,
derivationPath,
address,
origin,
}

type SignRequestProps = {
requestId?: Buffer;
signData: Buffer;
dataType: DataType;
derivationPath?: CryptoKeypath;
address: String;
origin?: string;
};

export enum DataType {
TRANSACTION = 1,
SIGN_PROOF = 2,
}

export class TonSignRequest extends RegistryItem {
private requestId?: Buffer;
private signData: Buffer;
private dataType: DataType;
private derivationPath?: CryptoKeypath;
private address: String;
private origin?: string;

getRegistryType = () => ExtendedRegistryTypes.TRON_SIGN_REQUEST;

constructor(args: SignRequestProps) {
super();
this.requestId = args.requestId;
this.signData = args.signData;
this.derivationPath = args.derivationPath;
this.address = args.address;
this.origin = args.origin;
this.dataType = args.dataType;
}

public getRequestId = () => this.requestId;
public getSignData = () => this.signData;
public getDerivationPath = () => this.derivationPath? this.derivationPath.getPath(): undefined;
public getAddress = () => this.address;
public getOrigin = () => this.origin;
public getDataType = () => this.dataType;

public toDataItem = () => {
const map: DataItemMap = {};
map[Keys.signData] = this.signData;
map[Keys.dataType] = this.dataType;

if (this.derivationPath) {
const derivationPath = this.derivationPath.toDataItem();
derivationPath.setTag(this.derivationPath.getRegistryType().getTag());
map[Keys.derivationPath] = derivationPath;
}

if (this.requestId) {
map[Keys.requestId] = new DataItem(
this.requestId,
RegistryTypes.UUID.getTag()
);
}

if (this.address) {
map[Keys.address] = this.address;
}

if (this.origin) {
map[Keys.origin] = this.origin;
}

return new DataItem(map);
};

public static fromDataItem = (dataItem: DataItem) => {
const map = dataItem.getData();

const requestId = map[Keys.requestId]
? map[Keys.requestId].getData()
: undefined;

const derivationPath = map[Keys.derivationPath]
? CryptoKeypath.fromDataItem(map[Keys.derivationPath])
: undefined;

return new TonSignRequest({
requestId,
signData: map[Keys.signData],
dataType: map[Keys.dataType],
derivationPath,
address: map[Keys.address],
origin: map[Keys.origin],
});
};

public static fromCBOR = (_cborPayload: Buffer) => {
const dataItem = decodeToDataItem(_cborPayload);
return TonSignRequest.fromDataItem(dataItem);
};

public static parsePath(path: string, xfp: string) {
const paths = path.replace(/[m|M]\//, "").split("/");
const pathComponent = paths.map((path) => {
const index = parseInt(path.replace("'", ""));
let isHardened = false;
if (path.endsWith("'")) {
isHardened = true;
}
return new PathComponent({ index, hardened: isHardened });
});
return new CryptoKeypath(pathComponent, Buffer.from(xfp, "hex"));
}

public static constructTonRequest(
signData: Buffer,
dataType: DataType,
address: String,
uuidString?: string,
origin?: string,
xfp?: string,
derivationHDPath?: string
) {
return new TonSignRequest({
requestId: uuidString
? Buffer.from(uuidParse(uuidString) as Uint8Array)
: undefined,
signData,
dataType,
derivationPath:
derivationHDPath && xfp
? TonSignRequest.parsePath(derivationHDPath, xfp)
: undefined,
address,
origin,
});
}
}
Loading

0 comments on commit 371cbf9

Please sign in to comment.