Skip to content

Commit

Permalink
Merge pull request #487 from arconnectio/development
Browse files Browse the repository at this point in the history
ArConnect BETA 1.18.4
  • Loading branch information
nicholaswma authored Oct 8, 2024
2 parents e46c390 + 7a48aa0 commit 3d20771
Show file tree
Hide file tree
Showing 14 changed files with 733 additions and 7 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;
59 changes: 55 additions & 4 deletions src/api/modules/connect/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { objectToUrlParams } from "./url";
import type { AuthResult } from "shim";
import { nanoid } from "nanoid";
import browser from "webextension-polyfill";
import { Mutex } from "~utils/mutex";

const mutex = new Mutex();
let keepAliveInterval: number | null = null;
let activePopups = 0;

export type AuthType =
| "connect"
Expand All @@ -13,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 Expand Up @@ -74,8 +80,11 @@ async function createAuthPopup(data: AuthData) {
* Await for a browser message from the popup
*/
const result = (authID: string, tabId: number) =>
new Promise<AuthResult>((resolve, reject) =>
new Promise<AuthResult>(async (resolve, reject) => {
startKeepAlive();

onMessage("auth_result", ({ sender, data }) => {
stopKeepAlive();
// validate sender by it's tabId
if (sender.tabId !== tabId) {
return;
Expand All @@ -93,5 +102,47 @@ const result = (authID: string, tabId: number) =>
} else {
resolve(data);
}
})
);
});
});

/**
* Function to send periodic keep-alive messages
*/
const startKeepAlive = async () => {
const unlock = await mutex.lock();

try {
// Increment the active popups count
activePopups++;
if (activePopups > 0 && keepAliveInterval === null) {
console.log("Started keep-alive messages...");
keepAliveInterval = setInterval(
() => browser.alarms.create("keep-alive", { when: Date.now() + 1 }),
20000
);
}
} finally {
unlock();
}
};

/**
* Function to stop sending keep-alive messages
*/
const stopKeepAlive = async () => {
const unlock = await mutex.lock();

try {
// Decrement the active popups count
activePopups--;
if (activePopups <= 0 && keepAliveInterval !== null) {
// Stop keep-alive messages when no popups are active
browser.alarms.clear("keep-alive");
clearInterval(keepAliveInterval);
keepAliveInterval = null;
console.log("Stopped keep-alive messages...");
}
} finally {
unlock();
}
};
2 changes: 1 addition & 1 deletion src/api/modules/sign/sign.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const background: ModuleFunction<BackgroundResult> = async (
// check if there is an allowance limit, if there is we need to check allowance
// if alwaysAsk is true, then we'll need to signAuth popup
// if allowance is disabled, proceed with signing
if (alwaysAsk) {
if (alwaysAsk || activeWallet.type === "hardware") {
// get address of keyfile
const addr =
activeWallet.type === "local"
Expand Down
7 changes: 7 additions & 0 deletions src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ browser.alarms.onAlarm.addListener(keyRemoveAlarmListener);
// handle importing ao tokens
browser.alarms.onAlarm.addListener(importAoTokens);

// handle keep alive alarm
browser.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === "keep-alive") {
console.log("keep alive alarm");
}
});

// handle window close
browser.windows.onRemoved.addListener(onWindowClose);

Expand Down
Loading

0 comments on commit 3d20771

Please sign in to comment.