Skip to content

Commit

Permalink
Merge pull request #483 from arconnectio/arc-501/bulk-sign-data-item
Browse files Browse the repository at this point in the history
arc-501:  add bulk sign data item api route
  • Loading branch information
nicholaswma authored Oct 7, 2024
2 parents b120956 + cb57b71 commit 6c48d06
Show file tree
Hide file tree
Showing 11 changed files with 658 additions and 3 deletions.
14 changes: 14 additions & 0 deletions assets/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1329,6 +1329,10 @@
"message": "Sign Item",
"description": "Sign message popup title"
},
"batch_sign_items": {
"message": "Batch Sign Items",
"description": "Batch sign message popup title"
},
"titles_signature": {
"message": "Sign message",
"description": "Sign message popup title"
Expand All @@ -1343,6 +1347,16 @@
}
}
},
"batch_sign_data_description": {
"message": "$APPNAME$ wants to sign the following transactions. Review the details below.",
"description": "Desription for signing an item containing a transfer",
"placeholders": {
"appname": {
"content": "$1",
"example": "permafacts.arweave.dev"
}
}
},
"signature_description": {
"message": "$APPNAME$ wants to sign a message. Review the message below.",
"description": "App signature request for data that cannot be decoded to string",
Expand Down
14 changes: 14 additions & 0 deletions assets/_locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,10 @@
"message": "签署项目",
"description": "Sign message popup title"
},
"batch_sign_items": {
"message": "批量签署项目",
"description": "批量签署消息弹出标题"
},
"titles_signature": {
"message": "签署消息",
"description": "Sign message popup title"
Expand All @@ -1331,6 +1335,16 @@
}
}
},
"batch_sign_data_description": {
"message": "$APPNAME$ 想要签署以下交易。请查看以下详细信息。",
"description": "Description for signing an item containing a transfer",
"placeholders": {
"appname": {
"content": "$1",
"example": "permafacts.arweave.dev"
}
}
},
"signature_description": {
"message": "$APPNAME$ 想要签署一条消息。请查看以下消息。",
"description": "App signature request for data that cannot be decoded to string",
Expand Down
5 changes: 4 additions & 1 deletion src/api/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import verifyMessageModule from "./modules/verify_message";
import verifyMessage from "./modules/verify_message/verify_message.background";
import signDataItemModule from "./modules/sign_data_item";
import signDataItem from "./modules/sign_data_item/sign_data_item.background";
import batchSignDataItemModule from "./modules/batch_sign_data_item";
import batchSignDataItem from "./modules/batch_sign_data_item/batch_sign_data_item.background";
import subscriptionModule from "./modules/subscription";
import subscription from "./modules/subscription/subscription.background";
import userTokensModule from "./modules/user_tokens";
Expand Down Expand Up @@ -66,7 +68,8 @@ const modules: BackgroundModule<any>[] = [
{ ...verifyMessageModule, function: verifyMessage },
{ ...signDataItemModule, function: signDataItem },
{ ...subscriptionModule, function: subscription },
{ ...userTokensModule, function: userTokens }
{ ...userTokensModule, function: userTokens },
{ ...batchSignDataItemModule, function: batchSignDataItem }
];

export default modules;
Expand Down
11 changes: 10 additions & 1 deletion src/api/foreground.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ import privateHash, {
} from "./modules/private_hash/private_hash.foreground";
import verifyMessageModule from "./modules/verify_message";
import verifyMessage from "./modules/verify_message/verify_message.foreground";
import batchSignDataItemModule from "./modules/batch_sign_data_item";
import batchSignDataItem, {
finalizer as batchSignDataItemFinalizer
} from "./modules/batch_sign_data_item/batch_sign_data_item.foreground";
import signDataItemModule from "./modules/sign_data_item";
import signDataItem, {
finalizer as signDataItemFinalizer
Expand Down Expand Up @@ -96,7 +100,12 @@ const modules: ForegroundModule[] = [
finalizer: signDataItemFinalizer
},
{ ...subscriptionModule, function: subscription },
{ ...userTokensModule, function: userTokens }
{ ...userTokensModule, function: userTokens },
{
...batchSignDataItemModule,
function: batchSignDataItem,
finalizer: batchSignDataItemFinalizer
}
];

export default modules;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { ModuleFunction } from "~api/module";
import { isNotCancelError, isRawDataItem } from "~utils/assertions";
import authenticate from "../connect/auth";
import browser from "webextension-polyfill";
import { getActiveKeyfile } from "~wallets";
import { freeDecryptedWallet } from "~wallets/encryption";
import { ArweaveSigner, createData, DataItem } from "arbundles";
import type { RawDataItem } from "../sign_data_item/types";

const background: ModuleFunction<number[][]> = async (
appData,
dataItems: unknown[]
) => {
// validate
if (!Array.isArray(dataItems)) {
throw new Error("Input must be an array of data items");
}

for (const dataItem of dataItems) {
isRawDataItem(dataItem);
}

const results: number[][] = [];

await authenticate({
type: "batchSignDataItem",
data: dataItems,
appData
});

// grab the user's keyfile
const decryptedWallet = await getActiveKeyfile().catch((e) => {
isNotCancelError(e);

// if there are no wallets added, open the welcome page
browser.tabs.create({ url: browser.runtime.getURL("tabs/welcome.html") });

throw new Error("No wallets added");
});

try {
if (decryptedWallet.type !== "local") {
throw new Error(
"Only local wallets are currently supported for batch signing"
);
}

const dataSigner = new ArweaveSigner(decryptedWallet.keyfile);

for (const dataItem of dataItems as RawDataItem[]) {
const { data, ...options } = dataItem;
const binaryData = new Uint8Array(data);

const dataEntry = createData(binaryData, dataSigner, options);

await dataEntry.sign(dataSigner);

results.push(Array.from<number>(dataEntry.getRaw()));
}
} finally {
// @ts-expect-error
freeDecryptedWallet(decryptedWallet.keyfile);
}

return results;
};

export default background;
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { TransformFinalizer } from "~api/foreground";
import type { ModuleFunction } from "~api/module";
import type { RawDataItem, SignDataItemParams } from "../sign_data_item/types";
import { isArrayBuffer } from "~utils/assertions";

const MAX_TOTAL_SIZE = 200 * 1024;

const foreground: ModuleFunction<Record<any, any>[]> = async (
dataItems: SignDataItemParams[]
) => {
if (!Array.isArray(dataItems)) {
throw new Error("Input must be an array of data items");
}

const totalSize = dataItems.reduce((acc, dataItem) => {
const dataSize =
typeof dataItem.data === "string"
? new TextEncoder().encode(dataItem.data).length
: dataItem.data.length;
return acc + dataSize;
}, 0);

if (totalSize > MAX_TOTAL_SIZE) {
throw new Error("Total size of data items exceeds 200 KB");
}

const rawDataItems: RawDataItem[] = dataItems.map((dataItem) => {
let rawDataItem: RawDataItem;

if (typeof dataItem.data !== "string") {
isArrayBuffer(dataItem.data);

rawDataItem = {
...dataItem,
data: Array.from(dataItem.data)
};
} else {
rawDataItem = {
...dataItem,
data: Array.from(new TextEncoder().encode(dataItem.data))
};
}

return rawDataItem;
});

return [rawDataItems];
};

export const finalizer: TransformFinalizer<number[][]> = (result) => {
return result.map((item) => new Uint8Array(item).buffer);
};

export default foreground;
11 changes: 11 additions & 0 deletions src/api/modules/batch_sign_data_item/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { PermissionType } from "~applications/permissions";
import type { ModuleProperties } from "~api/module";

const permissions: PermissionType[] = ["SIGN_TRANSACTION"];

const batchSignDataItem: ModuleProperties = {
functionName: "batchSignDataItem",
permissions
};

export default batchSignDataItem;
3 changes: 2 additions & 1 deletion src/api/modules/connect/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export type AuthType =
| "subscription"
| "signKeystone"
| "signature"
| "signDataItem";
| "signDataItem"
| "batchSignDataItem";

export interface AuthData {
// type of auth to request from the user
Expand Down
Loading

0 comments on commit 6c48d06

Please sign in to comment.