From ad43e9999888fd64a29960b328528545bcbfce5b Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Mon, 3 Jun 2024 17:18:53 -0400 Subject: [PATCH 1/4] [NayNay] Signing bug for validators - updated signing with adapters to retry the signing request if a specific error is hit as defined in this issue https://github.com/entropyxyz/sdk/issues/367 --- package.json | 1 + src/flows/sign/index.ts | 122 ++++++++++++++++++++++++++-------------- 2 files changed, 80 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index bc3949a..d5fdb3f 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "dependencies": { "@entropyxyz/sdk": "^0.1.5-5", "@ethereumjs/util": "^9.0.2", + "@polkadot/util": "^12.6.2", "@types/node": "^20.12.12", "alchemy-sdk": "^3.1.2", "ansi-colors": "^4.1.3", diff --git a/src/flows/sign/index.ts b/src/flows/sign/index.ts index 8d99fce..aefcd4b 100644 --- a/src/flows/sign/index.ts +++ b/src/flows/sign/index.ts @@ -1,7 +1,85 @@ import inquirer from "inquirer" +import { u8aToHex } from '@polkadot/util' import { initializeEntropy } from "../../common/initializeEntropy" import { debug, getSelectedAccount, print } from "../../common/utils" +let msg: string +let signingAttempts: number = 0 + +async function signWithAdapter (entropy, signingData?: { msg: { msg: string }, order: string[] }) { + try { + const messageQuestion = { + type: 'list', + name: 'messageAction', + message: 'Please choose how you would like to input your message to sign:', + choices: [ + 'Text Input', + // 'From a File', + ], + } + const userInputQuestion = { + type: "editor", + name: "userInput", + message: "Enter the message you wish to sign (this will open your default editor):", + } + // const pathToFileQuestion = { + // type: 'input', + // name: 'pathToFile', + // message: 'Enter the path to the file you wish to sign:', + // } + let messageAction + if (!msg) { + ({ messageAction } = await inquirer.prompt([messageQuestion])) + switch (messageAction) { + case 'Text Input': { + const { userInput } = await inquirer.prompt([userInputQuestion]) + msg = Buffer.from(userInput).toString('hex') + break + } + // case 'From a File': { + // break + // } + default: { + console.error('Unsupported Action') + return + } + } + } + debug('msg', msg); + const msgParam = { msg } + if (!signingData) { + signingData = { + msg: msgParam, + order: ['deviceKeyProxy', 'noop'], + } + } + const signature = await entropy.signWithAdaptersInOrder(signingData) + const signatureHexString = u8aToHex(signature) + print('signature:', signatureHexString) + print('verifying key:', entropy.signingManager.verifyingKey) + } catch (error) { + signingAttempts++ + const { message } = error + // See https://github.com/entropyxyz/sdk/issues/367 for reasoning behind adding this retry mechanism + if (message.includes('Invalid Signer') || message.includes('Invalid Signer in Signing group')) { + if (signingAttempts <= 1) { + const msgParam = { msg } + signingData = { + msg: msgParam, + order: ['noop', 'deviceKeyProxy'] + } + // Recursively retries signing with a reverse order in the subgroups list + await signWithAdapter(entropy, signingData) + } else { + console.error(message) + } + } else { + console.error(message) + } + return + } +} + export async function sign ({ accounts, endpoints, selectedAccount: selectedAccountAddress }, options) { const endpoint = endpoints[options.ENDPOINT] const actionChoice = await inquirer.prompt([ @@ -51,49 +129,7 @@ export async function sign ({ accounts, endpoints, selectedAccount: selectedAcco // return // } case 'Sign With Adapter': { - const messageQuestion = { - type: 'list', - name: 'messageAction', - message: 'Please choose how you would like to input your message to sign:', - choices: [ - 'Text Input', - // 'From a File', - ], - } - const userInputQuestion = { - type: "editor", - name: "userInput", - message: "Enter the message you wish to sign (this will open your default editor):", - } - // const pathToFileQuestion = { - // type: 'input', - // name: 'pathToFile', - // message: 'Enter the path to the file you wish to sign:', - // } - const { messageAction } = await inquirer.prompt([messageQuestion]) - let msg: string - switch (messageAction) { - case 'Text Input': { - const { userInput } = await inquirer.prompt([userInputQuestion]) - msg = Buffer.from(userInput).toString('hex') - break - } - // case 'From a File': { - // break - // } - default: { - console.error('Unsupported Action') - return - } - } - debug('msg', msg); - const msgParam = { msg } - const signature = await entropy.signWithAdaptersInOrder({ - msg: msgParam, - order: ['deviceKeyProxy', 'noop'], - }) - - print('signature:', signature) + await signWithAdapter(entropy) return } case 'Exit to Main Menu': From b729559e3dd3dc0954a29f9f2e984ba21815fbd6 Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Mon, 3 Jun 2024 22:07:48 -0400 Subject: [PATCH 2/4] reset attempts counter on successful signature --- src/flows/sign/index.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/flows/sign/index.ts b/src/flows/sign/index.ts index aefcd4b..745ec37 100644 --- a/src/flows/sign/index.ts +++ b/src/flows/sign/index.ts @@ -6,7 +6,7 @@ import { debug, getSelectedAccount, print } from "../../common/utils" let msg: string let signingAttempts: number = 0 -async function signWithAdapter (entropy, signingData?: { msg: { msg: string }, order: string[] }) { +async function signWithAdaptersInOrder (entropy, signingData?: { msg: { msg: string }, order: string[] }) { try { const messageQuestion = { type: 'list', @@ -53,8 +53,10 @@ async function signWithAdapter (entropy, signingData?: { msg: { msg: string }, o order: ['deviceKeyProxy', 'noop'], } } - const signature = await entropy.signWithAdaptersInOrder(signingData) + const signature = await entropy.signWithAdaptersInOrder(signingData) const signatureHexString = u8aToHex(signature) + // Resetting signingAttempts on success + signingAttempts = 0 print('signature:', signatureHexString) print('verifying key:', entropy.signingManager.verifyingKey) } catch (error) { @@ -69,13 +71,10 @@ async function signWithAdapter (entropy, signingData?: { msg: { msg: string }, o order: ['noop', 'deviceKeyProxy'] } // Recursively retries signing with a reverse order in the subgroups list - await signWithAdapter(entropy, signingData) - } else { - console.error(message) + await signWithAdaptersInOrder(entropy, signingData) } - } else { - console.error(message) } + console.error(message) return } } @@ -129,7 +128,7 @@ export async function sign ({ accounts, endpoints, selectedAccount: selectedAcco // return // } case 'Sign With Adapter': { - await signWithAdapter(entropy) + await signWithAdaptersInOrder(entropy) return } case 'Exit to Main Menu': From fa291d2447c2c7f80adb4576bb338bf86bf0cbd7 Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Wed, 5 Jun 2024 14:27:28 -0400 Subject: [PATCH 3/4] adding do not delete to comments --- src/flows/sign/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/flows/sign/index.ts b/src/flows/sign/index.ts index 745ec37..4471842 100644 --- a/src/flows/sign/index.ts +++ b/src/flows/sign/index.ts @@ -14,6 +14,7 @@ async function signWithAdaptersInOrder (entropy, signingData?: { msg: { msg: str message: 'Please choose how you would like to input your message to sign:', choices: [ 'Text Input', + /* DO NOT DELETE THIS */ // 'From a File', ], } @@ -22,6 +23,7 @@ async function signWithAdaptersInOrder (entropy, signingData?: { msg: { msg: str name: "userInput", message: "Enter the message you wish to sign (this will open your default editor):", } + /* DO NOT DELETE THIS */ // const pathToFileQuestion = { // type: 'input', // name: 'pathToFile', @@ -36,6 +38,7 @@ async function signWithAdaptersInOrder (entropy, signingData?: { msg: { msg: str msg = Buffer.from(userInput).toString('hex') break } + /* DO NOT DELETE THIS */ // case 'From a File': { // break // } From 71ee8b9569a6918e08273b8e7cadd2a821d7e315 Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Wed, 5 Jun 2024 20:50:17 -0400 Subject: [PATCH 4/4] fixed the race condition as per mixs suggestions --- src/flows/sign/index.ts | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/flows/sign/index.ts b/src/flows/sign/index.ts index 4471842..409295a 100644 --- a/src/flows/sign/index.ts +++ b/src/flows/sign/index.ts @@ -3,10 +3,10 @@ import { u8aToHex } from '@polkadot/util' import { initializeEntropy } from "../../common/initializeEntropy" import { debug, getSelectedAccount, print } from "../../common/utils" -let msg: string -let signingAttempts: number = 0 +let userInput: string -async function signWithAdaptersInOrder (entropy, signingData?: { msg: { msg: string }, order: string[] }) { +async function signWithAdaptersInOrder (entropy, signingData: { msg?: { msg: string }, order?: string[] } = {}, signingAttempts = 0) { + let msg = signingData?.msg?.msg try { const messageQuestion = { type: 'list', @@ -34,7 +34,7 @@ async function signWithAdaptersInOrder (entropy, signingData?: { msg: { msg: str ({ messageAction } = await inquirer.prompt([messageQuestion])) switch (messageAction) { case 'Text Input': { - const { userInput } = await inquirer.prompt([userInputQuestion]) + ({ userInput } = await inquirer.prompt([userInputQuestion])) msg = Buffer.from(userInput).toString('hex') break } @@ -48,34 +48,30 @@ async function signWithAdaptersInOrder (entropy, signingData?: { msg: { msg: str } } } - debug('msg', msg); + const msgParam = { msg } - if (!signingData) { + if (!signingData?.msg?.msg) { signingData = { msg: msgParam, order: ['deviceKeyProxy', 'noop'], } } + print('msg to be signed:', `"${userInput}"`) const signature = await entropy.signWithAdaptersInOrder(signingData) const signatureHexString = u8aToHex(signature) - // Resetting signingAttempts on success - signingAttempts = 0 print('signature:', signatureHexString) print('verifying key:', entropy.signingManager.verifyingKey) } catch (error) { - signingAttempts++ const { message } = error // See https://github.com/entropyxyz/sdk/issues/367 for reasoning behind adding this retry mechanism - if (message.includes('Invalid Signer') || message.includes('Invalid Signer in Signing group')) { - if (signingAttempts <= 1) { - const msgParam = { msg } - signingData = { - msg: msgParam, - order: ['noop', 'deviceKeyProxy'] - } - // Recursively retries signing with a reverse order in the subgroups list - await signWithAdaptersInOrder(entropy, signingData) + if ((message.includes('Invalid Signer') || message.includes('Invalid Signer in Signing group')) && signingAttempts <= 1) { + const msgParam = { msg } + signingData = { + msg: msgParam, + order: ['noop', 'deviceKeyProxy'] } + // Recursively retries signing with a reverse order in the subgroups list + await signWithAdaptersInOrder(entropy, signingData, signingAttempts + 1) } console.error(message) return